This is a setup for NFS4 with Kerberos and secure LDAP on Debian Wheezy. It's an update on the Squeeze manual (see history) and it is not really well tested at the moment (I did an upgrade from Squeeze myself, not a new install). But I've got some good experiences from users. It does not seem to work directly using jessie (but is still of great help).

Kerberos will use LDAP as backend database. Most of this setup comes from Heiko Noordhof. I know, there are allready pages in this wiki about NFS4, Kerberos and LDAP. This howto tries to give an easy start.

This page is about the server setup. You can find the client setup here:

The idea is, that you copy/paste parts of the howto to your console. Look out when you copy many lines, some commands (like "apt-get" and "read") will remove later lines from memory. That's why I made an empty line after that commands.

Remark: this page uses libnss-ldap, but the release notes advices to replace that with libnss-ldapd: I have not tried that at the moment on the server, but on the client I've tried it with Debian 8, but it did not work for me. Maybe I have to try harder ;-)

First of all, you will need a correct FQDN hostname. If you give the command "hostname --fqdn" this should give something like "" and not only "server". And you need also a correct reversed DNS, so e.g. "host" should give the complete name. I will advice you to add a line like this to /etc/hosts :   server

where "server" is the hostname of this machine, and "" is your domain.

This manual is not really about DNS, but I use the program "dnsmasq", because it does both DHCP and DNS, and it adds an IP it gives with DHCP automatically to DNS and reversed DNS. After installing it, you can edit the configfile (/etc/dnsmasq.conf), but I think it's better to create a configfile /etc/dnsmasq.d/default , something like :

server= # dns server 1 of your provider
server= # dns server 2 of your provider
domain="" # your domain name
dhcp-range=,,12h # the dhcp range and lease time
# with lines like this you can make fixed IP's: 
# dhcp-host=14:da:e9:96:c3:2b,

After this you have to restart dnsmasq and to test it:

service dnsmasq restart

And when it works:

echo "nameserver" > /etc/resolv.conf

In the firewall you need this ports open: 53 tcp and udp (dns), 67 udp (dhcp), 88 tcp and udp (kerberos), 123 udp (ntp), 636 tcp (ldaps), 749 tcp (kadmin), 2049 tcp (nfs).

Server setup:

# become root:
su -

# When you did install LDAP or Kerberos or NFS before, it is a good idea to remove it to
# have a clean start. Backup it first if you want to keep it!
apt-get purge krb5-kdc-ldap gnutls-bin krb5-admin-server nfs-kernel-server \
  ldap-utils slapd krb5-kdc

