LDAP a adresářové služby

Jaroslav Kortus, xkortus@fi.muni.cz


Obsah


1. Adresářové služby

Adresář by měla být specializovaná databáze určená pro čtení, procházení a vyhledávání. Adresáře by my měly poskytovat dostatečně popisné informace členěné podle různých kritérií (atributů). Adresářové služby běžně neposkytují služby obvyklé u větších databází, jako například transakční zpracování, a místo toho se soustřeďují na rychlé vyhledání požadovaných dat. Více na http://www.openldap.org/doc/admin22/intro.html#What is a directory service

LDAP je zkratka z "Lightweight Directory Access Protocol". Již z názvu je patrné, že je to odlehčená verze DAP, který se však v praxi snad nikde pořádně nepoužívá kvůli jeho příliš velké složitosti a s tím souvisejícími implementačními problémy. V současné době se používá třetí verze protokolu.

Adresář v LDAP tvoří zpravidla stromovou strukturu, jak ukazují diagramy na výše zmíněném URL. Identifikátor záznamu složíme jednoduše tak, že jdeme od vybraného záznamu a zřetězíme za sebe jeho identifikátor a identifikátory všech nadřazených uzlů. Výsledku se říká Distiguished name (DN).

2. Download a instalace

OpenLDAP lze stahnout na stránkách http://www.openldap.org/. K instalaci je nejjednodušší postupovat podle quickstart guide, která je k dispozici na http://www.openldap.org/doc/admin22/quickstart.html. Naštěstí většina nových distribucí obsahuje OpenLDAP server již ve své standardní výbavě.

Po stažení a instalaci (více k instalaci na http://www.openldap.org/doc/admin22/install.html ) je třeba server nakonfigurovat.

Hlavní konfigurační soubor je obvykle v /etc/openldap/slapd.conf. S distribucí je dodáván ukázkový konfigurační soubor a nejrychlejší cesta k funkční konfiguraci vede přes úpravu tohoto souboru.

Nejprve nastavíme úložiště pro databázi objektů: database ldbm

suffix     "dc=example,dc=com"
rootdn     "cn=Manager,dc=example,dc=com"
rootpw     neco    
directory /var/lib/example.com
index objectClass eq
Stručné vysvětlení:
backend - mechanizmus, který se bude starat o ukládání dat
suffix - přípona, kterou budou mít všechny vkládané záznamy
rootdn - záznam identifikující vrchního správce
rootpw - heslo pro vrchního správce. To může být buď plaintext nebo jeho hešovaná verze (rootpw {SSHA}ZKKuqbEKJfKSXhUbHG3fG8MDn9j1v4QN)
directory - udává fyzické úložiště dat na disku
index - určuje atributy, které budem indexovat (tvar atribut1,atribut2,... pravidlo1,..,pravidloN). V manuálu uvedené jako LDBM-specific volba, takže pro jiné "backendy" nemusí mít smysl.

Dále je třeba server spustit (buď přímo slapd z příkazového řádku nebo příslušné startup skripty z distribuce) a naplnit iniciálními daty.

3. Plnění dat

K plněni adresáře se běžně používaji tzv. LDIF soubory. Jsou to obyčejné textové (ascii) soubory, ve kterých se nacházejí LDAP záznamy oddělené prázdným řádkem. Poněkud přesnější definici nalezneme na URL http://www.openldap.org/doc/admin22/dbtools.html#The LDIF text entry format Základní tvar vypadá takto:

        # comment
        dn: <distinguished name>
        <attrdesc>: <attrvalue>
        <attrdesc>: <attrvalue>
První řádek je komentář. Druhý řádek udává atribut se jménem "dn", což je zkratka z Distinguished name. Každý záznam musí mít unikátní DN. Následují řádky, které udávají vždy jeden atribut a po dvojtečce jeho hodnotu. Nezapomínejme, že se jedná opravdu o ASCII soubory, takže například české znaky nemůžeme jednoduše zapisovat ani v názvech atributů a ani v jejich hodnotách.

V našem oblíbeném editoru vytvoříme nový textový LDIF soubor (třeba init.ldif) a do něj zadefinujeme pro začátek dva objekty. První je samotný kořen databáze (ten samý jako v slapd.conf) a správce databáze (opět ten samý jako v slapd.conf).

Náš první LDIF soubor bude vypadat takto:

dn: dc=example,dc=com 
objectclass: dcObject 
objectclass: organization 
o: Nazev organizace
dc: example

dn: cn=Manager,dc=,dc= 
objectclass: organizationalRole 
cn: Manager

Máme tedy data v textovém souboru a musíme je ještě dostat do samotného adresáře v LDAPu. K tomu poslouží například příkaz ldapadd.

ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f init.ldif

(parametry popořadě: jednodnuchá autentizace, identifikovat se jako "cn=...", zeptat se na heslo, použít data ze souboru init.ldif)

V případě, že jsme se někde nepřeklepnuli, příkaz skončí bez výpisu. Data jsou v databázi a můžeme začít pracovat. Doteď jsme tam však neuložili vlastně žádná pro nás hodnotná data.

Pro jistotu jednoduché ověření pomocí příkazu:

ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'
(jednoduchá autentizace, prohledávat pod "dc=...", hledat všechno).

Poznámka: V dokumentaci se lze dočíst, že po použití příkazu ldapadd bychom měli server zrestartovat, aby správně poznal všechny změny a dokonce se vyskytuje i doporučení, že před použítim ldapadd by neměl vůbec běžet, jinak se za nic neručí :). Je tedy dobré vyhnout se současnému plnění pomocí ldapadd a případných soubežných modifikací např. z již běžících systémů.

