Table of Contents

LDAP

LDAP definisce un semplice protocollo basato su TCP per la ricerca e l'aggiornamento di informazioni contenute all'interno di un directory service (database gerarchico).

Un elemento in una directory LDAP è referenziato in modo non ambiguo tramite un nome, detto distinguished name (DN), per esempio “cn=John Doe,ou=people,dc=wikipedia,dc=org”. Ogni elemento inoltre comprende una collezione di attributi.

Ognuno degli attributi dell'elemento è definito come membro di una classe di oggetti, raggruppati in uno schema. Ogni elemento nel database è associato a una o più classi di oggetti, che definiscono se un attributo sia opzionale o meno e che tipo di informazioni questo contenga. I nomi degli attributi solitamente sono scelti per essere facilmente memorizzabili, per esempio “cn” per common name, o “mail” per un indirizzo e-mail.

È possibile utilizzare LDAP per numerosi scopi; come database per l'autenticazione degli utenti Unix, come rubrica di contatti email, ecc.

Utilizzando LDAP per l'autenticazione è interessante la possibile integrazione con il sottosistema Kerberos versione 5. Le informazioni relative all'autenticazione (estremamente sensibili dal punto di vista della sicurezza) vengono tolte dal database LDAP e memorizzate nel più sicuro database Kerberos, inoltre si viene ad usufruire del sistema di ticketing di Kerberos, utilie ad esempio per abbinare il montaggio sicuro delle home directory via NFSv4.

Attributi LDAP

Questo è un elenco degli attributi più comuni utilizzati in una directory LDAP:

DN Distinguished_Name As the word 'distinguished' suggests, this is THE LDAP attribute that uniquely defines an object. Each DN must have a different name and location from all other objects. The other side of the coin is that DN provides a way of selecting any object in the LDAP directory. Once you have select the object, then you can change its attributes.
DC Domain_Content Example: DC=rigacci, DC=org. Note that DC=rigacci.org would be wrong.
CN Common Name From RFC2256: This is the X.500 commonName attribute, which contains a name of an object. If the object corresponds to a person, it is typically the person's full name.
OU Organizational_Unit
O OrganizationName
C CountryName
ST StateOrProvinceName
SN SurName From RFC2256: This is the X.500 surname attribute, which contains the family name of a person.

Schemi LDAP

Ognuno è libero di creare lo schema LDAP (struttura del database) che preferisce, è possibile anche disattivare l'opzione schemacheck in modo da poter inserire nel database oggetti non conformi allo schema (altamente sconsigliato).

Esistono comunque degli schemi predefiniti molto utilizzati, conviene adottare questi schemi in modo che i vari applicativi trovino il database nella forma in cui se lo aspettano. Ad esempio per memorizzare un archivio di contatti e-mail utilizzabile direttamente da Firefox si può utilizzare lo schema InetOrgPerson (RFC2798), un database per l'autenticazione può essere invece basato sullo schema posixAccount (nisSchema in RFC2307).

Il pacchetto slapd di Debian arriva con alcuni schemi predefiniti memorizzati in /etc/ldap/schema; fare riferimento ad essi anche per sapere quali sono gli attributi obbligatori (MUST) e quali facoltativi (MAY) di un oggetto, ad esempio:

objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top AUXILIARY
        DESC 'Abstraction of an account with POSIX attributes'
        MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
        MAY ( userPassword $ loginShell $ gecos $ description ) )

Fare attenzione che gli schemi possono essere di tipo STRUCTURAL, AUXILIARY o ABSTRACT (si guardi le definizioni dentro i file /etc/ldap/schema):

STRUCTURAL Indicates the attributes that the entry may have and where each entry may occur in the DIT.
AUXILIARY Indicates the attributes that the entry may have.
ABSTRACT Indicates a “partial” specification in the object class hierarchy; only structural and auxiliary subclasses may appear as entries in the directory.

Quindi quando si carica un oggetto dentro LDAP bisogna che sia riconducibile ad una objectClass di tipo STRUCTURAL. Ad esempio un oggetto non può essere dichiarato come semplice objectClass: posixAccount (che è una classe AUXILIARY); lo si deve completare dichiarando almeno una classe STRUCTURAL.

Tra le classi STRUCTURAL predefinite si potrebbe optare per la objectClass: top che è la più generica di tutte, infatti non impone la presenza di alcun attributo salvo un altro objectClass. In alternativa si potrebbe scegliere una objectClass: organizationalUnit adatta a descrivere persone ma anche gruppi, oppure la più specifica objectClass: person.

Autenticazione LDAP

In questo esempio si configura un server ed un client LDAP per effettuare l'autenticazione (login, ecc.). La distribuzione di riferimento è una Debian Lenny. In pratica dovremmo configurare il sistema di autenticazione PAM (il più usato in ambiente GNU/Linux) in modo tale che sia client di un server LDAP.