rm -r /var/lib/ldap/
rm -r /etc/ldap/
rm -r /etc/krb5kdc/
rm -r /root/kerberos-setup/
rm -r /srv/nfs4/
rm /etc/krb5.keytab
rm /etc/krb5.conf
rm /etc/idmapd.conf
rm /etc/ssl/certs/CAself-cert.pem
rm /etc/ssl/certs/*_slapd_cert.pem
rm /etc/ssl/*.info
rm /etc/ssl/private/*_slapd_key.pem
rm /etc/ssl/private/CAself-key.pem

# check your hostname if it's correct. When not, see the part at the begin of the howto
hostname --fqdn

# Main setup variables defaults
SERVER=$(hostname --fqdn)   # something like:
DOMAIN=${SERVER#*.}         # something like:
REALM=$(echo "${DOMAIN}" | tr '[:lower:]' '[:upper:]')   # something like: EXAMPLE.COM
LDAPROOT=""; IFS="."; for DC in $DOMAIN ; do LDAPROOT="${LDAPROOT},dc=$DC"; done;
LDAPROOT="${LDAPROOT#,}"    # something like: dc=example,dc=com
Change this defaults when needed with something like: DOMAIN='' \n"

# Install packages without questions. You can also answer the questions but this is
# pointless, because the configuration will be overwritten later.
# Some errors are normal because of the noninteractive install
DEBIAN_FRONTEND=noninteractive apt-get install ldap-utils slapd nfs-kernel-server \
  krb5-admin-server krb5-kdc krb5-kdc-ldap krb5-doc libnss-ldap nscd libpam-ldap \
  gnutls-bin ssl-cert ntp pwgen ssh rpl

# Directory for temporary setup files, good place to search later for passwords:
mkdir "$SETUPDIR"
chmod 700 "$SETUPDIR"

# Setup ldap.conf for clients
cat <<EOF >/etc/ldap/ldap.conf
URI     ldapi://

# Setup SSL/TLS certificate (self-signed) for TLS on LDAP
certtool --generate-privkey >"${CA_KEY}"
cat <<EOF >"${CA_INFO}"
cn = ${DOMAIN}
expiration_days = 8000
certtool \
    --generate-self-signed \
    --load-privkey "${CA_KEY}" \
    --template "${CA_INFO}" \
    --outfile "${CA_CERT}"
chgrp ssl-cert "${CA_KEY}"
chmod 0640 "${CA_KEY}"

# Generate private-key for TLS on the LDAP-service
certtool --generate-privkey >"${LDAP_TLS_KEY}"
cat <<EOF >"${LDAP_TLS_INFO}"
organization = ${DOMAIN}
cn = ${SERVER}
expiration_days = 8000
certtool \
    --generate-certificate \
    --load-privkey "${LDAP_TLS_KEY}" \
    --load-ca-certificate "${CA_CERT}" \
    --load-ca-privkey "${CA_KEY}" \
    --template "${LDAP_TLS_INFO}" \
    --outfile "${LDAP_TLS_CERT}"
chgrp ssl-cert "${LDAP_TLS_KEY}"
chmod 0640 "${LDAP_TLS_KEY}"

# Set access to ssl keys for LDAP-daemon (slapd)
adduser openldap ssl-cert

# Generate hash from admin password
LDAP_ADMIN_PW=$(pwgen -s 10 1)
echo -n "$LDAP_ADMIN_PW" >ldap-admin-pw.txt
chmod 600 ldap-admin-pw.txt
LDAP_ADMIN_HASH=$(slappasswd -h '{SHA}' -T ldap-admin-pw.txt)
touch passwords.txt; chmod 600 passwords.txt
echo "LDAP admin password: $LDAP_ADMIN_PW" >> passwords.txt

# Create ldif 1 and load it into LDAP
cat <<EOF >slapd-loglevel.ldif
dn: cn=config
changeType: modify
replace: olcLogLevel
olcLogLevel: stats
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f slapd-loglevel.ldif

# Create ldif 2 and load it into LDAP
cat <<EOF >slapd-tls.ldif
dn: cn=config
changeType: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: ${CA_CERT}
add: olcTLSCertificateFile
olcTLSCertificateFile: ${LDAP_TLS_CERT}
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: ${LDAP_TLS_KEY}
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f slapd-tls.ldif

# Create ldif 3 and load it into LDAP
cat <<EOF >slapd-database.ldif
dn: olcDatabase={1}hdb,cn=config
changeType: modify
replace: olcDbConfig
olcDbConfig: {0}set_cachesize 0 2097152 0
olcDbConfig: {1}set_lk_max_objects 1500
olcDbConfig: {2}set_lk_max_locks 1500
olcDbConfig: {3}set_lk_max_lockers 1500
olcDbConfig: {4}set_flags DB_LOG_AUTOREMOVE
replace: olcRootPW
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f slapd-database.ldif

# Do not listen on the clear-text port (389) only SSL/LDAPS (636)
sed -i 's|^SLAPD_SERVICES.*|SLAPD_SERVICES="ldaps:/// ldapi:///"|' /etc/default/slapd

# Add indices
cat <<EOF >indices.ldif
dn: olcDatabase={1}hdb,cn=config
add: olcDbIndex
olcDbIndex: cn eq,pres
olcDbIndex: uid eq,pres
olcDbIndex: uidNumber eq,pres
olcDbIndex: gidNumber eq,pres
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f indices.ldif

# Restart LDAP-server
/etc/init.d/slapd restart

# Setup LDAP structure
# Generate random password for kerberos-admin account in LDAP, and a hash.
LDAP_KRBADMIN_PW=$(pwgen -s 12 1)
echo -n "$LDAP_KRBADMIN_PW" >ldap-krbadmin-pw.txt
chmod 600 ldap-krbadmin-pw.txt
LDAP_KRBADMIN_HASH=$(slappasswd -h '{SHA}' -T ldap-krbadmin-pw.txt)

# Create ldif 4 and load it into LDAP
cat <<EOF >structure.ldif
dn: cn=krb-admin,${LDAPROOT}
cn: krb-admin
objectClass: organizationalRole
objectClass: simpleSecurityObject
userPassword: ${LDAP_KRBADMIN_HASH}

dn: ou=groups,${LDAPROOT}
objectClass: organizationalUnit
ou: groups

dn: ou=users,${LDAPROOT}
objectClass: organizationalUnit
ou: users
ldapadd -x -D cn=admin,"${LDAPROOT}" -y ldap-admin-pw.txt -f structure.ldif

# Setup kerberos services, with backend database in LDAP
gunzip -c /usr/share/doc/krb5-kdc-ldap/kerberos.schema.gz >krb.schema
echo 'include krb.schema' >slapd.tmp.conf
slaptest -f slapd.tmp.conf -F ./
mv  'cn=config/cn=schema/cn={0}krb.ldif' krb.schema.ldif
sed -i 's/^dn: cn={0}krb/dn: cn=kerberos,cn=schema,cn=config/' krb.schema.ldif
sed -i 's/^cn: {0}krb/cn: kerberos/' krb.schema.ldif
sed -i '/^structuralObjectClass/,$d' krb.schema.ldif
ldapadd -QY EXTERNAL -H ldapi:///  -f krb.schema.ldif

# Add index
cat <<EOF >krbindex.ldif
dn: olcDatabase={1}hdb,cn=config
add: olcDbIndex
olcDbIndex: krbPrincipalName eq,pres,sub
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f krbindex.ldif

# Add some ACL's
cat <<EOF >krbacl.ldif
dn: olcDatabase={1}hdb,cn=config
replace: olcAccess
olcAccess: to attrs=userPassword,shadowLastChange,krbPrincipalKey by dn="cn=krb-admin,${LDAPROOT}" write by anonymous auth by self write by * none
add: olcAccess
olcAccess: to dn.base="" by * read
add: olcAccess
olcAccess: to * by dn="cn=krb-admin,${LDAPROOT}" write by * read
ldapmodify -v -Y EXTERNAL -H ldapi:/// -f krbacl.ldif

# Setup MIT-kerberos
cp /etc/krb5.conf /etc/krb5.conf.bak-$(date +%Y%m%d_%H%M)
cat <<EOF >/etc/krb5.conf
        default_realm = ${REALM}
        krb4_config = /etc/krb.conf
        krb4_realms = /etc/krb.realms
        kdc_timesync = 1
        ccache_type = 4
        forwardable = true
        proxiable = true
        ticket_lifetime = 525600

        ${REALM} = {
                kdc = ${SERVER}
                admin_server = ${SERVER}
                default_domain = ${DOMAIN}
                database_module = openldap_ldapconf

        .${DOMAIN} = ${REALM}

        krb4_convert = true
        krb4_get_tickets = false

     admin_server = SYSLOG:INFO:DAEMON
     default = SYSLOG:INFO:DAEMON

        ldap_kerberos_container_dn = cn=krbContainer,${LDAPROOT}

        openldap_ldapconf = {
                db_library = kldap
                ldap_kdc_dn = "cn=krb-admin,${LDAPROOT}"
                ldap_kadmind_dn = "cn=krb-admin,${LDAPROOT}"
                ldap_service_password_file = /etc/krb5kdc/service.keyfile
                ldap_servers = ldapi:///
                ldap_conns_per_server = 5

# create kdc.conf
cat <<EOF > /etc/krb5kdc/kdc.conf
    kdc_ports = 750,88

    ${REALM} = {
        database_name = /var/lib/krb5kdc/principal
        admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
        acl_file = /etc/krb5kdc/kadm5.acl
        key_stash_file = /etc/krb5kdc/stash
        kdc_ports = 750,88
        max_life = 365d 10h 0m 0s
        max_renewable_life = 7d 0h 0m 0s
        master_key_type = des3-hmac-sha1
        supported_enctypes = aes256-cts:normal arcfour-hmac:normal des3-hmac-sha1:normal
        default_principal_flags = +preauth

# ACL to grant all permissions to principals like "<name>/admin@<REALM>"
echo '*/admin *' >>/etc/krb5kdc/kadm5.acl