4. Schémata

Doposud jsme absolvovali spíše cvičení kopírování a vkládání do shellu. Jednoduše zde byly nějaké atributy, pak něco jako objectclass a když jsme to nakopírovali do shellu, tak nám vzniknul funkční LDAP.

Jenže v LDAPu (alespoň v tomhle) platí, že nic nedefinovaného z nebe nespadlo. A všechno máme definované v tzv. schématech. Standardní distribuce již obsahuje několik užitečných a obsáhlých schémat. Která ze schémat použijeme záleží na nás a určujeme to použitím direktiv "include" v konfiguračním souboru (slapd.conf).

Pro naše potřeby použijeme:

include     /etc/openldap/schema/core.schema
include     /etc/openldap/schema/cosine.schema
include     /etc/openldap/schema/nis.schema
include     /etc/openldap/schema/inetorgperson.schema

Každý objekt definovaný ve schématech má své jméno a jednoznačný identifikátor, tzv. OID (Object identifier, podobné najdeme třeba v SNMP). Tato čísla si sice můžeme vymýšlet dle libosti, ale dřív nebo později bychom se tak dostali do problémů. Lepší je využít standardních objektů a tříd a vlastní výplody číslovat do segmentu začínajícího 1.1. (experimentální "dead" namespace).

I v definici schemat se logicky odráží stromová struktura, tedy dědičnost. Základem všeho je třída (objectClass) jménem "top". Protože ji musí obsahovat (dědit z ní) každý objekt, není při psaní LDIF záznamů povinná (nahoře byla vynechána). Ostatní třídy jsme již museli explicitně uvést. V definici schémat je použit podobný princip a dědí se z ní standardně.

Každá třída může definovat kromě jména třídy i atributy, které s sebou objekty z ní vytvořené ponesou. Může rovněž určit, které atributy budou povinné a které pouze volitelné. Při definici objektů v LDAP tedy specifikujeme všechny objectClass, které bude objekt používat a poté všechny atributy, které chceme nebo musíme vyplnit.

