Table of Contents

Autenticazione Kerberos per NFSv4

La versione 4 del protocollo NFS si distingue dalla precedente versione 3 soprattutto per l'uso obbligatorio di una sicurezza forte (basata su Kerberos versione 5) e di un migliore meccanismo di locking (mandatory locking invece di advisory locking). L'uso del protocollo TCP su una sola porta facilita il passaggio attraverso i firewall.

In questo esempio semplificato il Kerberos Distribution Center (KDC) dove gira il demone è lo stesso host su cui gira il servizio NFS. Non esistono KDC slave, solo il master.

La distribuzione di riferimento è una GNU/Linux Debian Lenny.

Web references

Verifiche preliminari

Installazione server

Questi i pacchetti Debian da installare sul server:

Si deve creare il realm (dominio a cui apparterranno i server e i client) e alcuni principal (credenziali di utenti e servizi). L'installazione del pacchetto krb5-config richiede il nome del realm il nome dei Kerberos servers e dell'Administrative server, queste informazioni vengono scritte in due file di configurazione:

In Debian abbiamo il comodo script krb5_newrealm che crea il database con gli account /var/lib/krb5kdc/principal, lo stash (un file che contiene la master key) /etc/krb5kdc/stash ed avvia i demoni eseguendo nell'ordine:

Ecco in dettaglio la creazione del realm con la richiesta della master key:

# kdb5_util create -s
Loading random data
Initializing database '/var/lib/krb5kdc/principal' for realm 'MYREALM.ORG',
master key name 'K/M@MYREALM.ORG'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key:

Lo script crea anche un file con le ACL /etc/krb5kdc/kadm5.acl, è consuetudine scommentare l'ultima riga per dare diritto di accesso a tutti i principal (account) il cui nome sia del tipo nome/admin:

# This file Is the access control list for krb5 administration.
# When this file is edited run /etc/init.d/krb5-admin-server restart to activate
# One common way to set up Kerberos administration is to allow any principal
# ending in /admin  is given full administrative rights.
# To enable this, uncomment the following line:
*/admin *

Quindi si deve creare anche un account a cui dare questi privilegi, ad esempio root/admin@MYREALM.ORG. Si utilizza l'utilty kadmin.local, come mostrato nel paragrafo successivo.

A questo punto sul server abbiamo tre demoni in esecuzione:

Dump e restore del database Kerberos

Per salvare tutti i principals in un file ASCII:

kdb5_util dump -verbose kerberos_principals.dump

Per effettuare il restore su una macchina appena installata, senza aver creato il database in precedenza:

kdb5_util load -verbose kerberos_principals.dump

Per completare il restore bisogna recuperare anche il file con lo stash (gli eventuali messaggi di errore indicano la mancanza della master key) e le ACL, si tratta dei file:

Modificare il lifetime dei ticket

La durata predefinita di un ticket è di 10 ore (installazione Debian). Per modificarla sono necessarie diverse operazioni.

Editare il file /etc/krb5kdc/kdc.conf e modificare il max_life del realm predefinito. Questo parametro viene usato per creare i nuovi principal (non serve riavviare alcun servizio):

