I needed to create a share on my network. Being in a GNU/Linux environment, my natural choice was NFS.
Unfortunately, by default, this protocol offers no security. The only thing the protocol offers is IP filtering, which remains rather weak, as there is no authentication and no encryption.
To overcome these two weaknesses, we have a choice between setting up a vpn or using the kerberos protocol. As I've never worked with the latter, I've chosen kerberos.
allow-hotplug ens192
iface ens192 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.254
dns-nameservers 192.168.1.254
root@debian:~# apt update && apt install nfs-kernel-server nfs-common krb5-user libpam-krb5 krb5-admin-server krb5-kdc
Note: you can answer STD.LOCAL and nfs.std.local to the questions asked. In fact, it doesn't matter as we'll define these parameters in the configuration files later.
root@debian:~# groupadd nfs
root@debian:~# usermod -a -G nfs nobody
root@debian:~# mkdir /nfs
root@debian:~# chmod 770 /nfs && chgrp nfs /nfs
192.168.1.100 nfs.std.local nfs
192.168.1.10 arch.std.local arch
192.168.1.11 debian.std.local debian
/nfs *(rw,sync,anongid=1001,root_squash,no_subtree_check,sec=krb5p)
/nfs 192.168.1.10(rw,sync,anongid=1001,root_squash,no_subtree_check,sec=krb5p) 192.168.1.11(rw,sync,anongid=1001,root_squash,no_subtree_check,sec=krb5p)
NEED_SVCGSSD=yes
root@debian:~# systemctl restart nfs-kernel-server.service && exportfs -arv
[libdefaults]
default_realm = STD.LOCAL
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
fcc-mit-ticketflags = true
allow_weak_crypto = false
[realms]
STD.LOCAL = {
kdc = nfs.std.local
admin_server = nfs.std.local
default_domain = std.local
}
[domain_realm]
.std.local = STD.LOCAL
std.local = STD.LOCAL
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
root@debian:~# kdb5_util -r STD.LOCAL create -s
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key: MasterOfTheDomain:p
Re-enter KDC database master key to verify: MasterOfTheDomain:p
#*/admin@STD.LOCAL *
kdcadmin/admin@STD.LOCAL *
root@debian:~# systemctl start krb5-kdc.service
root@debian:~# systemctl start krb5-admin-server.service
root@debian:~# kadmin.local
Authenticating as principal root/admin@STD.LOCAL with password.
kadmin.local: add_principal kdcadmin/admin@STD.LOCAL
No policy specified for kdcadmin/admin@STD.LOCAL; defaulting to no policy
Enter password for principal "kdcadmin/admin@STD.LOCAL":$superKDC$2000
Re-enter password for principal "kdcadmin/admin@STD.LOCAL":$superKDC$2000
Principal "kdcadmin/admin@STD.LOCAL" created.
kadmin: exit
root@debian:~# kadmin.local
Authenticating as principal root/admin@STD.LOCAL with password.
kadmin.local: add_principal nfsuser@STD.LOCAL
No policy specified for nfsuser@STD.LOCAL; defaulting to no policy
Enter password for principal "nfsuser@STD.LOCAL":NFScher$$
Re-enter password for principal "nfsuser@STD.LOCAL":NFScher$$
Principal "nfsuser@STD.LOCAL" created.
kadmin: exit
root@debian:~# kadmin.local
kadmin.local: addprinc -randkey nfs/nfs.std.local@STD.LOCAL
No policy specified for nfs/nfs.std.local@STD.LOCAL; defaulting to no policy
Principal "nfs/nfs.std.local@STD.LOCAL" created.
kadmin.local: addprinc -randkey nfs/arch.std.local@STD.LOCAL
No policy specified for nfs/arch.std.local@STD.LOCAL; defaulting to no policy
Principal "nfs/arch.std.local@STD.LOCAL" created.
kadmin.local: addprinc -randkey nfs/debian.std.local@STD.LOCAL
No policy specified for nfs/debian.std.local@STD.LOCAL; defaulting to no policy
Principal "nfs/debian.std.local@STD.LOCAL" created.
kadmin: exit
root@debian:~# kadmin.local
kadmin.local: ktadd nfs/nfs.std.local
kadmin.local: ktadd nfs/arch.std.local
kadmin.local: ktadd nfs/debian.std.local
kadmin.local: exit
root@debian:~# reboot
[root@arch ~]# pacman -S nfs-utils
[root@arch ~]# systemctl enable nfs-client.target
[root@arch ~]# systemctl start nfs-client.target
192.168.1.100 nfs.std.local nfs
192.168.1.10 arch.std.local arch
192.168.1.11 debian.std.local debian
[libdefaults]
default_realm = STD.LOCAL
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
fcc-mit-ticketflags = true
allow_weak_crypto = false
[realms]
STD.LOCAL = {
kdc = nfs.std.local
admin_server = nfs.std.local
default_domain = std.local
}
[domain_realm]
.std.local = STD.LOCAL
std.local = STD.LOCAL
We can now copy the /etc/krb5.keytab file from the server or generate it from client via Kerberos.
[root@arch ~]# kadmin -p kdcadmin/admin@STD.LOCAL
Authenticating as principal kdcadmin/admin@STD.LOCAL with password.
Password for kdcadmin/admin@STD.LOCAL: $superKDC$2000
kadmin: ktadd nfs/arch.std.local
Entry for principal nfs/arch.std.local with kvno 3, encryption type aes256-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal nfs/arch.std.local with kvno 3, encryption type aes128-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
kadmin: exit
Note: I got the following error: mount.nfs: Operation not permitted until the machine was completely rebooted.
[root@arch ~]# reboot
[root@arch ~]# mkdir /mnt/nfs
[root@arch ~]# mount -t nfs -o sec=krb5p nfs.std.local:/nfs /mnt/nfs/
Note: You should now have access to the nfs share from the root user, but you get the following error: cannot access '/mnt/nfs/': Permission denied from a user account. You need a kerberos ticket for this to work.
[user@arch ~]$ kinit nfsuser@STD.LOCAL
Password for nfsuser@STD.LOCAL: NFScher$$
You now have access to the nfs share with a simple user account.
[user@arch ~]$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: nfsuser@STD.LOCAL
Valid starting Expires Service principal
18/09/2022 20:14:38 19/09/2022 06:14:38 krbtgt/STD.LOCAL@STD.LOCAL
renew until 19/09/2022 15:14:33
nfs.std.local:/nfs /mnt/nfs nfs defaults,timeo=900,retrans=5,_netdev,sec=krb5p,user,noauto 0 0
root@debian:~# apt update && apt install nfs-common krb5-user
Note: you can answer STD.LOCAL and nfs.std.local to the questions asked. In fact, it doesn't matter as we we'll define these parameters in the configuration files later.
root@debian:~# systemctl enable nfs-client.target
root@debian:~# systemctl start nfs-client.target
192.168.1.100 nfs.std.local nfs
192.168.1.10 arch.std.local arch
192.168.1.11 debian.std.local debian
[libdefaults]
default_realm = STD.LOCAL
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
fcc-mit-ticketflags = true
allow_weak_crypto = false
[realms]
STD.LOCAL = {
kdc = nfs.std.local
admin_server = nfs.std.local
default_domain = std.local
}
[domain_realm]
.std.local = STD.LOCAL
std.local = STD.LOCAL
We can copy the /etc/krb5.keytab file from the server or generate it from the client via kerberos.
root@debian:~# kadmin -p kdcadmin/admin@STD.LOCAL
Authenticating as principal kdcadmin/admin@STD.LOCAL with password.
Password for kdcadmin/admin@STD.LOCAL: $superKDC$2000
kadmin: ktadd nfs/debian.std.local
Entry for principal nfs/arch.std.local with kvno 3, encryption type aes256-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal nfs/arch.std.local with kvno 3, encryption type aes128-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
kadmin: exit
Note: I got the following error: mount.nfs: an incorrect mount option was specified until the machine was completly rebooted.
[root@arch ~]# reboot
[root@arch ~]# mkdir /mnt/nfs
[root@arch ~]# mount -t nfs -o sec=krb5p nfs.std.local:/nfs /mnt/nfs/
Note: ou should now have access to the nfs share from the root user, but get the error: cannot open directory '/mnt/nfs/': Stale file handle from a user account. You need a Kerberos ticket for this to work.
[user@arch ~]$ kinit nfsuser@STD.LOCAL
Password for nfsuser@STD.LOCAL: NFScher$$
The user now has access to the nfs share.
[user@arch ~]$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: nfsuser@STD.LOCAL
Valid starting Expires Service principal
09/21/2022 18:12:01 09/22/2022 04:12:01 krbtgt/STD.LOCAL@STD.LOCAL
renew until 09/22/2022 18:11:39
09/21/2022 18:12:03 09/22/2022 04:12:01 nfs/nfs.std.local@STD.LOCAL
renew until 09/22/2022 18:11:39
nfs.std.local:/nfs /mnt/nfs nfs defaults,timeo=900,retrans=5,_netdev,sec=krb5p,user,noauto 0 0
root@debian:~# ktadd -k krb5.keytab nfs/archtoi.std.local
root@debian:~# klist
root@debian:~# kdestroy
root@debian:~# kinit -R
root@debian:~# kadmin.local
Authenticating as principal root/admin@STD.LOCAL with password.
kadmin.local: listprincs
K/M@STD.LOCAL
kadmin/admin@STD.LOCAL
kadmin/changepw@STD.LOCAL
kadmin/nfskerb@STD.LOCAL
kdcadmin/admin@STD.LOCAL
kiprop/nfskerb@STD.LOCAL
krbtgt/STD.LOCAL@STD.LOCAL
nfs/arch.std.local@STD.LOCAL
nfs/debian.std.local@STD.LOCAL
nfs/nfs.std.local@STD.LOCAL
nfsuser@STD.LOCAL
root@debian:~# kadmin.local
Authenticating as principal root/admin@STD.LOCAL with password.
kadmin.local: delete_principal nfs/arch.std.local@STD.LOCA
root@debian:~# klist -e -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
4 nfs/debian.std.local@STD.LOCAL (aes256-cts-hmac-sha1-96)
4 nfs/debian.std.local@STD.LOCAL (aes128-cts-hmac-sha1-96)
root@debian:~# ktutil
ktutil: rkt /etc/krb5.keytab
ktutil: list
slot KVNO Principal
---- ---- ---------------------------------------------------------------------
1 2 nfs/nfs.std.local@STD.LOCAL
2 2 nfs/nfs.std.local@STD.LOCAL
3 2 nfs/arch.std.local@STD.LOCAL
4 2 nfs/arch.std.local@STD.LOCAL
5 2 nfs/debian.std.local@STD.LOCAL
6 2 nfs/debian.std.local@STD.LOCAL
root@debian:~# ktutil
ktutil: delete_entry nfs/arch.std.local@STD.LOCAL
I'll show here the nftables rules I use to protect the server.
Service | TCP | UDP |
---|---|---|
Kerberos | 88, 749 | 88, 749 |
NFSv4 | 2049 |
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.all.autoconf=0
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.default.autoconf=0
root@debian:~# sysctl -p /etc/sysctl.conf
root@debian:~# systemctl enable nftables.service
#!/usr/sbin/nft -f
flush ruleset
# ----- IPv4 -----
table ip filter {
chain INPUT {
type filter hook input priority 0; policy drop; #by default, we drop traffic
iif lo accept comment "Accept any localhost traffic"
ct state invalid counter drop comment "Drop invalid connections"
ct state { established, related } counter accept comment "Accept traffic originated from us"
iif != lo ip daddr 127.0.0.1/8 counter drop comment "drop connections to loopback not coming from loopback"
tcp flags & (fin | syn | rst | psh | ack | urg) > urg counter drop comment "Drop TCP Null"
tcp flags & (fin | syn | rst | psh | ack | urg) < fin counter drop comment "Drop TCP XMS"
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem, echo-request } accept comment "Accept ICMP"
iif ens192 ip saddr { 192.168.1.10, 192.168.1.11 } tcp dport { ssh, 88, 749, 2049 } counter accept comment "Accept ssh, kerberos tcp and nfsv4"
iif ens192 ip saddr { 192.168.1.10, 192.168.1.11 } udp dport { 88, 749 } counter accept comment "Accept kerberos udp"
iif ens192 tcp sport { http, https, 53, ntp } counter accept comment "Accept http, https, dns and ntp TCP"
iif ens192 udp sport { 53, ntp } counter accept comment "Accept dns and ntp UDP"
log counter drop #log (/var/log/kern.log), count and drop any other rules
}
chain FORWARD {
type filter hook forward priority 0; policy drop;
counter comment "count dropped packets"
}
chain OUTPUT {
type filter hook output priority 0; policy drop;
oif ens192 ip daddr { 192.168.1.10, 192.168.1.11 } tcp sport { ssh, 88, 749, 2049 } counter accept comment "Accept ssh, kerberos tcp and nfsv4"
oif ens192 ip daddr { 192.168.1.10, 192.168.1.11 } udp sport { 88, 749 } counter accept comment "Accept kerberos udp"
tcp dport { http, https, 53, ntp } counter accept comment "Accept http, https, dns and ntp TCP"
udp dport { 53, ntp } counter accept comment "Accept dns and ntp UDP"
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem, echo-request, echo-reply } accept comment "Accept ICMP"
log counter drop #log (/var/log/kern.log), count and drop any other rules
}
}
root@debian:~# systemctl restart nftables.service
Contact :