V definicích můžeme najít tři typy tříd: "ABSTRACT", "STRUCTURAL", "AUXILIARY". Důležité je, že pokud chceme definovat objekt v LDAP, musí obsahovat právě jednu STRUCTURAL objectClass. Pokud tomu bude jinak, bude si slapd stěžovat a záznam neprovede.

Stručná charakteristika:
ABSTRACT - slouží pouze jako šablona pro ostatní
STRUCTURAL - definuje třídu s jistou specifickou množinou vlastností (např. člověk, auto, počítač,...)
AUXILIARY - doplňuje většinou STRUCTURAL třídy dalšími atributy

Tomuto chování lze snadno zabránit volbou "schemacheck off" v konfiguračním souboru slapd.conf. Potom můžeme skutečně do LDAPu vnutit cokoliv, ale samozřejmě to není ta správná cesta řešení problémů ;). Starší verze OpenLDAP nebyly tak striktní, ale novější verze mají standardně tuto kontrolu zapnutou.

Příklad definice:

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 ) )   

5. Autentizace a autorizace

Příhlášení se do LDAPu se běžně označuje jako "bind". Důležité je vědět, že pro přístup do LDAPu nedefinujeme nikde externě uživatele nebo účty, ale rovnou ukážeme na libovolný záznam v LDAP a řekneme, že to jsme my. Server nás následně požádá o heslo a porovná s heslem pro daný záznam.

Autorizaci musíme vyřešit v konfiguračním souboru serveru (slapd.conf). Podrobnější popis na: http://www.openldap.org/doc/admin22/slapdconfig.html#Access Control

Příklad:

       access to *
                by self write
                by anonymous auth
                by * read

Dává přístup ke všem objektům tak, že:
"záznam sám" se může zapsat (pokud jsme se jako on přihlásili, můžeme v sobě měnit data)
anonymní uživatel se může jako záznam přihlásit (nic jiného)
a ostatní (přihlášení) uživatelé mohou záznam číst

Důležité je pořadí vyhodnocování takovýchto deklarací. Nejprve se vyhodnocují deklarace u příslušných databázi v konfiguračním souboru a poté globální. V rámci těchto dvou kategorií se hledá první shoda pro zkoumaný objekt a část za "access to". Pokud se najde, prochází se pravidla pod ní uvedená opět postupně a zastaví se na první shodě na parametru za slovem "by". Pokud se našla shoda, srovná se požadovaná uroveň oprávnění (read,write,...) s úrovní definovanou v konfiguračním souboru. Pokud je shoda i tam, je požadovaný přistup povolen, jinak se zamítá. Je vidět, že pořadí deklarací v konfiguračním souboru je v každém kroku velmi důležité.

Z uvedeného například plyne, že pokud bychom přesunuli pravidlo "by self write" na konec, nemohl by nikdo svůj záznam měnit.

6. Praktické využití

Nyní máme funkční LDAP (kostru) a připravená potřebná schémata. Zbývá takový server ještě rozumně využít, třeba na autentizaci uživatelů do linuxu. Hlavní výhodou pro nás bude, že budeme mít centrální správu uživatelů. Celý problém rozdělíme na dvě části. První bude část autentizace uživatele do systému a druhou zjišťování informací o uživatelích.

Pro uživatelské účty se typicky používá objectClass posixAccount doplněná o shadowAccount, pro skupiny objectClass posixGroup.

6.1 Nastavení autentizace do systému

Pro tyto potřeby využijeme PAM autentizačního systému a jeho modulu pam_ldap.so ( http://www.padl.com/OSS/pam_ldap.html ), běžně součástí distribucí. Upravíme konfigurační soubor PAMu (například /etc/pam.d/sshd pro ssh daemona) a přidáme záznamy pro LDAP:

auth     sufficient   /lib/security/pam_ldap.so
account  sufficient   /lib/security/pam_ldap.so
password sufficient   /lib/security/pam_ldap.so

Tyto řádky musí příjít na vhodné místo tak, aby PAM dělal to, co si přejete ;). K určení tohoto místa (většinou před autentizací krz shadow) nejlépe použít minulý referát k PAMu.