Configurazione del server

Dopo aver configurato il server potrebbe essere utile configurare un altro server che replichi lo stesso database, utilizzando Syncrepl.

I pacchetti da installare sul server sono:

L'installazione predefinita di Debian Lenny crea un database la cui radice ha un DN (Distinguished Name) dc=rigacci,dc=org che segue lo standard RFC2247 e un amministratore cn=admin con password:

dn: dc=rigacci,dc=org
dn: cn=admin,dc=rigacci,dc=org

Tutto è memorizzato nel database bdb (Berkeley DB) contenuto in /var/lib/ldap/, quindi nel file di configurazione /etc/ldap/slapd.conf non viene scritta alcuna password. Queste le parti salienti del file di configurazione:

# Database specific directives apply to this databasse until another
# 'database' directive occurs
database        bdb

# The base of your directory in database #1
suffix          "dc=rigacci,dc=org"

# rootdn directive for specifying a superuser on the database.
# rootdn          "cn=admin,dc=rigacci,dc=org"
# rootpw         {SSHA}lzz5HF6eP7fRaB3G6L85zhk6zWWP8+Fk

# Where the database file are physically stored for database #1
directory       "/var/lib/ldap"

# Who can change the userPassword.
access to attrs=userPassword,shadowLastChange
        by dn="cn=admin,dc=rigacci,dc=org" write
        by anonymous auth
        by self write
        by * none

# The admin dn has full write access, everyone else
# can read everything.
access to *
        by dn="cn=admin,dc=rigacci,dc=org" write
        by * read

Attenzione: l'intero database è accessibile in lettura anche agli utenti anonimi. Eventualmente aggiungere una clausola by anonymous auth anche alla seconda ACL.

Le voci rootdn e rootpw (sopra commentate) possono essere utilizzate per memorizzare le credenziali di amministratore nel file di configurazione invece che nel database.

Volendo utilizzare LDAP per l'autenticazione è opportuno attivare la cifratura TLS. Bisogna creare un certificato, per semplificare le cose si crea un certificato auto-firmato e quindi faremo a meno di una Certification Authority. Questo è il file di configurazione utilizzato per creare il certificato, nei commenti le istruzioni per utilizzarlo:

#----------------------------------------------------------------
# Create an RSA key and a self-signed Certificate with the
# following command:
#
# openssl req -config /etc/ldap/ssl/ldap.rigacci.org.cnf \
#     -new -x509 -days 1461 -nodes \
#     -keyout /etc/ldap/ssl/ldap.rigacci.org.pem \
#     -out /etc/ldap/ssl/ldap.rigacci.org.pem
#
# The resulting file (unencrypted otherwise Slapd can't start
# automatically) will contains the RSA private key, so be sure
# to set its mode to 0400.
#----------------------------------------------------------------
[ req ]
prompt                          = no
default_bits                    = 2048
distinguished_name              = ldap.rigacci.org_distinguished_name

[ ldap.rigacci.org_distinguished_name ]
countryName                     = IT
stateOrProvinceName             = Italy
localityName                    = Firenze
organizationName                = Rigacci.Org
organizationalUnitName          = Information and Communications Technology
commonName                      = ldap.rigacci.org
emailAddress                    = webmaster@rigacci.org

Il certificato /etc/ldap/ssl/ldap.rigacci.org.pem deve essere protetto con permessi 0400 e deve appartenere all'utente LDAP (openldap:openldap in Debian). Per utilizzarlo si aggiunge a /etc/ldap/slapd.conf le righe:

# Allow the server to picks-up the default cypher.
# TLSCipherSuite         HIGH:MEDIUM:+SSLv2
TLSCertificateFile     /etc/ldap/ssl/ldap.rigacci.org.pem
TLSCertificateKeyFile  /etc/ldap/ssl/ldap.rigacci.org.pem

Conviene limitare il protocollo ldap all'indirizzo loopback e attivare pubblicamente solo ldaps. Questo in Debian Lenny si ottiene modificando /etc/default/slapd:

SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:///"

Se il server ldap è protetto da firewall bisogna aprire la porta 389 TCP e UDP, il protocollo ldpas (LDAP su SSL) invece è sulla porta 636 TCP e UDP.

In generale si dovrà indicare al client di accettare il certificato del server che non è firmato da alcuna Certificate Authority. Dovrebbe essere sufficiente aggiungere a /etc/ldap/ldap.conf la riga:

TLS_REQCERT     allow

Attenzione ai nomi! Se il certificato contiene un commonName diverso dal nome del server che useranno i client, bisogna rilassare ancora di più i vincoli del client, impostando:

TLS_REQCERT     never

