Getting Kerberos keytabs to the Kerberos client

Jurjen Bokma

July 2010


[Warning]Warning

Some key details twisted, and others omitted, for the sake of security.

We use NFSv4 with Kerberos, so on both the NFS servers and the NFS clients, we need to extract Kerberos principal keys into keytabs. We want to do this as securely as possible, so fetching them across HTTP with the rest of the unattended install is not a good idea.

So we have the Kerberos client port-knock on a server where knockd is running. The user running the daemon also holds a Kerberos principal that is allowed to create nfs/*@REALM principals, and to extract their keytabs. When a client knocks, the server creates a principal and extracts a keytab for it, and uses SSH to copy it to the client.

[Note]Note

This setup is still sensitive to spoofing of IP numbers. But if a rogue steals an IP number, the legitimate owner will cease being able to use NFS, so the attack will be noticed.

[Note]Note

This setup doesn't work too well with clients running behind NATting firewalls.

The knockd config in /etc/knockd.conf:

[Kerberizer]
sequence    = 13214,12986,28765
seq_timeout = 15
command     = /usr/local/bin/kerberos-nfs-key %IP%
tcpflags    = syn
    

The script that creates principals and extracts keys (/usr/local/bin/kerberos-nfs-key):

#!/bin/bash

set -e
set -u

REALM=MYDOMAIN.COM

[ -n "${1}" ] || exit 1

IP=${1}

FQDN=$(host ${IP}|awk '{print $5}'|sed 's/\.$//g')

# Against the occasional fixed-file attack
rm -f /var/tmp/nfs-${FQDN}.keytab

# Delete the old principal
kadmin -l delete nfs/${FQDN}@${REALM} || true

# (Re-)create the nfs/host principal
kadmin -l add \
-r \
--max-ticket-life=unlimited \
--max-renewable-life=unlimited \
--expiration-time=never \
--pw-expiration-time=never \
--attributes='' \
nfs/${FQDN}@${REALM}

# Extract the principal's keytab
kadmin -l ext_keytab \
-k /var/tmp/nfs-${FQDN}.keytab \
nfs/${FQDN}@${REALM}

# Scp the keytabl to the host
scp -o StrictHostKeyChecking=no /var/tmp/nfs-${FQDN}.keytab root@${FQDN}:/etc/krb5.keytab

rm /var/tmp/nfs-${FQDN}.keytab
    

And the script that sollicits a new key by knocking (it also enables the NFSv4 client, port knocking is at the bottom end):

#!/bin/bash

# This should get us a Kerberos principal

set -e

NFS_COMMON='/etc/default/nfs-common'
KRB5CONF=/etc/krb5.conf
HOME=/home
NFSSERVER=nfs.mydomain.com
IDMAPD_CONF=/etc/idmapd.conf
KADMIN=kadm.mydomain.com

# Adjust /etc/default/nfs-common
if ! grep '^NEED_STATD=no$' ${NFS_COMMON} ; then
echo "NEED_STATD=no" >> ${NFS_COMMON}
fi
if ! grep '^NEED_IDMAPD=yes$' ${NFS_COMMON} ; then
echo "NEED_IDMAPD=yes" >> ${NFS_COMMON}
fi
if ! grep '^NEED_GSSD=yes$' ${NFS_COMMON} ; then
echo "NEED_GSSD=yes" >> ${NFS_COMMON}
fi

# Adjust /etc/krb5.conf
if ! grep '^allow_weak_crypto = true$' ${KRB5CONF} ; then
sed -i.bak '/\[libdefaults\]/ a\
allow_weak_crypto = true' ${KRB5CONF}
fi

# Make sure gssd is started at boot
update-rc.d gssd defaults

# Create a mount point
install -d ${HOME} || true

# Create an fstab entry
if ! grep "^${NFSSERVER}:/  ${HOME} nfs4" /etc/fstab ; then
echo "${NFSSERVER}:/  ${HOME} nfs4   sec=krb5p 0 0" >> /etc/fstab
fi

cat <<EOF > ${IDMAPD_CONF}
[General]

Verbosity = 15
Pipefs-Directory = /var/lib/nfs/rpc_pipefs
Domain = mydomain.com
Local-Realms = MYDOMAIN.COM

[Mapping]

Nobody-User = nobody
Nobody-Group = nogroup

[Translation]

Method = nsswitch
EOF

# Create authorized_keys for root
install -d /root/.ssh
cat <<EOF *gt; /root/.ssh/authorized_keys
ssh-rsa <snip-and-replace even the public key>SAXmQE0QnodlGrA5XXi/nx035h6LB4mWiKOs8O2oQ/umJHLrr/MHurXtMOJLCZpofbXdlGredVwXYzw7mS2vX9q2FQ==
EOF

# Knock politely 
wget --connect-timeout 1 http://${KADMIN}:13214 || true
wget --connect-timeout 1 http://${KADMIN}:12986 || true
wget --connect-timeout 1 http://${KADMIN}:28765 || true

# Allow a few seconds for the server to fetch a Kerberos keytab
# and install it on this host using scp
sleep 15

# If it doesn't happen in five seconds, we reckon' it isn't going to happen at all,
# and we don't want that root key lyin' around
# FIXME: use an exit hook to remove it
rm /root/.ssh/authorized_keys

# Start the services
/etc/init.d/idmapd restart
/etc/init.d/gssd restart

# mount the NFS share
mount -a