-
Notifications
You must be signed in to change notification settings - Fork 3
4_6 RPC with TLS
A new standard called RPC-with-TLS that allows NFS traffic to be tunneled through (D)TLS has been defined in RFC 9289.
The goal is to provide cryptographic protection to the data in transit without requiring a complex Kerberos system.
Experimental implementations for Linux and FreeBSD exist.
There are two modes: TLS where only the server is authenticated and MTLS with mutual authentication between server and client.
To establish a connection, an opportunistic encryption mechanism similar to STARTTLS
for SMTP
is used to upgrade a regular NFS connection.
STRIPTLS
attacks can be prevented by configuring clients to only allow TLS connections or by evaluating the server's TLSA
DNS record.
While the purpose of RPC-with-TLS is similar to that of RPCSEC_GSS, it is not an authentication flavour because it is implemented on a layer below NFS.
For this reason, NFS requests sent over TLS typically use AUTH_SYS
as their authentication flavour.
This means that clients can still use any uid
to access files.
When MTLS is used, the FreeBSD implementation is able to prevent UID spoofing by reading the Subject Alternative Name from the client certificate and mapping it to a local user, ignoring the uid
specified in the AUTH_SYS
structure of the NFS request.
In the future, MTLS could be an alternative to Kerberos in some scenarios, however it is currently still experimental and not suitable for production use. The main advantage is that MTLS can be easier to setup if there are only a few machines and users because it doesn't require all the services that are necessary for a working Kerberos system.
The following section describes the setup in detail.
Starting at kernel version 6.4, Linux supports RPC-with-TLS.
It uses ktls
, a TLS implementation in the kernel.
In order to keep the code simple, the initial handshake is not implemented in the kernel and is instead delegated to a user space program using a netlink
interface.
The implementation of this program is tlshd
from the ktls-utils
package which uses the GnuTLS library to perform the handshake.
ktls
and tlshd
are not directly related to RPC or NFS and can also be used for other protocols.
At the moment the only other protocol that uses this technology is NVMe.
With a compatible network adapter, it is possible to use the ktls
offload feature that allows the encryption and decryption to be performed on the network adapter in order to reduce the CPU load.
As mentioned previously, RPC-with-TLS supports two modes.
- TLS: only the server is authenticated, similar to visiting a standard HTTPS website. An attacker cannot read or manipulate traffic between clients and the server but can still connect to the server and pretend to be a legtitimate client.
- MTLS: mutual authentication between server and client, similar to HTTPS with client certificates. An attacker cannot connect to the server without a valid certificate. On the Linux implemetation, a legitimate client can still impersonate other clients by using arbitrary UIDs.
Apart from the traditional TLS modes with certificates which work similar to HTTPS websites, ktls
and tlshd
also support TLS-PSK which uses pre-shared symmetric keys that are stored in a kernel keyring.
This mode is specified as optional in the RPC-with-TLS RFC but not supported by the Linux implementation.
It is only used by NVMe at the moment.
Both the server and the client use the same configuration file.
[authenticate]
#Keyrings, used for NVMe with TLS-PSK, not needed for NFS
#keyrings= <keyring>;<keyring>;<keyring>
#This section is for the client, remove orcomment out on the server
[authenticate.client]
#If no truststore is specified, the system's truststore will be used
x509.truststore = <pathname>
x509.certificate = <pathname>
x509.private_key = <pathname>
#This section is for the server, remove or comment out on the client
[authenticate.server]
#If no truststore is specified, the system's truststore will be used
x509.truststore = <pathname>
x509.certificate = <pathname>
x509.private_key = <pathname>
The following two sections describe how to set up both the TLS and MTLS mode. If you want to use MTLS, follow the instructions for TLS first.
To configure the TLS mode the, following files are necessary on the server:
- a private key
- a certificate containing the hostname, can be self-signed
The client needs a trust store that contains the certificate that was used to sign the server certificate. If the server uses a self-signed certificate, the trust store has to contain the certificate itself.
This section describes how to configure a setup where the server uses a certificate that is not self-signed and all keys and certificates are provided as files.
Generate a key and a certificate that will be used to sign server certificates with the following command.
certtool --generate-privkey --outfile server_signing.key
Create a certificate using the following command.
certtool --generate-self-signed --load-privkey server_signing.key --outfile server_signing.cert
Enter two characters as the country code, specify the number of days until expiration and answer the question "Does the certificate belong to an authority?" and "Is the above information ok?" with "y", everything else can be left blank. The country code in the certificate will be needed later for MTLS because in this mode the client and server will store each other's certificates in the kernel keyring which will result in an error if no country code is specified.
Generate a private key for the server.
certtool --generate-privkey --outfile server.key
Create a certificte containing the hostname of the server signed by the server signing key that was created before.
certtool --generate-certificate --load-privkey server.key --outfile server.cert --load-ca-certificate server_signing.cert --load-ca-privkey server_signing.key
Enter two characters as the country code, specify a shorter expiration than the previous certificate and answer the question "Enter a dnsName of the subject of the certificate" with the hostname of the server. Alternatively leave the dnsName field blank and specify the IP address when prompted. The clients have to be able to reach the server using the hostname or IP address specified in the certificate. It is possible to set mutliple names and an IP address in the same certificate.
Copy server.key
and server.cert
to the server and ensure that only root is able to read the key.
Also make sure that the key is located on a file system where it cannot be read via NFS using the no_subtree_check
attack.
In the server section of the /etc/tlshd.conf
file, specify the path to the key and the certificate.
A trust store is not needed on the server, it will be required later for MTLS.
[authenticate.server]
#x509.truststore = <pathname>
x509.certificate = /PATH/TO/server.cert
x509.private_key = /PATH/TO/server.key
By default all exports are accessible with and without TLS.
In order to only allow access via TLS, set the option xprtsec=tls
on the export.
Copy the server_singing.cert
file to the client.
In the client section of the /etc/tlshd.conf
file, specify the path to the server signing certificate.
A certificate and private key are not needed on the client, they will be required later for MTLS.
[authenticate.client]
x509.truststore = /PATH/TO/server_signing.cert
#x509.certificate = <pathname>
#x509.private_key = <pathname>
Alternatively, it is also possible to add the certificate to the system's truststore instead of explicitly specifying it in the configuration file. In this case, leave the configuration parameter for the trust store commented out.
By default, the mount command will not try to establish a TLS connection. Use the following command to use TLS.
sudo mount -t nfs -o xprtsec=tls server:/export /mountpoint
It is important to refer to the server using the hostname specified in the server certificate.
If the xprtsec=tls
option is ommitted, the client will not try to upgrade the connection to TLS.
This will cause the mount to fail if the export only supports TLS.
The MTLS configuration is based on the TLS configuration, so follow the steps from the previous section to get a working TLS setup.
The generation of keys and certificates for client authentication is symmetric to the server key setup.
Generate a key and a certificate that will be used to sign client certificates with the following command.
certtool --generate-privkey --outfile client_signing.key
Create a certificate using the following command.
certtool --generate-self-signed --load-privkey client_signing.key --outfile client_signing.cert
Enter two characters as the country code, specify the number of days until expiration and answer the question "Does the certificate belong to an authority?" and "Is the above information ok?" with "y", everything else can be left blank.
Generate a private key for the client.
certtool --generate-privkey --outfile client.key
Create a certificte containing the hostname of the client signed by the client signing key that was created before.
certtool --generate-certificate --load-privkey client.key --outfile client.cert --load-ca-certificate client_signing.cert --load-ca-privkey client_signing.key
Enter two characters as the country code, specify a shorter expiration and answer the question "Enter a dnsName of the subject of the certificate" with the hostname of the client. The server has to be able to perform a reverse lookup on the given hostname.
Copy client.key
and client.cert
to the client and make sure that only root is able to read the key.
Add the client key and certificate to the client section of tlshd.conf
.
The trust store remains the server signing certificate that was created previously.
[authenticate.client]
x509.truststore = /PATH/TO/server_signing.cert
x509.certificate = /PATH/TO/client.cert
x509.private_key = /PATH/TO/client.key
Copy client_signing.cert
to the server.
Add the client signing certificate as the trust store in the server section of tlshd.conf
[authenticate.server]
x509.truststore = /PATH/TO/client_signing.cert
x509.certificate = /PATH/TO/server.cert
x509.private_key = /PATH/TO/server.key
Alternatively, it is also possible to add the certificate to the system's trust store instead of explicitly specifying it in the configuration file.
In order to only allow clients with MTLS to access an export, set the option xprtsec=mtls
on the export.
Use the following command to mount an export using mtls.
sudo mount -t nfs -o xprtsec=mtls server:/export /mountpoint