Invece di usare ldaps (LDAP su SSL) Si potrebbe usare StartTLS, cioè una connessione cifrata sulla porta standard 389. Sarebbe però necessario configurare ulteriori vincoli (vedi Security Strength Factors) per evitare che i client usino connessioni non cifrate.

Per ottenere un dump del database in formato LDIF, da utente root si può usare il comando slapcat (che agisce direttamente sul database, senza passare per il demone slapd):

slapcat -b "dc=rigacci,dc=org"
dn: dc=rigacci,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
...

dn: cn=admin,dc=rigacci,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
userPassword:: e2MyeXB0hXhlTk95V1pJMu9J8FU=
structuralObjectClass: organizationalRole
...

Da utente non privilegiato si può utilizzare il comando ldapsearch. Nel primo esempio si utilizzano le credenziali di admin fornendo la relativa password (opzioni -D e -W). Nel secondo esempio si utilizza il protocollo ldaps, ma senza utilizzare credenziali privilegiate, pertanto si otterranno solo le informazioni pubbliche.

ldapsearch -x -D "cn=admin,dc=rigacci,dc=org" -W -b "dc=rigacci,dc=org"
ldapsearch -x -H ldaps://127.0.0.1/ -b "dc=rigacci,dc=org" "(objectclass=*)"

Per effettuare il restore di un dump ottenuto con slapcat si può utilizzare slapadd. Anch'esso agisce direttamente sul database, quindi è necessario fermare il demone slapd prima di eseguirlo. Questo è il modo giusto per fare il restore di tutti gli oggetti del database senza alterare gli attributi di sistema come createTimestamp, ecc.

Popolare il database

:!: ATTENZIONE: I nomi utente verranno memorizzati nel ramo LDAP People, i gruppi invece nel ramo Group. Conviene rispettare questo schema nonostante l'inconsistenza tra plurale (People) e singolare (Group) in quanto libnss-ldap usa questo schema per default (forse segue l'RFC2307?).

L'installazione Debian ha provveduto a creare solo la radice del database, ora vogliamo creare alcuni rami che conterranno alcuni oggetti. Ad esempio creiamo il ramo People, definito dal seguente schema ldif (lo salviamo in un file people.rigacci.org.ldif):

dn: ou=People,dc=rigacci,dc=org
ou: People
objectClass: top
objectClass: organizationalUnit
objectClass: domainRelatedObject
associatedDomain: rigacci.org

Lo schema viene aggiunto al database con il comando ldapadd:

ldapadd -x -D "cn=admin,dc=rigacci,dc=org" -W -f people.rigacci.org.ldif
Enter LDAP Password:
adding new entry "ou=People,dc=rigacci,dc=org"

Il database adesso è pronto per ricevere gli utenti. Volendo memorizzare gli account di sistema conviene utilizzare gli schemi posixAccount e shadowAccount. La migrazione dai tradizionali /etc/passwd e /etc/shadow può essere facilitata dagli script Migration Tools. Ecco un estratto di un file LDIF con un account:

dn: uid=niccolo,ou=People,dc=rigacci,dc=org
uid: niccolo
cn: niccolo
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword: {crypt}$1$hr0T2eCW$L.Rk7J9R1oOzJ3qTgv1YB0
shadowLastChange: 13815
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/niccolo
gecos: Niccolo Rigacci,,,

Ecco il comando per aggiungere un record nel database a partire dal file.ldif:

ldapadd -v -H ldap://127.0.0.1/ -x -D "cn=admin,dc=rigacci,dc=org" -W -f file.ldif

Per la modifica invece è richiesto un file più complesso, in cui prima si seleziona il record e quindi si indicano le operazioni da effettuare:

dn: uid=niccolo,ou=People,dc=rigacci,dc=org
changetype: modify
add: objectClass
objectClass: sambaSamAccount
-
replace: userPassword
userPassword: {crypt}$1$hr0T2eCW$L.Rk7J9E3oTvJ6qTgv1YB0

e il comando necessario è ldapmodify:

ldapmodify -v -x -D "cn=admin,dc=rigacci,dc=org" -W -f file.ldif

Attenzione ai caratteri NON ASCII: bisogna codificarli in UTF-8, ad esempio usando iconv(1). Attenzione però anche ai vincoli imposti dallo schema; ad esempio lo schema posixAccount non accetta caratteri non ASCII nel campo gecos e restituisce un errore del tipo:

Invalid Syntax
    additional info: gecos: value #0 invalid per syntax

Per eliminare qualcosa dal database si prepara un file.ldif con i DN che si vogliono eliminare, ad esempio:

uid=niccolo,ou=People,dc=rigacci,dc=org
uid=angela,ou=People,dc=rigacci,dc=org

e poi si utilizza il comando ldapdelete:

