Share NFS Home Directories Securely with Kerberos

You can share NFS home directories without enabling Kerberos for more secure authentication. But with the standard system authentication, it’s trivial for a remote user to change the UID of a local account on their PC and gain access to someone else’s home directory. Kerberos adds a requirement that the end user have a special security token to access the home directory. You can only acquire that security token from the designated key server by providing the correct password.

This guide shows you how to integrate a Fedora server with Active Directory so you can share user home directories over NFS more securely. This guide assumes you already have an Active Directory domain.

Install and configure NTP

The Kerberos protocol requires all the computers participating in cryptographic communication to have clocks synchronized to within five minutes.

First, synchronize the NFS server’s clock with the ntpdate command and then commit the change to the hardware clock with the hwclock command:

$ sudo -i
# MY_HOSTNAME=$(</etc/hostname)
# MY_DOMAIN=${MY_HOSTNAME#*.}
# dnf install -y ntpdate
# ntpdate $MY_DOMAIN
# hwclock -u -w

The # prompt shows commands that need to be run as root. The $ prompt shows commands that can be run as an unprivileged user. The sudo -i command allows you to become root to issue necessary commands.

This guide is meant to be copy-and-paste friendly. Any value you might need to customize appears as a MY_* variable you can tweak before running the remaining commands. Note that if you log out, these variable assignments are cleared.

The above commands assume the domain name part of your server’s hostname matches the domain name of your Active Directory. Unless you set special configuration options in Active Directory, you’ll probably need to set your hostname so the domain part matches your Active Directory domain name.

Now, install the ntp package:

# dnf install -y ntp

Next, configure the NTP service:

# MY_NETWORK=192.0.2.0
# MY_NETMASK=255.255.255.0
# MY_ADSERVER1=192.0.2.91
# MY_ADSERVER2=192.0.2.92
# cat << END > /etc/ntp.conf
tinker panic 0
interface ignore ipv6

driftfile /var/lib/ntp/drift
includefile /etc/ntp/crypto/pw
keys /etc/ntp/keys

restrict default ignore
restrict $MY_NETWORK mask $MY_NETMASK
restrict 127.0.0.1

server $MY_ADSERVER1
server $MY_ADSERVER2
END

If you need to quickly look up the IP addresses of your Active Directory servers, run this command:

# nslookup $MY_DOMAIN

Finally, add an exception to the firewall and start the service:

# firewall-cmd --add-service ntp
# firewall-cmd --runtime-to-permanent
# systemctl enable ntpd.service
# systemctl start ntpd.service

To verify that NTP is working, run this command:

ntpq -4 -p

Install and configure Kerberos

To enable Kerberos authentication on our server, install the krb5-workstation package:

# dnf install -y krb5-workstation

Then configure your default realm:

# MY_REALM=${MY_DOMAIN^^}
# cat << END > /etc/krb5.conf.d/${MY_DOMAIN%%.*}
[libdefaults]
  default_realm = $MY_REALM
  dns_lookup_kdc = true

[domain_realm]
  .$MY_DOMAIN = $MY_REALM
END

The default realm is your Active Directory domain name in all upper-case letters.

Install and configure SSSD

The next thing you need for KRB5 authenticated home directories is user IDs. You could create them manually on the NFS server. But if you have more than a few users, you’ll want to get the list of usernames and their associated UIDs from Active Directory. Use sssd to fetch the user IDs from Active Directory.

Begin by installing the sssd package:

# dnf install -y sssd

Now configure SSSD to use Active Directory as an ID provider:

# cat << END > /etc/sssd/sssd.conf
[sssd]
  services = nss
  config_file_version = 2
  domains = $MY_DOMAIN

[domain/$MY_DOMAIN]
  id_provider = ad
  ldap_idmap_range_min = 0
  ldap_idmap_range_max = 2100000000
  ldap_idmap_range_size = 100000000
  ldap_idmap_default_domain_sid = S-1-5-21-0-0-0
  krb5_store_password_if_offline = true
  cache_credentials = true
  ignore_group_members = true
  override_gid = 100
  override_shell = /bin/bash
  override_homedir = /home/%u