[realms]
    RIGACCI.ORG = {
        max_life = 16h 0m 0s

Modificare il maxlife del principal krbtgt/REALM@REALM, ad esempio con kdamin.local:

getprinc krbtgt/RIGACCI.ORG@RIGACCI.ORG
Maximum ticket life: 0 days 10:00:00
...
modprinc -maxlife "0 days 16 hours" krbtgt/RIGACCI.ORG@RIGACCI.ORG

Modificare il maxlife di tutti i principal creati in precedenza.

Creazione account utente

L'utility kadmin.local consente di creare nuovi account (nella terminologia Kerberos sono chiamati principal). Si esegue con i privlegi di root sul server che ospita il KDC e non richiede alcuna ulteriore autenticazione.

kadmin.local:  addprinc niccolo@MYREALM.ORG
WARNING: no policy specified for niccolo@MYREALM.ORG; defaulting to no policy
Enter password for principal "niccolo@MYREALM.ORG": 
Re-enter password for principal "niccolo@MYREALM.ORG": 
Principal "niccolo@MYREALM.ORG" created.

kadmin.local:  addprinc root@MYREALM.ORG
...
kadmin.local:  addprinc root/admin@MYREALM.ORG
...

kadmin.local:  listprincs
K/M@MYREALM.ORG
kadmin/admin@MYREALM.ORG
kadmin/changepw@MYREALM.ORG
kadmin/history@MYREALM.ORG
krbtgt/TEXNET.ORG@MYREALM.ORG
niccolo@MYREALM.ORG
root/admin@MYREALM.ORG
root@MYREALM.ORG

Come si vede esistono altri principal di servizio, oltre a quelli esplicitamente creati da noi.

Dopo aver aggiunto dei principal è possibile delegare le funzioni di amministratore ad altri utenti aggiungendo le opportune ACL in /etc/krb5kdc/kadm5.acl. L'utente delegato userà l'utility kadmin. È convenzione diffusa dare privilegi di amministratore a tutti gli utenti del tipo name/admin@REALM.

Per gestire i ticket Kerberos si usano le utility kinit, klist e kdestroy.

Policy di default

Conviene definire una policy di default, da cui tutti i nuovi account ereditano alcune caratteristiche, ad esempio la lunghezza minima della password. Per gli account già creati bisogna esplicitamente settare la nuova policy:

add_policy -minlength 5 -minclasses 2 default
get_policy default
modify_principal -policy default niccolo@MYREALM.ORG

Installazione client

Questi i pacchetti Debian da installare sul client:

L'installazione scrive in /etc/krb5.conf il realm e il server KDC predefinito.

Creazione account per server e client NFS

Sul server KDC si creano due account, uno per il server NFS e uno per il client (nella terminologia Kerberos un account viene detto principal), il nome dell'account è del tipo service_name/hostname@REALM. Sul server e sul client NFS bisogna invece creare le rispettive chiavi, che verranno salvate nei rispettivi file /etc/krb5.keytab.

Giusto per fare un esempio, una parte la eseguiamo sul server KDC (che nel nostro esempio è anche server NFS) da utente root con kadmin.local:

# kadmin.local
addprinc -randkey nfs/inside.mydomain.org@MYREALM.ORG
WARNING: no policy specified for nfs/inside.mydomain.org@MYREALM.ORG; defaulting to no policy
Principal "nfs/inside.mydomain.org@MYREALM.ORG" created.
kadmin.local:  listprincs
kadmin.local:  ktadd -e des nfs/inside.mydomain.org@MYREALM.ORG
Entry for principal nfs/inside.mydomain.org@MYREALM.ORG with kvno 3,
   encryption type DES cbc mode with RSA-MD5 added to keytab WRFILE:/etc/krb5.keytab.
kadmin.local:  exit

Il comando ktadd aggiunge una chiave nel file locale /etc/krb5.keytab. Questa chiave deve stare sul server NFS, non confondiamoci col fatto che KDC e server NFS sono sullo stesso host in questo esempio.

:!: ATTENZIONE al bug 413838:

Notare l'uso del comando ktadd con l'opzione -e des . Il risultato è la creazione di una sola entry nella keytab, associata alla crittografia DES; l'impostazione predefinita sarebbe quella di creare due entry, una delle quali con crittografia triplo DES. Il server NFS supporta solo il DES: un bug nella procedura di negoziazione Kerberos causa il fallimento del mount se è presente anche la chiave triplo DES.

La seconda parte la eseguiamo sul client NFS, con il comando kadmin (ovviamente richiede la password di amministratore Kerberos). È necessario usare l'utente root perché si deve aggiungere una chiave al file locale /etc/krb5.keytab:

# kadmin -p root/admin@MYREALM.ORG
Authenticating as principal root/admin@MYREALM.ORG with password.
Password for root/admin@MYREALM.ORG:
kadmin:  addprinc -randkey nfs/ulisse.mydomain.org@MYREALM.ORG
WARNING: no policy specified for nfs/ulisse.mydomain.org@MYREALM.ORG; defaulting to no policy
Principal "nfs/ulisse.mydomain.org@MYREALM.ORG" created.
kadmin:  ktadd nfs/ulisse.mydomain.org@MYREALM.ORG
Entry for principal nfs/ulisse.mydomain.org@MYREALM.ORG with kvno 5,
   encryption type Triple DES cbc mode with HMAC/sha1 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal nfs/ulisse.mydomain.org@MYREALM.ORG with kvno 5,
   encryption type DES cbc mode with CRC-32 added to keytab WRFILE:/etc/krb5.keytab.

Il comando addprinc genera un nuovo account sul server KDC remoto, il comando ktadd invece aggiunge una chiave nel file /etc/krb5.keytab locale.

Verificare che funzioni la risoluzione del nome DNS ulisse.mydomain.org, sia diretta che inversa.

Per vedere chiavi memorizzate localmente in /etc/krb5.keytab:

k5srvutil list
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   3 nfs/ulisse.mydomain.org@MYREALM.ORG
   3 nfs/ulisse.mydomain.org@MYREALM.ORG

Per eliminare delle chiavi si usa il comando k5srvutil delete.

Configurazione server NFS

Si configura il server NFS in modo che sappia trovare il server KDC (dovrebbe già essere così in seguito all'installazione), nel file /etc/krb5.conf:

[libdefaults]
        default_realm = MYREALM.ORG
[realms]
        MYREALM.ORG = {
                kdc = inside.mydomain.org
                admin_server = inside.mydomain.org
        }
[login]
        krb4_convert = false
        krb4_get_tickets = false

Infine il file /etc/exports deve richiedere l'autenticazione Kerberos e usare le opzioni specifiche di NFSv4:

/home  gss/krb5(fsid=0,rw,sync,no_root_squash,no_subtree_check)

Notare che dal kernel 2.6.23 la sintassi gss/krb5 è deprecata, si deve usare l'opzione sec= come suggerito da man exports(5).

Per vedere quali directory risultano esportate:

showmount -e 127.0.0.1
Export list for 127.0.0.1:
/home                      gss/krb5

ATTENZIONE: Non esportare sottodirectory di directory già esportate, specialmente mescolando tipi di accesso diverso (host e gss/krb5, NFSv3 e NFSv4). Si potrebbe incappare nell'errore che la sottodirectory non risulta esportata come ci si aspetta. In questo esempio si pensava di esportare /home e invece NFS cerca di esportare /:

mountd[2937]: refused mount request from 192.168.1.2 for /home (/): not exported

Altro possibile errore: se sul server non è attivo rpc.gssd oppure rpc.svcgssd il client non viene riconosciuto:

mountd[1116]: mount request from unknown host 192.168.1.2 for /home (/home)

Ricordarsi di attivare anche rpc.idmapd, altrimenti tutti i file esportati risulteranno appartenere a nobody:nogroup.

In Debian questo significa impostare NEED_SVCGSSD=yes in /etc/default/nfs-kernel-server e NEED_IDMAPD=yes e NEED_GSSD=yes in /etc/default/nfs-common.

Configurazione client NFS

È indispensabile attivare il demone rpc.idmapd, impostando NEED_IDMAPD=yes in /etc/default/nfs-common. Altrimenti user e group degli oggetti nel filesystem montato non sono corretti.

Bisogna attivare anche il demone rpc.gssd, con Debian è sufficiente impostare NEED_GSSD=yes in /etc/default/nfs-common. Altrimenti si incappa nel seguente errore:

# mount -v -t nfs4 inside.mydomain.org:/ /nfs/inside/home -o rw,sec=krb5
mount.nfs4: Connection timed out

ATTENZIONE: Con NFSv4 la directory esportata con l'opzione fsid=0 diventa la radice NFSv4, pertanto va montata come tale, senza specificare il percorso. Altrimenti questo è il messaggio di errore:

mount -t nfs4 inside.mydomain.org:/home /nfs/inside/home -o rw,sec=krb5
mount.nfs4: mounting inside.mydomain.org:/home failed, reason given by server:
  No such file or directory

Quindi per montare una tantum la directory:

mount -v -t nfs4 inside.mydomain.org:/ /net/inside -o rw,sec=krb5
mount.nfs4: pinging: prog 100003 vers 4 prot tcp port 2049
inside.mydomain.org:/ on /nfs/inside type nfs4 (rw,sec=krb5)

oppure per montarla ad ogni boot si mette in /etc/fstab:

inside.mydomain.org:/  /net/inside  nfs4  rw,sec=krb5  0  0

Troubleshooting

Se si sospettano problemi di permessi conviene controllare cosa dice il demone rpc.gssd sul client. Avviare il demone con l'opzione -vvv (Debian: impostando RPCGSSDOPTS=“-vvv” in /etc/default/nfs-common).

Ad esempio il seguente errore si risolve rinnovando il ticket Kerberos con il comando kinit(1):

rpc.gssd[13175]: ERROR: GSS-API: error in gss_acquire_cred(): The referenced credential has expired - No error

Qui abbiamo un problema con la risoluzione dei nomi; un nome risolve in un dirizzo IP il cui reverse lookup non risolve nel nome stesso:

# host cheope.rigacci.org
cheope.rigacci.org has address 192.168.200.245
# host 192.168.200.245
245.200.168.192.in-addr.arpa domain name pointer ldap.rigacci.org.

risultano i seguenti errori nel /var/log/auth.log del server:

krb5kdc[27119]: TGS_REQ (7 etypes {18 17 16 23 1 3 2}) 172.16.6.2: UNKNOWN_SERVER: authtime 1200091396,
    nfs/bari.rigacci.org@RIGACCI.ORG for nfs/ldap.rigacci.org@RIGACCI.ORG,
    Server not found in Kerberos database
krb5kdc[27119]: TGS_REQ (1 etypes {1}) 172.16.6.2: UNKNOWN_SERVER: authtime 1200091396,
    nfs/bari.rigacci.org@RIGACCI.ORG for nfs/ldap.rigacci.org@RIGACCI.ORG,
    Server not found in Kerberos database

e i seguenti sul client:

rpc.gssd[7053]: rpcsec_gss: gss_init_sec_context: (major) Unspecified GSS failure.
    Minor code may provide more information - (minor) Server not found in Kerberos database 

Altro errore legato al bug 413838: la negoziazione della crittografia DES (l'unica supportata dal server NFS) fallisce se sul server NFS esiste anche una chiave triple DES.

rpc.gssd[7053]: WARNING: Failed to create krb5 context for user with uid 0 with any credentials cache
                for server cheope.rigacci.org 

sul server si legge

rpc.svcgssd[9833]: leaving poll 
rpc.svcgssd[9833]: handling null request 
rpc.svcgssd[9833]: WARNING: gss_accept_sec_context failed 
rpc.svcgssd[9833]: ERROR: GSS-API: error in handle_nullreq: gss_accept_sec_context(): Unspecified GSS failure.
    Minor code may provide more information - Key version number for principal in key table is incorrect 
rpc.svcgssd[9833]: sending null reply