ldapdelete -v -x -D "cn=admin,dc=rigacci,dc=org" -W -f file.ldif

Per verificare che l'interrogazione dal client verso un server remoto funzioni si usa ldapsearch (notare l'uso del protocollo LDAP su SSL):

ldapsearch -v -x -H ldaps://ldap.rigacci.org -b "uid=niccolo,ou=People,dc=rigacci,dc=org"

Operazione del tutto analoga per aggiungere il ramo Group, ecco lo schema (lo conserviamo nel file group_rigacci_org.ldif):

dn: ou=Group,dc=rigacci,dc=org
ou: Group
objectClass: top
objectClass: organizationalUnit

Dopo aver aggiunto il ramo, lo si può popolare con entry del tipo:

dn: cn=dialout,ou=Group,dc=rigacci,dc=org
objectClass: posixGroup
objectClass: top
cn: dialout
userPassword: {crypt}x
gidNumber: 20
memberUid: niccolo
memberUid: angela

dn: cn=niccolo,ou=Group,dc=rigacci,dc=org
objectClass: posixGroup
objectClass: top
cn: niccolo
userPassword: {crypt}x
gidNumber: 1000

Indici

Per una maggiore efficienza delle query sul database LDAP è necessario mantenere degli indici sulle chiavi di ricerca più utilizzate. Se vengono fatte richieste su campi non indicizzati (e con loglevel stats) il file /var/log/syslog riporta:

slapd[30935]: <= bdb_equality_candidates: (uid) not indexed

Si aggiungono direttive in /etc/ldap/slapd.conf:

index uid,uidNumber,gidNumber,memberUid  eq

Purtroppo per reindicizzare il database bisogna fermare il demone, eseguire il comando slapindex(8) e riavviare il demone:

/etc/init.d/slapd stop
slapindex -b "dc=rigacci,dc=org"
chown -R openldap:openldap /var/lib/ldap/
/etc/init.d/slapd start

Password aging

Per la scadenza delle password si utilizza il campo shadowMax dello schema shadowAccount, con la stessa semantica del file shadow(5). Se invece di un sistema basato completamente su LDAP si implementa un sistema misto LDAP + Kerberos, la scadenza delle password dovrebbe essere gestita da Kerberos (come?) e quindi il campo shadowMax si imposta convenzionalmente a 99999 per indicare nessuna scadenza (in realtà sono circa 273 anni).

Migration tools

Scompattati i sorgenti, modificato migrate_common.ph impostando quanto segue:

# Default DNS domain
$DEFAULT_MAIL_DOMAIN = "rigacci.org";
# Default base
$DEFAULT_BASE = "dc=rigacci,dc=org";

per esportare gli utenti e i gruppi in un file LDIF è sufficiente eseguire:

./migrate_passwd.pl /etc/passwd > etc_passwd.ldif
./migrate_group.pl  /etc/group  > etc_group.ldif

Conviene ovviamente togliere gli account di sistema dal file LDIF risultante oppure (meglio!) far lavorare il migration tool su una versione emendata di /etc/passwd. NOTA: in Debian gli account normali sono quelli con UID compreso tra 1000 e 29999.

Configurare il client

Sul client che vuole fare autenticazione LDAP si devono configurare due componenti: PAM su LDAP (Pluggable Authentication Modules) e NSS su LDAP (Network Service Switch).

NOTA: Se la gestione delle password avviene tramite Kerberos single sign-on system, non si deve installare la componente PAM su LDAP. In tal caso si può comunque utilizzare NSS su LDAP.

Se si desidera amministrare il database LDAP dal client (non indispensabile) si installa il pacchetto ldap-utils.

PAM subsystem

I pacchetti da installare sono:

Il sistema PAM (Pluggable Authentication Modules) fornisce ai programmi che devono fare autenticazione una serie di servizi tramite una interfaccia di programmazione standard, in questo modo è possibile cambiare il sistema di autenticazione senza dover modificare i programmi.

I servizi offerti dai moduli PAM si dividono in quattro categorie:

account Verifica se l'utente ha il permesso di compiere una determinata azione.
auth Authentication: certifica l'identità dell'utente, in genere verificando una password.
password Funzioni per aggiornare il sistema di authentication (es. modifica password).
session Provvede funzioni per iniziare e chiudere una sessione, ad esempio il mount della home directory.

In generale i servizi di authentication sono i più importanti. Per un modulo PAM non è obbligatorio fornire tutti i servizi; ad esempio alcuni moduli PAM potrebbero non fornire la possibilità di cambiare la password.

La procedura Debian (Squeeze) di configurazione per libpam-ldap richiede diversi parametri, si può ri-eseguire con dpkg-reconfigure libpam-ldap:

LDAP server URI: ldaps://127.0.0.1/
Distinguished name of the search base: dc=rigacci,dc=org
LDAP version to use: 3
Allow LDAP admin account to behave like local root? Yes
Does the LDAP database require login? No
LDAP administrative account: cn=admin,dc=rigacci,dc=org
LDAP administrative password: ****
Local encryption algorithm to use for passwords: crypt
PAM profiles to enable: Unix authentication, LDAP Authentication

Le impostazioni vengono salvate in /etc/pam_ldap.conf e in /etc/pam_ldap.secret.

NOTA: L'help allegato alla domanda Allow LDAP admin account to behave like local root? non è chiaro: rispondendo No il file /etc/pam_ldap.secret contenente la password di amministratore LDAP non viene creato, quindi PAM non sarà in grado di modificare il database con i privilegi di amministratore.

NOTA: Non dovrebbe essere strettamente necessario che l'utente root del client sia amministratore del database LDAP, quindi si dovrebbe poter evitare che la password relativa venga memorizzata sul client.

ATTENZIONE: per evitare che le password possano essere facilmente intercettate è obbligatorio utilizzare il protocollo ldaps. Poiché il server utilizza un certificato auto-firmato è necessario disabilitarne la verifica. Editare il file /etc/pam_ldap.conf e aggiungere le opzioni:

ssl on
tls_checkpeer no

Per testare la componente PAM si può creare il file /etc/pam.d/test_ldap con le seguenti direttive:

auth      required         pam_ldap.so
account   required         pam_ldap.so
password  required         pam_ldap.so

ed utilizzare l'utility pamtester in questo modo:

./pamtester -v test_ldap niccolo authenticate
pamtester: invoking pam_start(test_ldap, niccolo, ...)
pamtester: performing operation - authenticate
Password:
pamtester: successfully authenticated

Contrariamente a quello che viene riportato in alcuni documenti pare che non sia obbligatorio risolvere l'indirizzo IP del server LDAP nel nome definito nel certificato del server stesso.

A questo punto, se le cose sembrano funzionare, si può configurare PAM in modo che utilizzi sempre LDAP, ritornando al tradizionale schema Unix in caso di fallimento. In Debian dovrebbe bastare modificare i file common-account, common-auth e common-password contenuti in /etc/pam.d/

/etc/pam.d/common-account

account sufficient  pam_ldap.so
account required    pam_unix.so try_first_pass

/etc/pam.d/common-auth

auth sufficient      pam_ldap.so
auth required        pam_unix.so nullok_secure try_first_pass

/etc/pam.d/common-password

password sufficient  pam_ldap.so
password required    pam_unix.so nullok obscure md5 try_first_pass

FIXME La sintassi dei dei file sopra non mi convince, sonsultare anche i documenti Setting up OpenLDAP e Setting up MIT Kerberos 5.

Autenticazione PAM per ssh

:!: La configurazione del demone ssh in Debian potrebbe non utilizzare PAM :!:

Per aumentare la sicurezza viene abilitata l'opzione UsePrivilegeSeparation rendendo non privilegiato il processo che riceve la connessione TCP, questo nelle vecchie versioni di OpenSSH creava problemi all'autenticazione PAM. Inoltre per mitigare un bug di sicurezza scoperto in passato si consigliava di impostare PAMAuthenticationViaKbdInt no.

Con OpenSSH 4.4p1 il problema è risolto e si può impostare l'opzione UsePAM yes. Consultare la man page di sshd_config(5) e controllare in /etc/ssh/sshd_config le seguenti opzioni:

UsePrivilegeSeparation yes
PasswordAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes

NSS subsystem

Tradizionalmente sui sistemi Unix alcune informazioni sugli utenti e sugli host sono contenute in file di configurazione (/etc/passwd, /etc/shadow, /etc/hosts, …). Il Name Service Switch (NSS) è un servizio che consente di sostituire questi file con uno o più database. Nel nostro caso vogliamo che le informazioni sugli utenti e sui gruppi siano su un database LDAP.

I pacchetti Debian da installare sono:

La procedura Debian di configurazione per libnss-ldap richiede diversi parametri (eseguire per vedere tutte le domande):

LDAP server Uniform Resource Identifier: ldap://127.0.0.1/
Distinguished name of the search base: dc=rigacci,dc=org
LDAP version to use: 3
Does the LDAP database require login? No
Special LDAP privileges for root? No
Make the configuration file readable/writeable by its owner only? Yes

Le impostazioni vengono salvate in /etc/libnss-ldap.conf. Attivare i privilegi speciali LDAP per root vorrebbe dire che l'utente root del client ha i privilegi di amministratore su LDAP, questo però richiederebbe di memorizzare in chiaro la password di amministratore LDAP nel file /etc/libnss-ldap.secret.

:!: NOTA: Forse è opportuno rispondere No all'ultima domanda, visto che anche gli utenti non privilegiati devono poter accedere (in modo anonimo) al database LDAP. Vedi avanti.

:!: ATTENZIONE: Se in /etc/libnss-ldap.conf viene impostata l'opzione binddn, allora il collegamento al server LDAP avviene con quel nome invece che in modo anonimo. Nel nostro caso si preferisce che l'utente non privilegiato faccia collegamenti anonimi, pertanto l'opzione viene omessa. Se l'utente è root allora l'accesso LDAP avviene con le credenziali specificate in rootbinddn e la password contenuta in /etc/libnss-ldap.secret.

Anche in questo caso è necessario disabilitare la verifica del certificato dal momento che non è firmato da alcuna Certificate Authority. Forziamo anche l'uso di SSL che altrimenti fallisce nonostante la scelta del protocollo ldaps. Quindi in /etc/libnss-ldap.conf si imposta:

ssl on
tls_checkpeer no

L'opzione tls_checkpeer potrebbe essere impostata a livello di libldap nel file /etc/openldap/ldap.conf, come dice il commento in libnss-ldap.conf.

:!: ATTENZIONE: Debian imposta i permessi di /etc/libnss-ldap.conf a 0600 perché in questo file potrebbero trovarsi le credenziali per accedere al database LDAP. Non è il caso nostro, anzi le interrogazioni per i database passwd e group vengono fatte in modo anonimo e devono essere eseguibili dall'utente non privilegiato (ad es. con getent(1)). Per ottenere questo risultato bisogna fare:

chmod 0644 /etc/libnss-ldap.conf

Nel caso serva memorizzare la password per accedere al database LDAP si utilizza il file /etc/libnss-ldap.secret (contiene solo la passwrod, senza il newline!) e si protegge il file con chmod 0600.

Rimane infine da configurare il file /etc/nsswitch.conf, in questo esempio la ricerca su LDAP viene fatta solo se fallisce la ricerca sui tradizionali file:

passwd:         files ldap
group:          files ldap
shadow:         files ldap

In Debian al posto della ricerca files si trova quella compat (che sta per NIS compatibility), questa modalità consente di utilizzare una speciale sintassi nel file /etc/passwd per includere gruppi di utenti.

:!: ATTENZIONE: la modifica del file /etc/nsswitch.conf non ha effetto immediato su tutti i programmi in esecuzione, ogni programma infatti legge la configurazione del NSS solo al suo avvio. Se si cambia il file di configurazione ad esempio si deve riavviare il demone nscd, utile anche il comando per invalidare la cache di nscd:

/etc/init.d/nscd reload
nscd --invalidate passwd 

Per testare il funzionamento del NSS si può utilizzare il comando getent(1) che cerca nei database (passwd, group, …) una specifica chiave:

getent passwd niccolo
niccolo:x:1002:1002:Niccolo Rigacci,,,:/home/niccolo:/bin/bash

L'installazione di nscd è praticamente obbligatoria se il server LDAP è su una macchina diversa da localhost. Infatti anche la più semplice operazione (ad esempio un ls -l) provoca numerose interrogazioni del NSS, ripetere le interrogazione su un database remoto risulterebbe in un rallentamento non accettabile.

Volendo una copia locale dei database passwd e group da usare quando disconnessi si può usare il pacchetto libnss-db e un cron-job che replica le informazioni dalla sorgente LDAP (pacchetto nss-updatedb).

Autenticazione Kerberos e LDAP

Kerberos si propone come sistema di Single Sign-On (SSO), cioè con una sola autenticazione è possibile accedere a molti servizi. Il server di autenticazione Kerberos mantiene solo le informazioni su nome/password (chiamate principals) e poco altro (scadenza, ecc.), non contiene informazioni su UID, GID, ecc.

Al momento del login il server Kerberos autentica l'utente e rialscia un Ticket-granting Ticket (TGT), cioè un ticket che verrà utilizzato in seguito per ottenere i ticket specifici per accedere ai servizi, senza dover ripetere la procedura di autenticazione. Con il comando klist(1) è possibile sapere quali ticket abbiamo ricevuto, con kinit(1) è possibile ottenere un nuovo TGT.

Grazie ad un robusto progetto di sicurezza, Kerberos non trasmette mail le password in rete.

L'accoppiata PAM + Kerberos può sostituire utilmente PAM + LDAP, migliorando la procedura di autenticazione. Ad esempio sarà possibile effettuare login su un host e montare una directory NFSv4 in modo sicuro, effettuando una sola autenticazione.

Configurando PAM opportunamente, non solo l'autenticazione del login verrà fatta sul server Kerberos, ma verrà anche rilasciato automaticamente il Ticket-granting Ticket, senza bisogno di eseguire kinit e digitare nuovamente la password. Questo è il motivo per cui è importante configurare il modulo session di PAM.