END
# chmod 600 /etc/sssd/sssd.conf

The ldap_idmap* values are important to ensure the UIDs Active Directory reports are consistent between the NFS server and all of its clients. Here’s a reference on how SID to uid/gid mapping works in sssd.

Even though you didn’t configure SSSD for authentication by including pam in the services list, end users may still be able to log in to the netboot server over SSH using PubkeyAuthentication or GSSAPIAuthentication methods. You may want to set an explicit limit for who can log in to your netboot server over SSH. For example:

# echo DenyGroups users >> /etc/ssh/sshd_config && systemctl restart sshd.service

Join Active Directory

Next, join the server to the Active Directory domain. Before performing the join, delete any computer accounts by the same name in the domain. This helps ensure you don’t carry over any incorrect settings from a previous join attempt:

# MY_USERNAME=jsmith
# adcli delete-computer "${MY_HOSTNAME%%.*}" -U "$MY_USERNAME"

Also, delete any previous version of the system keytab, to avoid carrying over any incorrect settings from a previous join attempt:

# rm -f /etc/krb5.keytab

Now you should be able to join the Active Directory domain:

# MY_OU="cn=computers,dc=${MY_DOMAIN//./,dc=}"
# adcli join $MY_DOMAIN --login-user="$MY_USERNAME" --computer-name="${MY_HOSTNAME%%.*}" --host-fqdn="$MY_HOSTNAME" --user-principal="host/$MY_HOSTNAME@$MY_REALM" --service-name="host" --service-name="nfs" --domain-ou="$MY_OU"

By default, Active Directory only allows normal users to join up to 10 computers to its domain (KB243327).

If adcli warns you about DNS not updating, your primary DNS servers may not be forwarding queries properly to the Active Directory domain controllers. Set your network configuration to reference the Active Directory servers directly for DNS.

The –service-name=”nfs” flag in the above command is important. The NFS service cannot serve Kerberized home directories without the nfs “serviceprincipalname”.

If the join succeeded, you should be able to start the SSSD service:

# systemctl start sssd.service

Configure PAM

Once sssd is running, configure the NFS server to resolve UIDs using it:

# cp -r /usr/share/authselect/default/sssd /etc/authselect/custom
# echo 'initgroups: files' >> /etc/authselect/custom/sssd/nsswitch.conf
# authselect select custom/sssd --force

Set initgroups to files as a performance optimization to prevent group information from being fetched from Active Directory. You can omit that line. If you do, though, you may see delays when you list files or perform other actions that try to look up UID and GID information.

At this point, you should be able to look up a user’s UID:

$ id $MY_USERNAME

You may find it necessary to run systemctl restart sssd.service before the above command works.

Create the home directories

Now that the ID provider is working, create the home directories by cloning the /etc/skel directory and setting permissions:

# cp -a /etc/skel /home/$MY_USERNAME
# chown -R $MY_USERNAME:users /home/$MY_USERNAME
# chmod -R go-rwx /home/$MY_USERNAME

Configure NFS ID mapping

Before you can export the home directories, you must configure NFS’s idmap service:

# cat << END > /etc/idmapd.conf
[General]
  Domain = $MY_DOMAIN
  Local-Realms = $MY_REALM

[Mapping]
  Nobody-User = nfsnobody
  Nobody-Group = nfsnobody

[Translation]
  Method = static,nsswitch
  GSS-Methods = static,nsswitch

[Static]
END

You must also define the special nfsnobody user for cases where a UID might not resolve to a username:

# echo 'nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin' >> /etc/passwd
# echo 'nfsnobody:!!:::::::' >> /etc/shadow
# echo 'nfsnobody:x:65534:' >> /etc/group
# echo 'nfsnobody:!::' >> /etc/gshadow