To by však bylo příliš málo. Pokračujeme úpravou konfiguračního souboru pro pam_ldap, který se většinou nachází v /etc/ldap.conf.

Vyplníme nezbytné minimum:

# IP Adresa LDAP serveru (může být i hostname)
host 127.0.0.1

# Větev v LDAPu, pod kterou budeme hledat účty
base dc=example,dc=com

# Verze LDAP protokolu, kterou budeme mluvit
ldap_version 3

# Účet, pod kterým se budeme do LDAPu hlásit 
binddn uid=validator,dc=example,dc=com

# Heslo k účtu
bindpw validator

Nyní by mělo být možné se do systému přihlásit účty z LDAP. Pokud ale uživatel vlastní nějaký soubor, příkaz ls -la by nám ukazoval pouze číslo. To je třeba ještě změnit.

6.2 Nastavení NSS

K tomuto účelu použijeme knihovnu nss_ldap ( http://www.padl.com/OSS/nss_ldap.html ), která potřebné informace vyhledá v LDAP. Opět bývá standardní součástí distribucí.

Konfigurace NSS spočívá v úpravě souboru /etc/nsswitch.conf a přípsání "ldap" všude tam, kde chceme, aby se LDAP používal. V našem případě to bude vypadat například takto (po úpravě):

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

Tím doplníme údaje z /etc/passwd, /etc/shadow a /etc/group z LDAP.

7. Kešování

Doteď jsme systém nastavili tak, že pro každou požadovanou informaci (např. převod uid čísla na jméno uživatele) se dotazoval LDAP serveru. To samozřejmě (i přes vhodné indexy) generuje na straně LDAP serveru značnou zátěž. Vhodným kešováním již na lokálním stroji by tato zátěž výrazně klesla.

K odstranění této zátěže můžeme použít nscd, konfigurační soubor je obvykle /etc/nscd.conf. Bližsí info viz man nscd.conf.

8. Příklad stromu pro autentizaci uživatelů

(kopie z dokumentace)

dn:dc=yourorg, dc=com
objectclass: top
objectclass: organizationalUnit

dn:ou=groups, dc=yourorg, dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups

dn:ou=people, dc=yourorg, dc=com
objectclass: top
objectclass: organizationalUnit
ou: people

dn: cn=Giuseppe LoBiondo, ou=people, dc=yourorg, dc=com
cn: Giuseppe Lo Biondo
sn: Lo Biondo
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: shadowAccount
uid:giuseppe
userpassword:{crypt}$1$ss2ii(0$gbs*do&@=)eksd
uidnumber:104
gidnumber:100
gecos:Giuseppe Lo Biondo
loginShell:/bin/zsh
homeDirectory: /home/giuseppe
shadowLastChange:10877
shadowMin: 0
shadowMax: 999999
shadowWarning: 7
shadowInactive: -1
shadowExpire: -1
shadowFlag: 0

dn: cn=mygroup, ou=groups, dc=yourorg, dc=com
objectclass: top
objectclass: posixGroup
cn: mygroup
gidnumber: 100
memberuid: giuseppe
memberuid: anotheruser

9. Odkazy

http://www.openldap.org/doc/admin22/quickstart.html
http://www.openldap.org/
RFC2253, "Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names."
http://www.rfc-editor.org/rfc/rfc2253.txt
RFC2251 "The Lightweight Directory Access Protocol (v3)"
ftp://kalamazoolinux.org/pub/pdf/ldapv3.pdf
http://en.tldp.org/HOWTO/LDAP-HOWTO/
http://www.tldp.org/HOWTO/LDAP-Implementation-HOWTO/pamnss.html
GQ LDAP klient
http://biot.com/gq/