How to configure NFS over TLS

NFS over TLS protects file traffic at the RPC transport layer while keeping normal NFS export and mount semantics. It is useful when an export still uses sec=sys for Unix user identity but the network path should not carry file data in clear text.

Linux implements this feature as RPC-with-TLS. The kernel asks tlshd to perform the TLS handshake, tlshd uses X.509 trust material, and the export's xprtsec= option decides whether clients may connect without TLS, with server-authenticated TLS, or with mutual TLS.

A production rollout should treat certificate names and trust stores as part of the mount contract. Clients must mount with a DNS name covered by the server certificate, and the export should deny non-TLS clients before the share is exposed beyond a trusted test host.

Steps to require NFS over TLS for an export:

  1. Install the NFS and TLS handshake packages on the server.
    $ sudo dnf install --assumeyes nfs-utils ktls-utils
    Dependencies resolved.
    ##### snipped #####
    Installed:
      ktls-utils-0.11-3.el9.aarch64
      nfs-utils-2.5.4-43.el9.aarch64
    Complete!

    ktls-utils provides tlshd. Red Hat documents native NFS TLS support from RHEL 9.6; older kernels or NFS user-space packages may not support xprtsec=.

  2. Create a server private key and certificate signing request.
    $ sudo openssl req -new -newkey rsa:4096 -noenc \
      -keyout /etc/pki/tls/private/files.example.net.key \
      -out /etc/pki/tls/private/files.example.net.csr \
      -subj "/C=US/ST=State/L=City/O=Example Organization/CN=files.example.net" \
      -addext "subjectAltName=DNS:files.example.net,IP:192.0.2.40"

    The certificate's Subject Alternative Name must cover the hostname or IP address that clients use in the mount source. Keep the private key readable only by root.

  3. Request a server certificate from the CA trusted by the clients.

    The CA should return a server certificate such as files.example.net.crt and the CA certificate such as ca.crt. Use an internal CA, enterprise PKI, or another trusted issuer that matches the client trust policy.

  4. Import the CA certificate into the server trust store.
    $ sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
  5. Rebuild the server trust store.
    $ sudo update-ca-trust

    Leaving x509.truststore unset in /etc/tlshd.conf makes tlshd use the system trust store.

  6. Install the signed server certificate.
    $ sudo mv files.example.net.crt /etc/pki/tls/certs/
  7. Restore SELinux labels on the certificate and key paths.
    $ sudo restorecon -Rv /etc/pki/tls/certs/ /etc/pki/tls/private/
    Relabeled /etc/pki/tls/certs/files.example.net.crt
    Relabeled /etc/pki/tls/private/files.example.net.key
  8. Add the server certificate and private key to the tlshd server authentication section.
    $ sudoedit /etc/tlshd.conf
    [authenticate.server]
    x509.certificate= /etc/pki/tls/certs/files.example.net.crt
    x509.private_key= /etc/pki/tls/private/files.example.net.key

    Edit the active tlshd configuration file installed by the local package. Red Hat-family packages use /etc/tlshd.conf.

  9. Enable and start the tlshd service on the server.
    $ sudo systemctl enable --now tlshd.service

    tlshd reads its configuration when it starts. Restart tlshd.service after later certificate or /etc/tlshd.conf changes.

  10. Open the export definition file on the server.
    $ sudoedit /etc/exports.d/projects.exports
  11. Require TLS transport security on the export.
    /srv/nfs/projects 192.0.2.0/24(rw,sync,no_subtree_check,root_squash,sec=sys,xprtsec=tls)

    xprtsec=tls requires a TLS-protected transport without requiring a client certificate. Use xprtsec=mtls only after configuring client certificates under the tlshd client authentication section.

  12. Reload the saved export definitions.
    $ sudo exportfs -ra

    No output usually means exportfs accepted the saved definitions. Fix any reported path, selector, or option error before testing from a client.

  13. Verify that the active export requires TLS.
    $ sudo exportfs -v
    /srv/nfs/projects
    		192.0.2.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,root_squash,no_all_squash,xprtsec=tls)

    Look for xprtsec=tls in the selector's option set. Defaults such as wdelay, hide, and no_all_squash can appear even when they were not written in the source file.

  14. Install the NFS and TLS handshake packages on the client.
    $ sudo dnf install --assumeyes nfs-utils ktls-utils
    Dependencies resolved.
    ##### snipped #####
    Complete!
  15. Import the CA certificate into the client trust store.
    $ sudo cp ca.crt /etc/pki/ca-trust/source/anchors/
  16. Rebuild the client trust store.
    $ sudo update-ca-trust
  17. Enable and start the tlshd service on the client.
    $ sudo systemctl enable --now tlshd.service
  18. Create the client mount point.
    $ sudo mkdir --parents /mnt/projects

    Files already inside /mnt/projects become hidden while the NFS filesystem is mounted there.

  19. Mount the export with TLS transport security.
    $ sudo mount -t nfs -o xprtsec=tls files.example.net:/srv/nfs/projects /mnt/projects

    Use the server hostname covered by the certificate. A raw IP address fails hostname validation unless the certificate also includes that IP address as a subject alternative name.

  20. Verify the mounted NFS source and options.
    $ findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS /mnt/projects
    TARGET        SOURCE                                FSTYPE OPTIONS
    /mnt/projects files.example.net:/srv/nfs/projects  nfs4   rw,relatime,vers=4.2,sec=sys,xprtsec=tls
    ##### snipped #####

    Some clients show transport security in tlshd logs instead of every mount-option view. Treat the successful handshake log as the transport proof when the mount table omits xprtsec=tls.

  21. Check the client tlshd log for the successful handshake.
    $ sudo journalctl -u tlshd.service --since -5m --no-pager
    Jul 05 14:20:31 client.example.net tlshd[2184]: Handshake with files.example.net (192.0.2.40) was successful
  22. Create a temporary mount point for the non-TLS denial test.
    $ sudo mkdir --parents /mnt/projects-no-tls-test
  23. Verify that an unencrypted mount is denied.
    $ sudo mount -t nfs -o xprtsec=none files.example.net:/srv/nfs/projects /mnt/projects-no-tls-test
    mount.nfs: access denied by server while mounting files.example.net:/srv/nfs/projects

    If this mount succeeds, the server export is not enforcing xprtsec=tls for the client selector that matched the request. Recheck /etc/exports.d/projects.exports and the active exportfs -v table before exposing the share.

  24. Remove the temporary denial-test mount point.
    $ sudo rmdir /mnt/projects-no-tls-test