Enable Kerberos and share the home directories

Enable KRB5 authentication on the NFS pseudo filesystem:

# MY_SUBNET=192.0.2.0
# MY_PREFIX=24
# echo "/export -fsid=0,ro,sec=sys:krb5,root_squash $MY_SUBNET/$MY_PREFIX" > /etc/exports

Now create and mount the home filesystem:

# mkdir /export/home
# echo '/home /export/home none bind 0 0' >> /etc/fstab
# mount /export/home

Last, we define the home export and restart the NFS server to ensure all configuration changes are registered:

# echo "/export/home -rw,sec=krb5,root_squash $MY_SUBNET/$MY_PREFIX" > /etc/exports.d/home.exports
# systemctl restart nfs-server.service

Make sure everything looks right on the export. In particular, make sure the krb5 flag is set on both the root export and the home sub-filesystem:

# exportfs -v

The output from the above command should include at least the following two lines (emphasis added):

/export         192.0.2.0/24(sync,wdelay,hide,no_subtree_check,fsid=0,sec=sys:krb5,ro,secure,root_squash,no_all_squash)
/export/home    192.0.2.0/24(sync,wdelay,hide,no_subtree_check,sec=krb5,rw,secure,root_squash,no_all_squash)

The Kerberos protocol can also provide encryption (krb5p) or integrity (krb5i) for the NFS export, but these variants of the krb5 option will cause a significant reduction in performance. You probably don’t want to use them unless you really need them.


Photo by Pietro Jeng on Unsplash.

For System Administrators

