I needed to create a network share on my network. Being in a GNU/Linux environement, my natural choice was NFS.
Unfortunately, by default, this protocol provide no security whatsoever. The only thing the protocol offers is per-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. Having never worked on the latter, I chose 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 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 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: You should now have access to the nfs share from the root user, but get the 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 w'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 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$$
We now have access to the nfs share from the user.
[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
Here I'll show 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 :