# Ppassword for kerberos LDAP backend
KLDAPPW=`pwgen -s 10 1`
echo "$KLDAPPW" > $SETUPDIR/kerberos-ldap-password.txt
echo "Kerberos LDAP password: $KLDAPPW" >> passwords.txt

# Tell which password to use for kerberos LDAP backend
echo -e "You will now be asked to enter the password (twice). \n
Please copy and paste the password, it is: $KLDAPPW\n"

# Create basic entries in the kerberos LDAP backend
kdb5_ldap_util -w "$LDAP_KRBADMIN_PW" \
    -D  "cn=krb-admin,$LDAPROOT" \
    create \
    -subtrees "$LDAPROOT" \
    -r "$REALM" \
    -s \
    -H ldapi:///

# tell which password to use for the account kerberos daemons use:
echo -e "You will now be asked to enter another password (twice). \n
Please copy/paste the following password in there:  $LDAP_KRBADMIN_PW\n"

# Stash the LDAP-password for the account the kerberos daemons use to
# read and write to LDAP
kdb5_ldap_util -w "$LDAP_KRBADMIN_PW" \
    -D  "cn=krb-admin,$LDAPROOT" \
    stashsrvpw \
    -f /etc/krb5kdc/service.keyfile \

# Restart the Kerberos daemons
/etc/init.d/krb5-kdc restart  
/etc/init.d/krb5-admin-server restart

# policies, not very secure, you can change it later with modify_policy, see "man kadmin"
kadmin.local -q "add_policy user"
kadmin.local -q "add_policy machine"
kadmin.local -q "add_policy admin"

# create an kerberos admin user:
read -p "Kerberos admin username, normally your name (without '/admin'): " KADMIN