14 Comments

  1. Andrej

    Thanks Gregory for a nice tutorial about kerberized NFS. I tried something similar a while ago, and I managed to set-up what you described (although with OpenLDAP, not AD), but I had difficulty understanding how 3rd party services can automatically obtain and keep kerberos tickets which would provide them a persistent access (rw) to the kerberized NFS storage. For example, if I have a backup script that copies files to the NFS storage and it is run by cron… Could you give references for that scenario? Thanks.

    • Sure, you should be able to use the “ktutil” command to store a kerberos account password:

      $ ktutil
      ktutil: add_entry -password -p admin@EXAMPLE.EDU -k 1 -e RC4-HMAC
      ktutil: write_kt admin.keytab
      ktutil: quit

      You should then be able to use the “kinit” command in your script to initialize a kerberos ticket using the admin.keytab file:

      kinit -kt admin.keytab admin@EXAMPLE.EDU

      Or, in the case of a continuously running service, you may want to create a cron job for the service with a line like the following which renews the kerberos ticket every 6 hours:

      0 */6 * * * /usr/bin/kinit -kt $HOME/admin.keytab -l 6:00

      In the case of a backup script, you might also have to map the kerberos account principal to the root user by adding a line like the following to the “[Static]” section of the /etc/idmapd.conf file on the NFS server:

      admin@EXAMPLE.EDU = root

      And set the “no_root_squash” flag on “/export/home” in /etc/exports.d/home.exports (maybe /export as well).

      • Andrej

        Thanks for the reply! OK, I’ll try it – makes sense, since I’m already familiar with the keytab file. What about systemd services which need to access kerberized NFS? Isn’t there some more “systematic” approach besides granting tickets by cron to them?

        • Honestly, you are asking about things that I haven’t done since before systemd existed, so I don’t really know for sure. My guess is that you could wrap your systemd process in a bash script that would keep the ticket renewed. Maybe something like the following:

          #!/usr/bin/bash

          kinit -kt /path/to/service.keytab -l 6:00

          service_exec &

          while true; do
          sleep 21600
          kinit -kt /path/to/service.keytab -l 6:00
          done

          I’m not sure if that would work though. It might depend on the type of the systemd service and any number of other things.

          • Hi Andrej: I was just skimming through my comments and I realized that my last response to you may have seemed a bit negative. I didn’t mean to come across that way at all.

            Also, I thought I should point out that there is another way for services to get access to the user home directories — a second share of the “/home” filesystem can be made that does not require kerberos authentication. For example, you could add a line like the following to /etc/exports.d/home.exports:

            /export/home -rw,sec=sys,root_squash 192.168.0.0/24

            You would then have a second NIC in your server assigned an address on that private network that would connect directly to a separate server on which you could run services that you wanted to have access to the user home directories without needing kerberos (e.g. apache).

            That may work for some things like apache and backups but not for others like services that the end users may need to run on their client systems.

            Hope that helps,
            Greg

            • Andrej

              Hello Gregory. An interesting point, although I have to use a protected/private network in that case. Nevertheless, by suggesting those solutions, you made me think if my ideas about sharing data across a network are bad by design.

              P.S. No worries, I didn’t negatively receive your previous comment.

  2. Kenyon Ralph

    Why would you disable IPv6 for ntpd, with no explanation, here in 2018? Stop spreading bad configurations.

    • The reasons to use IPv4 for NTP are:

      1) Compatibility – I doubt there are many (if any) IPv6-only Active Directory domains. I’m sure that there are, however, many networks still using IPv4-only routers.

      2) Stability – The IPv6 support that was added to NTP is an “imperfect” hack. See: https://tools.ietf.org/html/draft-boudreault-ipv6-ntp-refid-00

      3) Manageability – Among the thing that NTP’s IPv6 reworking didn’t include was an IPv6-compatible “mask” parameter. The mask parameter only supports the IPv4 “dotted decimal” notation (255.255..). Without a mask parameter, one is limited to either listing their Active Directory domain controllers individually one both the “restrict” lines AND the “server” lines (which creates a manageability “gotcha” for the user later on when Active Directory domain controllers are replaced because they are likely to update the server name in one place and not the other) or opening NTPD to the world (as the default configuration does albeit with some protocol restrictions).

      4) Security – NTP is still one of the biggest reflectors used for DDoS attacks (see: https://blogs.akamai.com/2018/06/summer-soti—ddos-by-the-numbers.html). If you don’t have to open your NTPD to the world, why would you want to?

      So, the configuration that I provided is more likely to “just work” (point 1), “work well” (point 2), “be manageable” (point 3), and “be more secure” (point 4).

      If someone wants to just change the “server” lines in the default configuration, I expect that they can figure out how to do that themselves.

  3. Vincent Brobald

    Of course, it is interesting to talk about the integration with the Windows world, but it is a bit disappointing to see the article aiming exclusively at Active Directory considering the title. Let’s not forget that protocols like Kerberos and LDAP have very robust FOSS implementation.

    AD is not a prerequisite for secure NFS, you can create your own KDC using packages or use enterprise-grade solutions like (Free)IPA to ensure a coherent management of the platform security.

    • Actually, I completely agree.

      The real reason that this article uses Active Directory is because it is meant to be used in conjunction with the NetBoot series and those articles are targeted at people who’s clients are probably not running FOSS. This sort of kerberized NFS server can be setup independently of the NetBoot server though, so it was decided to make this a stand-alone article.

  4. Paul

    Thanks for the article. These two lines can be condensed into one:

    systemctl enable ntpd.service

    systemctl start ntpd.service

    Becomes:

    systemctl enable –now ntpd.service

    • Thanks Paul, I didn’t know about that short cut. I expect that I’ll make good use of it in my day-to-day job. I might stick with the long form for the guides though. Sometimes it is better to show people the long way of doing things so that they get a better understanding of what is going on before you show the trick that makes it all happen with the push of a button. 😉

      • Paul

        You’re welcome, I only learnt about it recently in another fedoramagazine article! I agree, depending on the audience, a series of steps may be more informative than dense commands with too much magic.

  5. Phozzy

    You may automate most of these tasks by using FreeIPA

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions

%d bloggers like this: