ldap

Ldap è sempre stato ai miei occhi un argomento piuttosto ostico. I motivi sono delle enormi lacune di base e una mia difficoltà nel capirne con precisione l'utilità.Esistono online un sacco di guide rapide per la configurazione di server/client migrazioni etc.. Ne riporto qui un po' che ho utilizzato nel corso degli anni per fare esperienza
http://www.6tech.org/2013/01/ldap-server-and-centos-6-3/
http://ducky-pond.com/posts/15 e http://ducky-pond.com/posts/16
http://docs.adaptivecomputing.com/viewpoint/hpc/Content/topics/1-setup/installSetup/settingUpOpenLDAPOnCentos6.htm

 Visto che le suddette guide (e immagino anche le altre mille che ho trovato) sono sicuramente meglio scritte e più chiare di qualunque cosa io possa produrre sarò estremamente rapido nel parlare della configurazione, che tratterò quindi alla fine.

 Parlerò esclusivamente di openldap e del nuovo backend di configurazione chiamato slapd-config. Non dirò nulla sul vecchio sistema di configurazione chiamato slapd.conf (che è poi il nome del file dove si trova(va) la configurazione del server).

 OBIETTIVI:

 Per prima cosa ho bisogno di pormi un obiettivo per fare esperienza nella configurazione.
L'idea è quella di installare un server openldap, creare 2 alberi di directory (uno che userò per l'autenticazione, l'altro per memorizzare i dati effettivi di cui ho bisogno), connettermi in maniera sicura con un client (che può essere un sw tipo jxplorer , i comandi di ldap-common, uno script php etc) e interrogare il mio db.
Uno dei grossi pregi di di ldap, per quanto sono riuscito a capire, sta proprio nella moltitudine di programmi esterni che ci si possono interfacciare.

PREMESSE:

Ora faccio un numero spropositato di premesse, che mi servono proprio a cercare di colmare in qualche modo le lacune che ho sull'argomento.

1p) slap-config
è il nuovo backend di configurazione per un server ldap (openldap). Al posto di avere un solo file di configurazione (slapd.conf) con una sintassi sua, avremo una serie di file di configurazione situati in /etc/openldap/slapd.d con una sintassi tipica dei database ldap: Quindi ti troverai una serie di ldif organizzati in una struttura gerarchica. Questi file NON vanno editati a mano ma servendosi di alcuni tool di configurazione. Per comodità e per il fatto di non aver ancora ben capito come si usano questi tool ho sempre editato tramite vi i file di configurazione  con l'accortezza di farlo a server ldap fermo.

 Questo il contenuto della directory slapd.d su un sistema centos appena installato (o quasi)

 slapd.d/
├── cn=config
│   ├── cn=schema
│   │   ├── cn={0}corba.ldif
│   │   ├── cn={10}ppolicy.ldif
│   │   ├── cn={11}collective.ldif
│   │   ├── cn={1}core.ldif
│   │   ├── cn={2}cosine.ldif
│   │   ├── cn={3}duaconf.ldif
│   │   ├── cn={4}dyngroup.ldif
│   │   ├── cn={5}inetorgperson.ldif
│   │   ├── cn={6}java.ldif
│   │   ├── cn={7}misc.ldif
│   │   ├── cn={8}nis.ldif
│   │   └── cn={9}openldap.ldif
│   ├── cn=schema.ldif
│   ├── olcDatabase={0}config.ldif
│   ├── olcDatabase={-1}frontend.ldif
│   ├── olcDatabase={1}monitor.ldif
│   ├── olcDatabase={2}bdb.ldif
│   └── olcDatabase={3}bdb.ldif
└── cn=config.ldif

Questo l'albero che mi ha creato un ubuntu
slapd.d/
├── cn=config
│   ├── cn=module{0}.ldif
│   ├── cn=schema
│   │   ├── cn={0}core.ldif
│   │   ├── cn={1}cosine.ldif
│   │   ├── cn={2}nis.ldif
│   │   ├── cn={3}inetorgperson.ldif
│   │   └── cn={4}samba.ldif
│   ├── cn=schema.ldif
│   ├── olcBackend={0}hdb.ldif
│   ├── olcDatabase={0}config.ldif
│   ├── olcDatabase={-1}frontend.ldif
│   ├── olcDatabase={1}hdb.ldif
│   └── olcDatabase={2}hdb.ldif
└── cn=config.ldif

La documentazione ufficiale spiega pittosto bene il tutto.
http://www.openldap.org/doc/admin24/slapdconf2.html
Mi limito quindi ad alcune considerazioni.

I file su cui si agisce primariamente sono quelli che terminano in hdb (o bdb). In questi file (olcDatabase={1}hdb.ldif) trovi le configurazioni del db ldap che vuoi andare a popolare coi tui dati (utenti,password,permessi,certificati etc..) e, ciascuna directory che andrai a creare, avrà quindi un suo file di configurazione che si applica esclusivamente ad esso.

Ci sono poi file di configurazione come config e frontend che vengono sempre creati implicitamente e che contengono direttive che si applicano a tutti gli altri database.

Il file backend invece contiene direttive che si applicano a tutti i database di quel tipo, per esempio olcBackend={0}hdb.ldif conterrà direttive comuni a tutti i database hdb, direttive che possono venir sovrascritte poi nei file di configurazione dei singoli db.

Se devi importare un vecchio file di configurazione slapd.conf nel nuovo formato basta dare il comando:
slaptest -f /etc/openldap/slapd.conf -F /etc/openldap/slapd.d/
Non è sempre scontato che funzioni tutto così facilmente.

2p)Dove vengono poi fisicamente memorizzati i database ldap?

In ciascun file di configurazione olcDatabase={1}hdb.ldif trovi una direttiva chiamata olcDirectory: che indica la cartella (di solito di base /var/lib/ldap) che conterrà tutti i file del database. Questa cartella deve poter essere letta dall'utente col quale gira il server ldap (quindi tipicamente ldap.ldap,oppure openldap.openldap) e, al suo interno, se il db è di tipo hdb o bdb, puoi (anzi devi) mettere un file chiamato DB_CONFIG che contiene informazioni circa le ottimizzazioni per l'accesso, dimensioni della cache,  etc... Puoi copiarti questo file dall'esempio che trovi in
cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
(questo è da approfondire).

3p)Basi di ldap
Configurando ldap e inserendo dei dati mi sono accorto di non aver capito molto della struttura organizzativa di ldap...Mi aiuto quindi scrivendo proprio 2 cavolate di base e cercando di fare paragoni con un db relazionale tipo mysql.

Ogni record che vado a inserire in una directory ldap ha degli attributi e posso pensare a questi come le colonne di un db mysql.

Caratteristiche come il numero di attributi,il tipo di ciascun attributo, il fatto che siano obbligatori o meno (insomma le operazioni di data definition language) in ldap vengono definite dalle objectclass che sono anch'esse degli attributi (speciali).

Un insieme delle definizioni di attributi e classi prende il nome di schema,che, come avviene per le classi, può fare riferimento anche ad attributi e classi definiti in altri schemi.

Questo un esempio di definizione di attributi in uno schema.

attributetype ( 2.5.4.3 NAME („cn‟ „commonName‟ ) DESC „RFC2256: common name(s) for which the entry is known by‟ SUP name )

attributetype ( 2.5.4.4 NAME („sn‟ „surname‟ ) DESC „RFC2256: last name(s) for which the entry is known by‟ SUP name )

attributetype ( 2.5.4.35 NAME („userPassword‟ )DESC „RFC2256/2307: password of user‟
EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )

Questo un esempio di definizione di objectclass in uno schema

Objectclass ( 2.5.6.6 NAME „person‟
DESC „RFC2256: a person‟
SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber ) )

Quindi, in sostanza, una entry o record del db sarà una cosa del tipo

dn: cn=angelino,ou=access,dc=merli,dc=lan
cn: angelino
objectclass: organizationalRole
objectclass: top
objectclass: simpleSecurityObject
userpassword: {SSHA}la_password

Questa entry ha una sua posizione all'interno del ramo, ha 2 objectclass che ne definiscono gli attributi obbligatori e facoltativi e 2 attributi (gli unici obbligatori).

Classe TOP
Essendo a sua volta objectClass un particolare attributo, il nome o i nomi delle classi che si desidera utilizzare per ogni entry devono essere specificati all'interno delle entry medesima tramite una apposita classe chiamata top che prevede un unico attributo obbligatorio (objectClass, appunto) e nessuno
facoltativo.
Le classi "top" e "alias" sono la root della gerarchia.

Esempi di sottoclassi
L'object class "organizationalPerson" è una sottoclasse della object class "Person"
che a sua volta è sottoclasse della "top"  Quando vai a creare una nuova entry ldap devi sempre specificare tutte le objectclass a cui una nuova entry appartiene.
Alcuni servizi di directory non supportano il sublclassing delle object class per cui devi sempre specificare anche tutte le superclassi della entry (non necessariamente nell'ordine così da incasinare un po')
objectclass: top
objectclass: person
objectclass: organizationalperson
objectclass: inetorgperson

Chiaramente su questo argomento trovi di tutto online, ho scritto solo alcune cose di base per rinfrescarmi la memoria e non incazzarmi quando non riesco a fare un ldapadd...

4p)Protocolli porte e demoni
http://www.openldap.org/doc/admin24/runningslapd.html

Questi i nomi dei protocolli e le porte
URL           Protocol                  Transport
ldap:///      LDAP                       TCP port 389
ldaps:///    LDAP over SSL     TCP port 636
ldapi:///    LDAP                       IPC (Unix-domain socket)

Se in fase di connessione da un client dai ldaps://nome_host:636 fa già partire la connessione tls e non c'è quindi bisogno di rispecificare lo starttls. Puoi anche specificare ldap://nome_host e poi scrivere start_tls-> In questo modo si collegherà alla port 389 ma utilizzerà il layer di sicurezza ssl/tls.

Se lanci il demone specificando -h puoi cambiare la porta di default su cui sta in ascolto e l'interfaccia su cui si binda.
Un tipico comando da utilizzare in fase di debug per far partire il demone senza utilizzare gli scritp della distribuzione può essere

slapd -h ldap:/// -d1 -> Tutti i log
oppure
slapd -h ldap:///-d256 -> Mostra tutti i bind effettuati e i comandi che vengono letti dal server
slapd -h ldap:/// -d128 -> Mostra tutte le regole di autenticazione che vengono applicate

se specifichi -d? ti mostra tutte le modaità di debug che si mette in ascolta su tutte le interfacce sulla porta di default non ssl e con debug.

5p)Authentication

Questo un argomento che mi ha portato via un sacco di tempo per riuscire a capire

Intuitivamente un client si collega a un server ldap tramite il protocollo (ldap); una volta stabilita la connessione ci sarà il problema dell'autenticazione/accreditamento delle credenziali per poter operare sul db: Questa seconda fase è il binding

La grande confusione che ho sempre fatto è dovuta al motivo che, a parte il super utente (Manager o Admin), gli utenti con cui ti colleghi sono definiti all'interno del db stesso. Non è una cosa nuova o sconvolgente in quanto pure mysql funziona così col db “mysql” che contiene tutte le credenziali.

Il problema è quindi che quindi il client deve poter leggere il db dove ci sono gli utenti per autenticarsi, quando ancora non si è autenticato.

Questo è un ottimo articolo che parla di questo argomento:
http://thecarlhall.wordpress.com/2011/01/04/ldap-authentication-authorization-dissected-and-digested/

Per come la vedo io
1 – Il server guarda nella richiesta LDAP_bind e cerca queste 3 informazioni

  • Il dn che il client gli ha passato (se lo ha passato)
  • Il metodo di autenticazione usata (se Simple o SALS e in questo ultimo caso vorrà anche sapere che metodo sals usare).
  • Le credenziali (la password per esempio) incluse nella richiesta

2 – Se il dn è vuoto, o se non vengono specificate delle credenziali il server assumerà che il client voglia bindarsi anonimamente.

3- Se il dn contiene informazioni e ci sono anche delle credenziali allora il server, tramite una utenza “di servizio”, cercherà nell'area del db ldap (il dn) dove pensa di trovare le credenziali passategli

4- Se il controllo delle credenziali andrà a buon fine allora l'utente verrà bindato con qulle credenziali (e i permessi ad esse associati).

Il punto 3 dell'utenza di servizio è cruciale. In questa fase l'utente non è autenticato, non è anonimo perchè ha fornito delle credenziali, ma ha gli stessi permessi di un utente anonimo . Quindi (e questo sarà utile dopo) l'elenco degli utenti con cui bindarsi al server deve :

- o essere leggibile da tutti anche senza fornire credenziali
- o essere leggibile dagli anonimi se forniscono però credenziali. (quindi, sono anonimi in un tempo t0, quindi in una prima fase di bind, ma poi forniscono credenziali al tempo t1)

Esempio di bind con debug
conn=1000 fd=15 ACCEPT from IP=127.0.0.1:45652 (IP=0.0.0.0:389)
conn=1000 op=0 BIND dn="cn=utente1,dc=access,dc=net" method=128
conn=1000 op=0 BIND dn="cn=utente1,dc=access,dc=net" mech=SIMPLE ssf=0
conn=1000 op=0 RESULT tag=97 err=0 text=
conn=1000 op=1 SRCH base="ou=People,dc=rete,dc=net" scope=2 deref=0 filter="(uid=merli)"
conn=1000 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text=
conn=1000 op=2 UNBIND
conn=1000 fd=15 closed

Esempio di bind Anonimo con debug
conn=1001 fd=15 ACCEPT from IP=127.0.0.1:45653 (IP=0.0.0.0:389)
conn=1001 op=0 BIND dn="" method=128
conn=1001 op=0 RESULT tag=97 err=0 text=
conn=1001 op=1 SRCH base="dc=access,dc=net" scope=2 deref=0 filter="(objectClass=*)"
conn=1001 op=1 SEARCH RESULT tag=101 err=0 nentries=3 text=
conn=1001 op=2 UNBIND
conn=1001 fd=15 closed

Casi
In quasi tutti i programmi esterni che utilizzano come backend ldap per avere un'anagrafica degli utenti e memorizzarne le password, all'interno della configurazione del programma si specifica un utente per effettuare il binding col db ldap (ho provato squid, samba, pam_sssd / nss_pam, script php vari, radius etc)

5p1) Metodi di autentica

Ci sono 2 metodi:

1- Simple authentication: Il client manda un DN e una password al server. Il server compara la password mandata dal client con l'entry memorizzata nella directory. Fine della storia.
2- SASL – Fornisce un più ampio numero di meccanismi di autenticazione, alcuni dei quali sono simili alla simple authentication (tipo PLAIN e LOGIN), altri invece sono più complessi e sicuri

Questo comando mostra quali metodi sasl sono supportati dal server
ldapsearch -x -H "ldap://" -s base -b "" supportedSASLMechanisms

5p2) Password

Le password di default sono memorizzate nell'attributo userPassword. I metodi di codifica di default sono
CLEAR -> Nessuna crittazione
CRYPT ->Una l'algoritmo di crittazione di Unix
SHA, SHA256, SHA384, SHA512 -> Usa il Secure Hashing Algorithm
SSHA, SSHA256, SSHA384, SSHA512 -> Usa il Salted Secure Hashing Algorithm
Spesso ho letto che si suggerisce di utilizzare SSHA.
SHA e SSHA usano lo stesso schema sha1 per fare l'hash ma ssha usa un “random salt” (che tradurrei stringa variabile): Questa stringa viene in qualche modo concatenata all'hash della password e quindi la stessa password avrà sempre un hash ssha diverso.

6p)Access control
http://www.openldap.org/doc/admin24/access-control.html
Mi assicuro di aver ben capito questo paragrafo in modo da non impazzire nella configurazione del controllo degli accessi.

In particolare
none = nessun tipo di accesso
auth = permette all'utente di autenticarsi
search = permette all'utente di cercare (ma non di vedere i risultati)
read = permette all'utente di vedere i risultati

7p) Tipi di database
BDB ->Berkley DB -> Transactional Backend
HDB -> Variazione gerarchica del bdb
Ce ne sono poi altri (tipo mdb). Online si trovano diverse informazioni e test di velocità.

CONFIGURAZIONE

Su un sistema Centos/Fedora inizio l'installazione

server -> Server
clients -> Strumenti client tipo ldapadd ldapsearch ldapmodify
openldap- > Client
migrationtools -> Simpatici tool per migrare le utenze locali di linux su un db ldap

Una volta portata a termine l'installazione c'è da configurare il tutto a mano. Su ubuntu c'è un comodo tool che permette di velocizzare un po' le cose rispondendo a una serie di domande. Per lanciarlo basta scrivere sudo dpkg-reconfigure slapd.

Mi copio un esempio di DB_CONFIG nella directory di default in cui è andato a installare il db ldap.

In questo momento /var/lib/ldap è una directory vuota che conterrà poi il db di default del nostro server ldap. Visto che voglio 2 db mi copio la directory e assegno i giusti permessi

Volendo 2 db mi copio la configurazione di default del primo db su un secondo file di configurazione

Creo la password che utilizzerò nel file di configurazione del primo db

Vado a editare il file di configurazione del primo db, quello principale che conterrà la mia directory.

Configuro il db di monitoraggio, solo per il db principale.

Creo il certificato e la chiava privata per utilizzare tls

Vado nella configurazione del primo db e gli indico il certificato e la chiave appena creati.

#Queste direttive le metto in fondo

Configuro ldap per mettersi in ascolto anche utilizzando ssl.

in ubuntu questo file è in /etc/defaults/slapd
Ovviamente se vuoi metterlo in ascolto solo sulla 636 con ssl devi scrivere anche SLAPD_LDAP = no

Creo una password per il secondo db, quello di accesso.

Configuro il db di accesso

Configuro il client, che è sempre la stessa macchina: Quindi mi copio il certificato dove sarà leggibile dal client e dico al client di non fare troppo il sofisticato col certificato e, per esempio, di non incazzarsi se il nome dell'host non corrisponde al nome dell'host del certificato

TEST di base e POPOLAMENTO del db
Prima prova con l'utente amministratore sul db di accesso

Enter LDAP Password: password_access
# extended LDIF
#
# LDAPv3
# base <> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1

Prova con l'utente amministratore sul db principale

Enter LDAP Password: password
# extended LDIF
#
# LDAPv3
# base <> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1

Ora sono entrambi vuoti

Poi creo la root dell'albero per il mio db principale con un ldif di questo tipo

e lo addo

Poi lo popolo con dei dati facili facili migrando le utenze del server linux col migrationtools.
Edito il file migrate_common.ph per specificargli i dati di base del mio db principale

Creo i file ldif per popolare il db

Gli tolgo la root del db che ho gia creato genero gruppi e password, e li addo al mio db

Poi creo la root del db access

e lo popolo con l'elenco degli utenti che faranno accesso al db che quindi avranno bisogno di una password

Creio il file ldif coi 2 utenti e le password appena create

Ora guardo con jxplorer il risultato di tutto se mi può andar bene .

CONFIGURAZIONE-PERMESSI
Ora sistemo i permessi
- Voglio che solo gli utenti autenticati leggano db principale e che non sia possibile utilizzare gli utenti locali di questo db per autenticarsi.
- Voglio invece un Bind anonimo sul db_access senza leggere le password degli utenti che si trovano
- Voglio che dopo essersi autenticati sul db  access si possa leggere solo la propria password ma non l'hash di quella degli altri.

Sul db principale

users -> Sono gli utenti autenticati

Sul db di accesso, accesso anonimo ma lettura del parametro password solo per l'utente stesso

TEST finali
Così facendo mi autentico con utente1 sul db di accesso e poi leggo il db principale

Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <ou=People,dc=rete,dc=net> with scope subtree
# filter: uid=merli
# requesting: ALL
#

# merli, People, rete.net
dn: uid=merli,ou=People,dc=rete,dc=net
uid: merli
cn: Gabriele Merli
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword:: la_password
shadowLastChange: 15173
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 501
gidNumber: 501
homeDirectory: /home/merli
gecos: Gabriele Merli

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

La lettura anonima del db principale non funziona, come previsto

# extended LDIF
#
# LDAPv3
# base <ou=People,dc=rete,dc=net> with scope subtree
# filter: uid=merli
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1

La lettura anonima del db di accesso si ma senza vedere le password

# extended LDIF
#
# LDAPv3
# base <dc=access,dc=net> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# access.net
dn: dc=access,dc=net
dc: access
o: Root del db access
objectClass: top
objectClass: dcObject
objectClass: organization

# utente1, access.net
dn: cn=utente1,dc=access,dc=net
cn: utente1
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole

# utente2, access.net
dn: cn=utente2,dc=access,dc=net
cn: utente2
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole

# search result
search: 2
result: 0 Success

# numResponses: 4
# numEntries: 3

La lettura da bindato del db di accesso funziona vedendo solo le password dell'utente che fa la richiesta

Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=access,dc=net> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# access.net
dn: dc=access,dc=net
dc: access
o: Root del db access
objectClass: top
objectClass: dcObject
objectClass: organization

# utente1, access.net
dn: cn=utente1,dc=access,dc=net
cn: utente1
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole
userPassword:: e1NTSEF9VnFRbmZsTDROWHJBbnRXU0pNOXpYWFQ1WmJEWkJsMk8=

# utente2, access.net
dn: cn=utente2,dc=access,dc=net
cn: utente2
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole

# search result
search: 2
result: 0 Success

# numResponses: 4
# numEntries: 3

POSSIBILI VARIAZIONI
Mettendo invece così sul db di accesso

Otterremmo non più il bind anonimo su questo ma forziamo gli anonimi a loggarsi per poterlo leggere

Sul db principale invece potrei mettere una cosa del genere

In questo modo, se nel db inserisci dei record che possono essere usati come credenziali (ovvero che hanno come attributo userPassword) l'utente potrà bindarsi utilizzando questi record.

La confusione che si viene a creare sta proprio nel fatto che spesso in ldap memorizziamo un databse di anagrafiche con password, permessi etc (utenti di samba, utenti linux, utenti per qualsiasi servizio etc). In questi casi ci serviamo di “credenziali esterne” per accedere a ldap e leggere/scrivere il db (binding) , poi dobbiamo decidere come fare a valutare l'anagrafica (e le password) lette nel db.

Se ci affidiamo a un programma esterno allora va bene la mia configurazione originale: Nella configurazione di questo programma esterno metteremo le credenziali utilizzate per bindarsi con ldap (un utente del db di accesso nel mio caso). Il programma esterno poi leggerà il db principale, troverà ciò che cerca (unteti/gruppi/password/autorizzazioni etc) e si comporterà di conseguenza garantendo l'autenticazione coi suoi metodi.

Se invece ci affidiamo a ldap stesso allora bisogna utilizzare queste ultime acl (possibili variazioni) perchè, seppur in un primo tempo il binding avviene con l'utente del db di accesso, poi per la verifica delle credenziali ldap cerca di bindarsi con l'utente che gli viene passato e, nella mia configurazione originale questo non è possibile: Se il binding ha successo =>credenziali autenticate, altrimenti no.

TEST CON SQUID_LDAP

Ora faccio qualche test con il programma esterno di squid per fare le autenticazioni tipo http. Il programma sta in /us/lib(64)/squid/ e può chiamarsi squid_ldap_auth, ldap_auth, basic_ldap_auth ma è sempre lo stesso.
Questo programma si appoggia all'autenticazione di ldap.

Sul db principale metto queste regole
olcAccess: {0}to *
by dn="cn=utente1,dc=access,dc=net" read
by dn="cn=utente2,dc=access,dc=net" read
by users search
by anonymous auth
by * none
Quindi i 2 utenti privilegiati leggono, gli utenti autenticati possono cercare gli anonimi possono autenticarsi, tutti gli altri niente.
Ora vedo il comportamento di ldapsearch, poi guardo quello di squid_ldap_auth.

Con un utente privilegiato cerco

Enter LDAP Password:
# merli, People, rete.net
dn: uid=merli,ou=People,dc=rete,dc=net
uid: merli
cn: Gabriele Merli
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
objectClass: sambaSamAccount
shadowLastChange: 15173
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 501
gidNumber: 501
homeDirectory: /home/merli
gecos: Gabriele Merli
sambaNTPassword: GAGSDFGFSDGERTWEFVSFSDFSDFSD
sambaSID: s
userPassword:: e1NIQX0rUmgzY0pVY0NNVnRXejZLeE91dTVXeWJKR2s9

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Qui ben si vede molto bene che binda utente1, cerca merli, fine bind
conn=1001 fd=15 ACCEPT from IP=127.0.0.1:51099 (IP=0.0.0.0:389)
conn=1001 op=0 BIND dn="cn=utente1,dc=access,dc=net" method=128
conn=1001 op=0 BIND dn="cn=utente1,dc=access,dc=net" mech=SIMPLE ssf=0
conn=1001 op=0 RESULT tag=97 err=0 text=
conn=1001 op=1 SRCH base="ou=People,dc=rete,dc=net" scope=2 deref=0 filter="(uid=merli)"
conn=1001 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text=
conn=1001 op=2 UNBIND
conn=1001 fd=15 closed

Con un utente non privilegiato cerco

Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <ou=People,dc=rete,dc=net> with scope subtree
# filter: uid=merli
# requesting: ALL
#

# search result
search: 2
result: 0 Success

L' utente loggato può cercare ma non vedere i risultati

Con utente anonimo cerco

# extended LDIF
#
# LDAPv3
# base <ou=People,dc=rete,dc=net> with scope subtree
# filter: uid=merli
# requesting: ALL
#

# search result
search: 2
result: 50 Insufficient access

L'anonimo non può cercare

Ora provo le autentiche

Bind con utente privilegiato e credenziali di un utente nel db principale

Sul server vedo che si effettua il bind con l'utente privilegiato, effettua la ricerca dello user indicato, lo trova e si binda con lo user indicato. Questo bind significa che l'autenticazione è positiva.

conn=1002 fd=15 ACCEPT from IP=127.0.0.1:51100 (IP=0.0.0.0:389)
conn=1002 op=0 BIND dn="cn=utente1,dc=access,dc=net" method=128
conn=1002 op=0 BIND dn="cn=utente1,dc=access,dc=net" mech=SIMPLE ssf=0
conn=1002 op=0 RESULT tag=97 err=0 text=
conn=1002 op=1 SRCH base="ou=People,dc=rete,dc=net" scope=2 deref=0 filter="(uid=merli)"
conn=1002 op=1 SRCH attr=1.1
conn=1002 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text=
conn=1002 op=2 BIND anonymous mech=implicit ssf=0
conn=1002 op=2 BIND dn="uid=merli,ou=People,dc=rete,dc=net" method=128
conn=1002 op=2 BIND dn="uid=merli,ou=People,dc=rete,dc=net" mech=SIMPLE ssf=0
conn=1002 op=2 RESULT tag=97 err=0 text=
conn=1002 op=3 UNBIND
conn=1002 fd=15 closed

Bind (didattico perchè non ha senso) con utente del db locale e  credenziali del db locale

Si può notare che effettua correttamente il primo bind (da acl l'utente può autenticarsi e fare search), cerca merli, non trova nulla e non riesce quindi a ribindarsi

conn=1004 fd=15 ACCEPT from IP=127.0.0.1:54999 (IP=0.0.0.0:389)
conn=1004 op=0 BIND dn="uid=merli,ou=People,dc=rete,dc=net" method=128
conn=1004 op=0 BIND dn="uid=merli,ou=People,dc=rete,dc=net" mech=SIMPLE ssf=0
conn=1004 op=0 RESULT tag=97 err=0 text=
conn=1004 op=1 SRCH base="ou=People,dc=rete,dc=net" scope=2 deref=0 filter="(uid=merli)"
conn=1004 op=1 SRCH attr=1.1
conn=1004 op=1 SEARCH RESULT tag=101 err=0 nentries=0 text=
conn=1004 op=2 UNBIND
conn=1004 fd=15 closed

Bind anonimo e credenziali del db locale

Gli anonimi hanno solo il privilegio di autenticarsi per cui non si riesce a bindare ne tantomeno a cercare alcunchè.

conn=1000 fd=15 ACCEPT from IP=127.0.0.1:55000 (IP=0.0.0.0:389)
conn=1000 op=0 SRCH base="ou=People,dc=rete,dc=net" scope=2 deref=0 filter="(uid=merli)"
conn=1000 op=0 SRCH attr=1.1
conn=1000 op=0 SEARCH RESULT tag=101 err=50 nentries=0 text=
conn=1000 op=1 UNBIND
conn=1000 fd=15 closed

Ultimo esempio guardando i log con debug 128, immaginiamo un solo db con 2 utenti, con uno effettuo il bind e poi provo a verificare le credenziali dell'altro

Avendo letto il manuale del plugin ldap di pgina che dice
http://pgina.org/docs/v3.1/ldap.html

"When a search is used, the plugin connects to the LDAP server anonymously (or via supplied credentials) and attempts to find an entry for the user. If the entry is found, the plugin closes the connection and attempts to bind again using the DN of the entry, and the password provided by the user. If this bind is successful, the plugin registers success."

Mi sono chiesto se bastasse quindi il permesso di ricerca di un dn dopo essersi bindato con un'altro dn (non privilegiato)

Questo il db

+--> dc=merli,dc=lan (4)
---> ou=Computers
+--> ou=Groups (1)
+--> ou=Users (2)
| ---> uid=gmerli
| ---> uid=sarti

Queste le regole

olcAccess: {0}to attrs=userPassword,shadowLastChange
by self write
by anonymous auth
by dn="cn=admin,dc=merli,dc=lan" write
by * none
olcAccess: {1}to *
by users search
by anonymous auth
by * none

Quindi no, non basta il permesso di search.
Vediamo cosa succede, i passaggi sono sempre Richiesta di privilegi su una entry/attributo, Valutazione in base alle acl, Risposta

5193ed7e => access_allowed: result not in cache (userPassword)
5193ed7e => access_allowed: auth access to "uid=gmerli,ou=Users,dc=merli,dc=lan" "userPassword" requested
5193ed7e => acl_get: [1] attr userPassword
5193ed7e => acl_mask: access to entry "uid=gmerli,ou=Users,dc=merli,dc=lan", attr "userPassword" requested
5193ed7e => acl_mask: to value by "", (=0)
5193ed7e <= check a_dn_pat: self
5193ed7e <= check a_dn_pat: anonymous
5193ed7e <= acl_mask: [2] applying auth(=xd) (stop)
5193ed7e <= acl_mask: [2] mask: auth(=xd)
5193ed7e => slap_access_allowed: auth access granted by auth(=xd)
5193ed7e => access_allowed: auth access granted by auth(=xd)
5193ed7e connection_input: conn=1003 deferring operation: binding

5193ed7e => access_allowed: search access to "ou=Users,dc=merli,dc=lan" "entry" requested
5193ed7e => acl_get: [2] attr entry
5193ed7e => acl_mask: access to entry "ou=Users,dc=merli,dc=lan", attr "entry" requested
5193ed7e => acl_mask: to all values by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193ed7e <= check a_dn_pat: users
5193ed7e <= acl_mask: [1] applying search(=scxd) (stop)
5193ed7e <= acl_mask: [1] mask: search(=scxd)
5193ed7e => slap_access_allowed: search access granted by search(=scxd)
5193ed7e => access_allowed: search access granted by search(=scxd)
5193ed7e <= bdb_equality_candidates: (uid) not indexed

5193ed7e => access_allowed: search access to "ou=Users,dc=merli,dc=lan" "uid" requested
5193ed7e => acl_get: [2] attr uid
5193ed7e => acl_mask: access to entry "ou=Users,dc=merli,dc=lan", attr "uid" requested
5193ed7e => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193ed7e <= check a_dn_pat: users
5193ed7e <= acl_mask: [1] applying search(=scxd) (stop)
5193ed7e <= acl_mask: [1] mask: search(=scxd)
5193ed7e => slap_access_allowed: search access granted by search(=scxd)
5193ed7e => access_allowed: search access granted by search(=scxd)

5193ed7e => access_allowed: search access to "uid=gmerli,ou=Users,dc=merli,dc=lan" "uid" requested
5193ed7e => acl_get: [2] attr uid
5193ed7e => acl_mask: access to entry "uid=gmerli,ou=Users,dc=merli,dc=lan", attr "uid" requested
5193ed7e => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193ed7e <= check a_dn_pat: users
5193ed7e <= acl_mask: [1] applying search(=scxd) (stop)
5193ed7e <= acl_mask: [1] mask: search(=scxd)
5193ed7e => slap_access_allowed: search access granted by search(=scxd)
5193ed7e => access_allowed: search access granted by search(=scxd)

5193ed7e => access_allowed: search access to "uid=sarti,ou=Users,dc=merli,dc=lan" "uid" requested
5193ed7e => acl_get: [2] attr uid
5193ed7e => acl_mask: access to entry "uid=sarti,ou=Users,dc=merli,dc=lan", attr "uid" requested
5193ed7e => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193ed7e <= check a_dn_pat: users
5193ed7e <= acl_mask: [1] applying search(=scxd) (stop)
5193ed7e <= acl_mask: [1] mask: search(=scxd)
5193ed7e => slap_access_allowed: search access granted by search(=scxd)
5193ed7e => access_allowed: search access granted by search(=scxd)

5193ed7e => access_allowed: read access to "uid=sarti,ou=Users,dc=merli,dc=lan" "entry" requested
5193ed7e => acl_get: [2] attr entry
5193ed7e => acl_mask: access to entry "uid=sarti,ou=Users,dc=merli,dc=lan", attr "entry" requested
5193ed7e => acl_mask: to all values by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193ed7e <= check a_dn_pat: users
5193ed7e <= acl_mask: [1] applying search(=scxd) (stop)
5193ed7e <= acl_mask: [1] mask: search(=scxd)
5193ed7e => slap_access_allowed: read access denied by search(=scxd)
5193ed7e => access_allowed: no more rules
5193ed7e send_search_entry: conn 1003 access to entry (uid=sarti,ou=Users,dc=merli,dc=lan) not allowed

Quindi si binda, cerca se stesso, cerca l'altro utente, non riesce a leggere l'entry  dell'altro utente, chiusa connessione

Se invece avessimo messo

olcAccess: {0}to attrs=userPassword,shadowLastChange
by self write
by anonymous auth
by dn="cn=admin,dc=merli,dc=lan" write
by * none
olcAccess: {1}to *
by users read
by anonymous auth
by * none

5193edbb => access_allowed: result not in cache (userPassword)
5193edbb => access_allowed: auth access to "uid=gmerli,ou=Users,dc=merli,dc=lan" "userPassword" requested
5193edbb => acl_get: [1] attr userPassword
5193edbb => acl_mask: access to entry "uid=gmerli,ou=Users,dc=merli,dc=lan", attr "userPassword" requested
5193edbb => acl_mask: to value by "", (=0)
5193edbb <= check a_dn_pat: self
5193edbb <= check a_dn_pat: anonymous
5193edbb <= acl_mask: [2] applying auth(=xd) (stop)
5193edbb <= acl_mask: [2] mask: auth(=xd)
5193edbb => slap_access_allowed: auth access granted by auth(=xd)
5193edbb => access_allowed: auth access granted by auth(=xd)
5193edbb connection_input: conn=1000 deferring operation: binding

5193edbb => access_allowed: search access to "ou=Users,dc=merli,dc=lan" "entry" requested
5193edbb => acl_get: [2] attr entry
5193edbb => acl_mask: access to entry "ou=Users,dc=merli,dc=lan", attr "entry" requested
5193edbb => acl_mask: to all values by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193edbb <= check a_dn_pat: users
5193edbb <= acl_mask: [1] applying read(=rscxd) (stop)
5193edbb <= acl_mask: [1] mask: read(=rscxd)
5193edbb => slap_access_allowed: search access granted by read(=rscxd)
5193edbb => access_allowed: search access granted by read(=rscxd)
5193edbb <= bdb_equality_candidates: (uid) not indexed

5193edbb => access_allowed: search access to "ou=Users,dc=merli,dc=lan" "uid" requested
5193edbb => acl_get: [2] attr uid
5193edbb => acl_mask: access to entry "ou=Users,dc=merli,dc=lan", attr "uid" requested
5193edbb => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193edbb <= check a_dn_pat: users
5193edbb <= acl_mask: [1] applying read(=rscxd) (stop)
5193edbb <= acl_mask: [1] mask: read(=rscxd)
5193edbb => slap_access_allowed: search access granted by read(=rscxd)
5193edbb => access_allowed: search access granted by read(=rscxd)

5193edbb => access_allowed: search access to "uid=gmerli,ou=Users,dc=merli,dc=lan" "uid" requested
5193edbb => acl_get: [2] attr uid
5193edbb => acl_mask: access to entry "uid=gmerli,ou=Users,dc=merli,dc=lan", attr "uid" requested
5193edbb => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193edbb <= check a_dn_pat: users
5193edbb <= acl_mask: [1] applying read(=rscxd) (stop)
5193edbb <= acl_mask: [1] mask: read(=rscxd)
5193edbb => slap_access_allowed: search access granted by read(=rscxd)
5193edbb => access_allowed: search access granted by read(=rscxd)

5193edbb => access_allowed: search access to "uid=sarti,ou=Users,dc=merli,dc=lan" "uid" requested
5193edbb => acl_get: [2] attr uid
5193edbb => acl_mask: access to entry "uid=sarti,ou=Users,dc=merli,dc=lan", attr "uid" requested
5193edbb => acl_mask: to value by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193edbb <= check a_dn_pat: users
5193edbb <= acl_mask: [1] applying read(=rscxd) (stop)
5193edbb <= acl_mask: [1] mask: read(=rscxd)
5193edbb => slap_access_allowed: search access granted by read(=rscxd)
5193edbb => access_allowed: search access granted by read(=rscxd)

5193edbb => access_allowed: read access to "uid=sarti,ou=Users,dc=merli,dc=lan" "entry" requested
5193edbb => acl_get: [2] attr entry
5193edbb => acl_mask: access to entry "uid=sarti,ou=Users,dc=merli,dc=lan", attr "entry" requested
5193edbb => acl_mask: to all values by "uid=gmerli,ou=users,dc=merli,dc=lan", (=0)
5193edbb <= check a_dn_pat: users
5193edbb <= acl_mask: [1] applying read(=rscxd) (stop)
5193edbb <= acl_mask: [1] mask: read(=rscxd)
5193edbb => slap_access_allowed: read access granted by read(=rscxd)
5193edbb => access_allowed: read access granted by read(=rscxd)
5193edbb => access_allowed: result not in cache (userPassword)

5193edbb => access_allowed: auth access to "uid=sarti,ou=Users,dc=merli,dc=lan" "userPassword" requested
5193edbb => acl_get: [1] attr userPassword
5193edbb => acl_mask: access to entry "uid=sarti,ou=Users,dc=merli,dc=lan", attr "userPassword" requested
5193edbb => acl_mask: to value by "", (=0)
5193edbb <= check a_dn_pat: self
5193edbb <= check a_dn_pat: anonymous
5193edbb <= acl_mask: [2] applying auth(=xd) (stop)
5193edbb <= acl_mask: [2] mask: auth(=xd)
5193edbb => slap_access_allowed: auth access granted by auth(=xd)
5193edbb => access_allowed: auth access granted by auth(=xd)

Log 256 e si vede il doppio bind
5193f41f conn=1000 op=0 BIND dn="uid=gmerli,ou=users,dc=merli,dc=lan" method=128
5193f41f conn=1000 fd=15 ACCEPT from IP=127.0.0.1:40193 (IP=0.0.0.0:389)
5193f424 conn=1000 op=0 BIND dn="uid=gmerli,ou=Users,dc=merli,dc=lan" mech=SIMPLE ssf=0
5193f424 connection_input: conn=1000 deferring operation: binding
5193f424 conn=1000 op=0 RESULT tag=97 err=0 text=
5193f424 conn=1000 op=1 SRCH base="ou=users,dc=merli,dc=lan" scope=2 deref=0 filter="(uid=sarti)"
5193f424 conn=1000 op=1 SRCH attr=1.1
5193f424 <= bdb_equality_candidates: (uid) not indexed
5193f424 conn=1000 op=2 BIND anonymous mech=implicit ssf=0
5193f424 conn=1000 op=2 BIND dn="uid=sarti,ou=Users,dc=merli,dc=lan" method=128
5193f424 conn=1000 op=2 BIND dn="uid=sarti,ou=Users,dc=merli,dc=lan" mech=SIMPLE ssf=0
5193f424 conn=1000 op=2 RESULT tag=97 err=0 text=
5193f424 conn=1000 op=3 UNBIND
5193f424 conn=1000 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text=
5193f424 conn=1000 fd=15 closed

Quindi si binda, cerca se stesso, cerca l'altro utente, lo trova, legge l'entry, cerca di leggere la password e per farlo, si autentica, OK

BACKUP e RESTORE

Il modo più semplice di effettuare un backup di un db ldap del tipo BDB o HDB è quello di utilizzare slapcat che effettua una copia "offline" dell'interno database specificato

Non c'è autenticazione perchè non si contatta il server ldap ma si lavora fisicamente sui file contenenti il db.

Per effettuare un restore, dopo aver bloccato il demone basterà digitare

Servirà poi ricreare gli indici tramite il comando slapindex
Qui il link alla documentazione
http://www.openldap.org/doc/admin24/dbtools.html

Print Friendly, PDF & Email