echo "kerberos admin username: $KADMIN/admin" >> passwords.txt
KADMINPWD=`pwgen -s 10 1`
read -p "Password for Kerberos admin user, specify a password (return='$KADMINPWD') : " KADMINPW

if test "$KADMINPW" = ""; then KADMINPW="$KADMINPWD"; fi
echo "kerberos admin paswword: $KADMINPW" >> passwords.txt
kadmin.local -q "addprinc -pw $KADMINPW -policy admin $KADMIN/admin"

# The NFS-server must know the same user names and UID's as the clients.
# Therefore our server name service switch needs to be a client of the server itself.
cp /etc/libnss-ldap.conf /etc/libnss-ldap.conf-$(date +%Y%m%d_%H%M)
cat <<EOF >/etc/libnss-ldap.conf
uri ldapi://
ldap_version 3
base ${LDAPROOT}
scope sub

# Activate LDAP in name service switch
sed -i 's/compat/files ldap/' /etc/nsswitch.conf
/etc/init.d/nscd restart

# Setup NFS4-service
# ==> Security level "krb5i" is middle level. More secure (everything encrypted)
# would be: "krb5p". This must match mount option on the client.
mkdir -p /srv/nfs4/home
echo "/srv/nfs4 *.${DOMAIN}(rw,sync,sec=krb5i,fsid=0,crossmnt,no_subtree_check) " >> /etc/exports
echo "/srv/nfs4/home *.${DOMAIN}(rw,sync,sec=krb5i,no_subtree_check) " >> /etc/exports

# Create kerberos principal for NFS-server
kadmin.local -q "addprinc -randkey nfs/${SERVER}"

# Store the key for the nfs-service principal in (default) keytab file
kadmin.local -q "ktadd nfs/${SERVER}"

# Configure NFS-service daemons for kerberized NFS4:
sed -i 's/^ *NEED_SVCGSSD=.*$/NEED_SVCGSSD=yes/' /etc/default/nfs-kernel-server
sed -i 's/^ *NEED_IDMAPD=.*$/NEED_IDMAPD=yes/' /etc/default/nfs-common
sed -i 's/^ *NEED_GSSD=.*$/NEED_GSSD=yes/' /etc/default/nfs-common

# create idmapd.conf
cat <<EOF >/etc/idmapd.conf
Verbosity = 1 
Pipefs-Directory = /var/lib/nfs/rpc_pipefs

Nobody-User = nobody
Nobody-Group = nogroup

# Restart the NFS4 (related) services
/etc/init.d/nfs-kernel-server stop
/etc/init.d/nfs-common restart
/etc/init.d/nfs-kernel-server start

# in many cases you want to change the umask, so normal users will give write access for the group.
# see "man pam_umask" or for more information.
echo -e "\nsession optional umask=0002" >> /etc/pam.d/common-session

# Download and install some usefull scripts in /usr/local/sbin/
# maybe take a look first in
mkdir -p /srv/nfs4/data/; mkdir -p /srv/nfs4/home/
mkdir scripts; cd scripts
wget -qr -nd --no-parent
rm index.html*
rpl "dc=example,dc=com" "$LDAPROOT" variables
rpl "secret" "$LDAP_ADMIN_PW" variables
chmod +x *
chmod 600 variables uid guid
cp -a * /usr/local/sbin/
touch /var/log/au.log
chmod 600 /var/log/au.log

# Test creating users
au jan25  # creates a user "jan25" in Kerberos and LDAP
getent passwd jan25   # check if the user excists
ru jan25  # removes the test user

# Add a real user and an machine account
au   # add user
ama  # add machine account

# There is a problem starting the daemons, see: 
rpl "exit 0" "/etc/init.d/krb5-admin-server start
/etc/init.d/krb5-kdc start
exit  0" /etc/rc.local

# Write some extra information to the password.txt file:
echo "LDAP root:  ${LDAPROOT}" >> $SETUPDIR/passwords.txt
echo "LDAP super-user account: cn=admin,${LDAPROOT}" >> $SETUPDIR/passwords.txt
echo "Kerberos realm: ${REALM}" >> $SETUPDIR/passwords.txt

The setupdir contains all passwords among other things. Please make sure to remove this directory later.

When you have troubles, it's a good idea to check if all the daemons are running:

ps aux | grep [r]pc.gssd
ps aux | grep [i]dmap
ps aux | grep [r]pc.svcgssd
ps aux | grep [k]rb5kdc
ps aux | grep [k]admind

When you upgrade from Squeeze, you need a new /etc/krb5.keytab file, because in the old key are wrong encryption-types. And you need in many cases a new private-key for TLS on the LDAP-service. In the old situation (Squeeze) the validity date of this key was not correct checked, but in Wheezy it is, see this bug:

Please send comments to