Il resto delle informazioni utente (UID, GID, home directory, shell, …) non sono memorizzati nel database Kerberos, pertanto staranno nei tradizionali file Unix oppure su un server LDAP e gestiti dal Name Service Switch (NSS). Tradizionalmente la shadow password viene impostata a *K*, per significare che è nel database Kerberos.

Qui abbiamo una guida all'installazione di Kerberos per NFSv4 (utile per la configurazione del server KDC), esiste inoltre questa guida all'installazione di Kerberos per l'autenticazione: MIT Kerberos installation on Debian.

Sull'host che deve usufruire di questa autenticazione si installa il modulo PAM che si appoggia al server Kerberos e le utility di supporto Kerberos. In pratica servono i pacchetti Debian:

L'installazione di krb5-config genera il file di configurazione /etc/krb5.conf che contiene il realm di appartenenza e il nome DNS del server KDC. Eventualmente eseguire dpkg-reconfigure krb5-config per specificare nuovamente i parametri, ad esempio:

Default Kerberos version 5 realm: RIGACCI.ORG
Does DNS contain pointers to your realm's Kerberos Servers? No
Kerberos servers for your realm: kdc.soluzioni.per
Administrative server for your Kerberos realm: kdc.soluzioni.per

Nel database Kerberos deve essere presente solo il principal (account) dell'utente. Non è necessario creare un principal per l'host su cui si effettua il login, né tantomeno sull'host è necessario creare una chiave nel keytab file.

Per verificare che l'autenticazione Kerberos funzioni un utente qualunque può utilizzare kinit, verificare il risultato con klist e rimuovere il ticket con kdestroy:

# kinit angela@RIGACCI.ORG
Password for angela@RIGACCI.ORG: 
# klist -5 
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: angela@RIGACCI.ORG
Valid starting     Expires            Service principal
01/07/08 22:36:23  01/08/08 08:36:23  krbtgt/RIGACCI.ORG@RIGACCI.ORG
        renew until 01/08/08 22:36:19
# kdestroy

Per fare in modo che tutti i programmi PAM utilizzino Kerberos si modificano alcuni file in /etc/pam.d/, aggiungendo al tradizionale pam_unix.so il modulo pam_krb5.so.

ATTENZIONE: Con Debian Squeeze queste aggiunte vengono effettuate automaticamente con l'installazione del pacchetto libpam-krb5, grazie al comando pam-auth-update. Le regole sono in realtà molto più elaborate ed efficaci.

common-auth:       auth      sufficient  pam_krb5.so minimum_uid=1000
                   auth      required    pam_unix.so try_first_pass nullok_secure

common-session:    session   optional    pam_krb5.so minimum_uid=1000
                   session   required    pam_unix.so

common-account:    account   required    pam_krb5.so minimum_uid=1000
                   account   required    pam_unix.so

common-password:   password  sufficient  pam_krb5.so minimum_uid=1000
                   password  required    pam_unix.so nullok obscure md5

FIXME La sintassi dei dei file sopra non mi convince, sonsultare anche i documenti Setting up OpenLDAP e Setting up MIT Kerberos 5.

File temporanei krb5cc_pam in /tmp/

Pare che il modulo pam_krb5 in alcune circostanze non cancelli il file temporaneo con il ticket rilasciato da Kerberos. Tali file si accumulano indefinitamente in /tmp con nomi del tipo krb5cc_pam_*, tale situazione si verifica ad esempio con il courier authdaemon per ogni login POP3.

Forse è possibile configurare meglio il modulo pam, nel dubbio si attiva un cronjob che cancella i ticket più vecchi di qualche ora (non ha senso tenere i ticket oltre le 10 ore, vedere qual'è il max_life dei ticket):

#!/bin/sh
# Remove temporary Kerberos ticket cache left by PAM subsystem.
find /tmp -name "krb5cc_pam_*" -mmin +240 | xargs -r rm

Rubrica contatti su LDAP

La distribuzione di riferimento è una Debian Sarge. Si installano i seguenti pacchetti:

apt-get install slapd ldap-utils

Durante l'installazione viene chiesto come costruire il DN (Distinguished Name) dell'archivio LDAP, si suggerisce di usare il nome del dominio DNS. Ad esempio con il dominio rigacci.org, la radice dell'albero LDAP diventa DN dc=rigacci, dc=org. Ogni oggetto dovrà essere figlio di questo.

La porta da aprire sul firewall è la 389 TCP e UDP.

Pacchetti Debian Sarge

Il pacchetto Debian Sarge propone di supportare solo la v.3 del protocollo.

Configurazione

/etc/ldap/slapd.conf

Dentro il file di configurazione del server LDAP ci sono alcune opzioni globali (già presenti nel file di configurazione), si aggiunge la definizione del database da gestire (oppure la si include da un file esterno). Una struttura di database d'esempio è la seguente:

# See man slapd.conf
# Log connections/operations/results
loglevel        256
database        dbm
suffix          "o=ipbox"
rootdn          "cn=LdapAdmin,o=ipbox"
#rootpw         {SSHA}lxy7HF6eP7fRaA3G5L85zhk6zWZP8+Fk
rootpw          soloperprova
schemacheck     on
lastmod         on
directory       /var/lib/ldap/ipbox
index           cn,sn  pres,eq,sub

access to attrs=userPassword
        by dn="cn=LdapAdmin,o=ipbox" write
        by anonymous auth
        by self write
        by * none

access to *
        by dn="cn=LdapAdmin,o=ipbox" write
        by * read

Come si vede nel file di configurazione viene indicato quali indici mantenere nel database ed acluni criteri di accesso. Nell'esempio sopra ciascun utente può cambiare la propria password, l'utente cn=LdapAdmin,o=ipbox ha accesso completo in scrittura, tutti gli altri in sola lettura. L'utente privilegiato e la sua password è definito direttamente nel file di configurazione, quindi non è necessario inserirlo nel database vero e proprio.

Dopo aver modificato il file di configurazione si esegue /etc/init.d/slapd restart, il log viene scritto in /var/log/syslog.

Importare/esportare dati da file .ldif

NOTA: Prima di poter importare un oggetto nel database LDAP bisogna aver creato tutti gli oggetti parent, a partire dalla radice (ricordarsi che LDAP gestisce un database ad albero gerarchico). Ad esempio prima di aggiungere un distinguished name uid=jdoe,ou=people,dc=example,dc=com, bisogna che siano stati aggiunti gli oggetti dc=example,dc=com e ou=people,dc=example,dc=com.

In questo esempio si crea l'oggetto o=ipbox prima di poter aggiungere le persone del tipo cn=Mario,o=ipbox. Questo si può fare con un primo file .ldif (vedere più avanti com caricare un file .ldif):

dn: o=ipbox
objectClass: top
objectClass: organization
o: ipbox

Si possono importare dei dati nel database a partire da un file .ldif, ecco ad esempio rubrica.ldif con un record:

dn: cn=Mario Rossi,o=ipbox
objectClass: top
objectClass: inetOrgPerson
cn: Mario Rossi
displayName: Mario Rossi
givenName: Mario
sn: Rossi
uid: oqY6hwrjK3
mail: mario.rossi@texnet.it

Si importa il file nel database con comandi del tipo:

ldapadd -h 127.0.0.1         -D "cn=LdapAdmin,o=ipbox" -W -xv -f rubrica.ldif
ldapadd -H ldap://127.0.0.1/ -D "cn=LdapAdmin,o=ipbox" -W -xv -f rubrica.ldif

Per esportare il contenuto del database in un file .ldif invece si usa il comando:

slapcat -b "o=ipbox"

Struttura dati

Per creare una rubrica si consiglia di seguire uno dei due schemi più diffusi: inetOrgPerson oppure mozillaOrgPerson. Il secondo è più ricco di attributi, ma non è compreso nella pacchettizzazione standard Debian. Si scarica il file .schema da Internet, lo si salva in /etc/ldap/schema/, si aggiunge a /etc/ldap/slapd.conf una riga:

include /etc/ldap/schema/mozillaorgperson.schema

Pare che Outlook e Outlook Express si trovino a proprio agio con entrambe le strutture dati, altrettanto per Kmail.

Accesso ai dati

Come client possiamo utilizzare KAddressBook (la rubrica di KDE), oppure LDAP Explorer (un applicativo in PHP basato sul web, puntare il browser su http://ldapserver/ldapexplorer/). I parametri necessari alla connessione in lettura/scrittura sono i seguenti:

Server LDAP 10.0.1.111
Porta TCP 389
Base DN o=ipbox
Query type One level (not recursive)
User (Bind DN as) cn=LdapAdmin,o=ipbox
Password soloperprova

Per un accesso in sola consultazione si immettono gli stessi dati, omettendo User e Password.

phpLDAPadmin

Si installa il pacchetto Debian phpldapadmin. Il file di configurazione /etc/phpldapadmin/config.php contiene il nome derl server LDAP, la porta, il nome con cui collegarsi. Se la connessione viene fatta alla porta 636 ldaps bisogna non attivare l'opzione tls, altrimenti si ottiene l'errore:

slapd[29352]: conn=40 op=0 STARTTLS
slapd[29352]: conn=40 op=0 RESULT oid= err=1 text=TLS already started

Per accedere all'interfaccia web di amministrazione si punta il browser su http://ldap.mydomain.org/phpldapadmin/, usando le opportune credenziali.