Table of Contents

osnova skoleni

  1. Co si odnesete
  2. Co je ldap
  3. Instalace na linuxu
  4. Pridavani zaznamu
  5. Vyhledavani
  6. PhpLDAPadmin
  7. Pridavani strukturalnich zaznamu
  8. Pokrocile vyhledavani
  9. Ukazka php
  10. Zabezpeceni, zalohovani a indexy

Co si odnesete

Co je LDAP

LDAP je protokol. Neurcuje nic jineho, ani zpusob ulozeni dat.

Mame zaznam (napriklad ja uid=dalibor,ou=people,dc=starlab), ten se sklada z nekolika objectClass a ta ma v sobe atributy a ty maji v sobe hodnoty. Cele to pripomina adresar, skoro jako DNS. Tak a ted vite o LDAPu skoro vse.

Narozdil od ostatnich systemu (napr. sql) maji klice/atributy preddefinovana jmena a jsou prednstaveny vybranou objectClass pro dany zaznam.

LDAP pripomina spise dresar, kde je jmeno prijmeni, adresa, telefon a dnes jeste email a heslo, nic vic z toho nedelejte. Poznamka pro me: Vyjasnit terminologii, DIT, schemata, classy, atributy, strukturalni, povinne a nepovinne. Nakousnout podivnou prefixovou notaci dotazu.

An entry is basically a collection of attributes under a name used to describe something.

objectclass ( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
attributetype ( 2.5.4.41 NAME 'name' DESC 'RFC4519: common supertype of name attributes'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )

Unlike traditional relational databases, schemas in LDAP are simply collections of related objectClasses and attributes

In its definition, an objectClass can identify a par ent objectClass from which to inherit its attributes. This is done using SUP followed by the objectClass to inherit from. For instance, the organizationalPerson objectClass begins like this: objectclass ( 2.5.6.7 NAME 'organizationalPerson' SUP person STRUCTURAL . .

inetOrgPerson → organizationalPerson → person → top

Skoro vsechny dedicnosti objectClass ve stromu konci specielni objectClassou zvanou “top”. Prakticky jen proto, “aby tam neco bylo”.

Instalace na Linux

# apt-get install slapd ldap-utils

Poznamka pro me: zminit fail2ban a omezit jen na vasi sit, kde je DB

Existuji jeste krome BDB take HDB a MDB. Mene konfigurace, rychlejsi. BDB je naopak vyzkousena min. 20 let a opravitelna.

Uchylarna s DB_CONFIG

DB_CONFIG
# WARNING: Before tuning the following parameters, _PLEASE READ_
#   /usr/share/doc/slapd/README.DB_CONFIG.gz
 
# Set the database in memory cache size.
#
# set_cachesize <gbytes> <bytes> <ncache>
#   Sets the database in memory cache size. 
#   Database entries and indexes will be stored in this cache to 
#   avoid disk access during database read and write operations. 
#   Tuning this value can greatly effect your database performance. 
#   The parameters are:
#      <gbytes>: The number of gigabytes of memory to allocate to the cache.
#      <bytes>: The number of bytes of memory to allocate to the cache.
#      <ncache>: The number of cache segments to use. If this value is set to 
#          0 or 1 then Berkeley DB will try to allocate one contiguous section 
#          of memory for the cache. If this value is greater than 1, the cache 
#          will be split into that number of segments.
#set_cachesize   0       52428800        0
 
# For the Debian package we use 2MB as default but be sure to update this
# value if you have plenty of RAM
set_cachesize   0 2097152   0
 
# Sets the database startup flags.
#
# set_flags <flag>
#   There are various flag options that may be set. The DB_TXN_NOSYNC flag 
#   tells the database not to immediately flush transaction buffers to disk. 
#   Setting this flag can help speed up database access during periods of 
#   database write activity BUT at expense of data safety. Enable it only
#   to load data with slapadd, while slapd is not running.
#set_flags       DB_TXN_NOSYNC
 
 
# Set the maximum in memory cache in <bytes> for database file name caching.
#
# set_lg_regionmax <bytes>
#   This value should be increased as the number of database files increases 
#   (tables and indexes).
#set_lg_regionmax        1048576
 
# Set the maximum size of log files in <bytes>.
#
# set_lg_max <bytes>
#   Logs will be rotated when <bytes> amount of data have been written to 
#   one log file. This value should be at least four times the size of 
#   set_lg_bsize.
#set_lg_max              10485760
 
# Set the in memory cache for log information.
#
# set_lg_bsize <bytes>
#   When <bytes> amount of logging information have been written to this 
#   cache it will be flushed to disk.
#set_lg_bsize            2097152
# For the Debian package we use 512kByte which should suffice for typical
# directory usage (read often, write seldom)
set_lg_bsize  524288
 
# Set the log file directory to <directory>. 
#
# set_lg_dir              /usr/local/var/openldap-logs
#   Log files should preferably be on a different disk than the 
#   database files. This both improves reliability (for disastrous
#   recovery) and speed of the database.
#set_lg_dir <directory>
 
 
# Sven Hartge reported that he had to set this value incredibly high
# to get slapd running at all. See http://bugs.debian.org/303057
# for more information.
 
# Number of objects that can be locked at the same time.
set_lk_max_objects      5000
# Number of locks (both requested and granted)
set_lk_max_locks        5000
# Number of lockers
set_lk_max_lockers  5000

Filipika proti slapd.d a pouziti slapd.conf. Soubor vytvorime pomoci

 cat >slapd.conf

vkladani staci copy&paste, prostredni tlacitko mysi a ukoncime stiskem ctrl+d.

slapd.conf
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema
 
# Allow LDAPv2 client connections.  This is NOT the default.
allow bind_v2
#disallow bind_anon
 
#schemacheck on
pidfile /var/run/slapd/slapd.pid
argsfile /var/run/slapd/slapd.args
loglevel 50
 
# DB
modulepath /usr/lib/ldap
moduleload back_bdb
backend bdb
#checkpoint 51230
database bdb
suffix "dc=pb"
rootdn "cn=admin,dc=pb"
rootpw deLTa2019
cachesize 10000
 
# multi master
moduleload syncprov.la
moduleload accesslog.la
serverID 2
checkpoint 10240 720
 
directory "/var/lib/ldap"
index objectClass eq
lastmod on
 
access to dn.base="" by * read
 
access to *
  by dn="cn=admin,dc=example,dc=net" write
  by * read
#!/bin/sh
 
DIR=/etc/ldap/slapd.d
 
rm $DIR -rf
mkdir $DIR
slaptest -f slapd.conf -F $DIR
chown openldap.openldap -R $DIR
cp bin/DB_CONFIG /var/lib/ldap
chown openldap.openldap -R /var/lib/ldap

Nezapomenout na gymnastiku s

 chown openldap.openldap /var/lib/ldap -R
 chown openldap.openldap /etc/ldap/slapd.d -R

pri vytvareni databaze a provadeni slaptest -f -F.

Test, zda-li slapd posloucha na portu 389

netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State 
tcp        0      0 0.0.0.0:9102            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:5666            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:389             0.0.0.0:*               LISTEN
tcp6       0      0 :::111                  :::*                    LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::25                   :::*                    LISTEN
tcp6       0      0 :::5666                 :::*                    LISTEN
tcp6       0      0 :::389                  :::*                    LISTEN

Pridavani zaznamu

Nejdrive si vytvorime soubor a pak ho pridame pomoc ldapadd

pb.ldif
version: 1
 
## version not strictly necessary (and some implementations reject it) but generally good practice
 
## DEFINE DIT ROOT/BASE/SUFFIX ####
## uses RFC 2377 (domain name) format
 
## dcObject is an AUXILIARY objectclass and MUST
## have a STRUCTURAL objectclass (organization in this case)
# this is an ENTRY sequence and is preceded by a BLANK line
 
dn: dc=pb
dc: pb
description: Praguebest
objectClass: dcObject
objectClass: organization
o: Praguebest s.r.o.
 
 
 
## FIRST Level hierarchy - people
# this is an ENTRY sequence and is preceded by a BLANK line
dn: ou=people, dc=pb
ou: people
description: All people in organisation
objectClass: organizationalUnit
 
## SECOND Level hierarchy - people entries
# this is an ENTRY sequence and is preceded by a BLANK line
 
dn: cn=Radek Saturka,ou=people,dc=pb
objectclass: inetOrgPerson
cn: Radek Saturka
cn: Radek
sn: Saturka
uid: saturka
mail: radek.saturka@praguebest.com
ou: manager
 
## SECOND Level hierarchy - people entries
# this is an ENTRY sequence and is preceded by a BLANK line
 
dn: cn=Bill Novak,ou=people,dc=pb
objectclass: inetOrgPerson
cn: Bill Novak
cn: William
sn: Novak
uid: bnovak
mail: bill@example.com
ou: project
 
## SECOND Level hierarchy - people entries
# this is an ENTRY sequence and is preceded by a BLANK line
 
dn: cn=John Novak,ou=people,dc=pb
objectclass: inetOrgPerson
cn: John Novak
sn: novak
uid: jnovak
mail: jim@example.com
ou: prog
 
dn: cn=Robert Novak,ou=people,dc=pb
objectclass: inetOrgPerson
cn: Robert Novak
cn: Robert
sn: Novak
uid: rnovak
mail: robert@example.com
ou: project
 
dn: ou=groups, dc=pb
ou: groups
description: generic groups for GDPR
objectClass: organizationalUnit
pb_add1.ldif
dn: cn=projekt_a,ou=groups,dc=pb
cn: projekt_a
description: grupa ktera smi jen na projekt_a
objectClass: groupOfNames
member: cn=Bill Novak,ou=people,dc=pb
 
dn: cn=projekt_b,ou=groups,dc=pb
cn: projekt_b
description: grupa ktera smi jen na projekt_b
objectClass: groupOfNames
member: cn=John Novak,ou=people,dc=pb
   ldapadd  -x -D "cn=admin,dc=pb" -w heslo -f pb_add1.ldif

Ted sami pridejte

pb_add2.ldif
dn: uid=cervenka,ou=people,dc=pb
objectclass: inetOrgPerson
cn: David Cervenka
sn: Cervenka
uid: cervenka
mail: cervenka@praguebest.com
 
dn: uid=coufal,ou=people,dc=pb
objectclass: inetOrgPerson
cn: Martin Coufal
sn: Coufal
uid: coufal
mail: coufal@praguebest.com
 
dn: uid=cajkovsky,ou=people,dc=pb
objectclass: inetOrgPerson
cn: Igor Cajkovsky
sn: Cajkovsky
uid: cajkovsky
mail: cajkovsky@praguebest.com

Vyhledavani

Mnoho prikladu

ldapsearch -x -b 'uid=novak,ou=people,dc=pb' 
ldapsearch -x -b 'cn=Radek Saturka,ou=people,dc=pb' 
ldapsearch -x  -b 'ou=people,dc=pb'  uid=saturka
ldapsearch -x  -b 'ou=people,dc=pb'  uid=jnovak
ldapsearch -x  -b 'dc=pb'  uid=saturka
ldapsearch -x  -b 'dc=pb'  uid=jnovak

A pozor, porad jedeme anonymne bez hesla, jinak bude search vypadat takto:

 ldapsearch -x -b 'ou=people,dc=pb'  -D uid=cajkovsky,ou=people,dc=pb -w blooood '(&(objectClass=inetOrgPerson)(uid=cajkovsky)(memberOf=cn=projekt_b,ou=groups,dc=pb))'
 

Instalace PhpLDAPadmin

Pozor, je potreba pouzit php5, protoze nema opravene par chyb. Napr. create_function je v php7 obsolete

 $CACHE[$sortby] = create_function('$a, $b',$code);

Nam bude pro ukazku bohate stacit apache2 s php5 modulem. Pokud mate debian 9 stretch, pak je treba pro vyukove ucely pridat do /etc/apt/sources.list

   deb http://debian.superhosting.cz/debian/ jessie main contrib non-free

Provedte update db a pak instalaci

   apt update
   apt install libapache2-mod-php5  php5.0-xml 

Par uprav v konfiguraci a muzeme jet

$servers->setValue('server','host','127.0.0.1');
 
/* The port your LDAP server listens on (no quotes). 389 is standard. */
// $servers->setValue('server','port',389);
 
/* Array of base DNs of your LDAP server. Leave this blank to have phpLDAPadmin
   auto-detect it for you. */
$servers->setValue('server','base',array('dc=pb'));
 
/* Five options for auth_type:
   1. 'cookie': you will login via a web form, and a client-side cookie will
      store your login dn and password.
   2. 'session': same as cookie but your login dn and password are stored on the
      web server in a persistent session variable.
   3. 'http': same as session but your login dn and password are retrieved via
      HTTP authentication.
   4. 'config': specify your login dn and password here in this config file. No
      login will be required to use phpLDAPadmin for this server.
   5. 'sasl': login will be taken from the webserver's kerberos authentication.
      Currently only GSSAPI has been tested (using mod_auth_kerb).
 
   Choose wisely to protect your authentication information appropriately for
   your situation. If you choose 'cookie', your cookie contents will be
   encrypted using blowfish and the secret your specify above as
   session['blowfish']. */
$servers->setValue('login','auth_type','session');
 
/* The DN of the user for phpLDAPadmin to bind with. For anonymous binds or
   'cookie','session' or 'sasl' auth_types, LEAVE THE LOGIN_DN AND LOGIN_PASS
   BLANK. If you specify a login_attr in conjunction with a cookie or session
   auth_type, then you can also specify the bind_id/bind_pass here for searching
   the directory for users (ie, if your LDAP server does not allow anonymous
   binds. */
$servers->setValue('login','bind_id','cn=admin,dc=pb');

Racte namirit vas browser na cerstve nainstalovany ldapadmin http://127.0.0.1/phpldapadmin

Pridavani strukturalnich zaznamu

# ldapadd -x -D "cn=admin,dc=pb" -W -f memberof_config.ldif
Enter LDAP Password: 
adding new entry "cn=module,cn=config"
ldap_add: Insufficient access (50)

# ldapadd -x -D "cn=admin,dc=pb" -W -f memberof_config.ldif
Enter LDAP Password: 
ldap_bind: Invalid credentials (49)

Pri pridavani strukturalni zmeny hluboko do strev ldapu - memberOf. Obvykla metoda ldapadd selhala na nedostatku prav pozdeji na chybnem prihlaseni

Klasický BindDN uživatel (admin), který je největší kápo celého ldapu neměl dost oprávnění.

Nevim, jestli je to specialitka Debianu, ale vytvoří vám admina jen pro váš celý DIT. Jenže DIT není vše, existuje ještě “ldap v ldapu”. Ne, nezbláznil jsem se, ale měkké mozky vymysleli, že nově se bude konfigurace LDAPu ukládat do adresářové struktury slap.d/cn=config/foo=bar atd. Při pečlivějším zkoumání zjistíte, že máte databáze dvě, jedna se jmenuje bla=bla{0} a druhe foo=bar{1}. A právě ta {0} je strukturální mimo celý váš DIT.

Soubor můžete plaintext editovat

 /etc/ldap/slapd.d/cn=config/olcDatabase\=\{0\}config.ldif

a změnit admina a v debianu přidat heslo. Bacha na koncovku cn=config, bez ní to nejde. Jen pro úplnost uvádím jaká chyba vznikne ( olcRootPW: value #0: <olcRootPW> can only be set when rootdn is under suffix )

olcRootDN: cn=admin,cn=config
olcRootPW: superheslo

Je nutné restartovat ldap:

 service slapd restart

a vyzkoušet funkčnost pomocí

 ldapwhoami -x -D "cn=admin,cn=config" -x -w superheslo
 dn:cn=admin,cn=config

Pokud vše funguje můžete vesele přidat memberOf pošetilost

 ldapadd -x -D "cn=admin,cn=config" -W -f memberof_config.ldif
memberof_config.ldif
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
olcModuleLoad: memberof
olcModulePath: /usr/lib/ldap
 
dn: olcOverlay={0}memberof,olcDatabase={1}bdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf

Proc nepouzivat “muzevsude” a jine powergroupy? → protoze tam z lenosti pak skonci vsichni.

Pokrocile vyhledavani

Syntaxe je prefixova, kterou vymyslel chory mozek, aby byl LDAP jeste vice hustej nezli je

 ldapsearch -x -b 'ou=people,dc=pb' '(&(objectClass=inetOrgPerson)(uid=cajkovsky)(memberOf=cn=projekt_b,ou=groups,dc=pb))'
 
 
 NAME=ldapsearch -x -h 127.0.0.1 "(&(objectClass=person)(|(telephoneNumber=${NUM})(mobile=${NUM})(homePhone=${NUM})(fax=${NUM})))" cn | sed -n 's/cn:\s\(.*\)/\1/p
 
 ldapsearch -x -b "dc=pb"  '(objectClass=inetOrgPerson)' uid

Ukazka kodu v PHP

Pozor na mala a velka pismena v poli memberOf → memberof

<?php
 
    $server = "ldap://127.0.0.1";
 
    $ldap = ldap_connect($server);
    $username = 'cajkovsky';
    $password = 'blooood';
 
    $ldap_base = 'ou=people,dc=pb';
 
    ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
 
#    $bind = @ldap_bind($ldap, "cn=$username,$ldap_base", $password);
    $bind = @ldap_bind($ldap, "uid=$username,$ldap_base", $password);
 
                if($bind) {
                        print "gut";
                } else {
                        print "access denied";
                }
 
   $filter = "(uid=" . $username . ")";
   $attrs = array("memberOf");
   $result = ldap_search($ldap, 'ou=people,dc=pb', $filter, $attrs);
 
   $entries = ldap_get_entries($ldap, $result);
   print_r($entries[0]['memberof']);

Dotaz na clenstvi ve skupine je diky schizofrennimu memberof jednoduche

<?php
 
    $server = "ldap://127.0.0.1";
 
    $ldap = ldap_connect($server);
    $username = 'cajkovsky';
    $password = 'blooood';
 
    $ldap_base = 'ou=people,dc=pb';
 
    ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
 
#    $bind = @ldap_bind($ldap, "cn=$username,$ldap_base", $password);
    $bind = @ldap_bind($ldap, "uid=$username,$ldap_base", $password);
 
                if($bind) {
                        print "gut";
                } else {
                        print "access denied";
                }
 
   $filter = "(uid=" . $username . ")";
   $attrs = array("memberOf");
   $result = ldap_search($ldap, 'ou=people,dc=pb', $filter, $attrs);
 
   $entries = ldap_get_entries($ldap, $result);
   print_r($entries[0]['memberof']);
 
   $filter = "(&(objectClass=inetOrgPerson)(uid=cajkovsky)(memberOf=cn=projekt_b,ou=groups,dc=pb))";
 
   $result = ldap_search($ldap, 'ou=people,dc=pb', $filter, $attrs);
   $attrs = array("uid");
   $entries = ldap_get_entries($ldap, $result);
   print_r($entries);
?>

Zabezpeceni, zalohovani a indexy

disallow bind_anon A nasledne opet pregenerovat slapd.d. Pozor zmizi vam memberof a to si uzijeme. Musite proste znovu pridat olcRootDN i PW jinak se vam bude ldap chovat dost zvlastne a bohuzel nebude ani hazet chyby. Lze upravovat i online pres modifikaci pomoci ldapadd:

*zalohovani* Zde jen prakticky slapcat, vzhledem k obvykle velikosti v radu jednotek MB se muze zalohovat vesele i nekolikrat denne a zasadne gzipovat, protoze se jedna o krasny plaintext.

*indexy* index objectClass uid eq

ldapsearch -xLLL -b cn=config -D cn=admin,cn=config -W olcDatabase={1}bdb olcDbIndex
Enter LDAP Password: 
dn: olcDatabase={1}bdb,cn=config
olcDbIndex: objectClass eq
ldapmodify -x -D cn=admin,cn=config -W
Enter LDAP Password: 
dn: olcDatabase={1}bdb,cn=config
add: olcDbIndex
olcDbIndex: cn eq
olcDbIndex: uid eq
modifying entry "olcDatabase={1}bdb,cn=config"
# ldapsearch -xLLL -b cn=config -D cn=admin,cn=config -W olcDatabase={1}bdb olcDbIndex
Enter LDAP Password: 
dn: olcDatabase={1}bdb,cn=config
olcDbIndex: objectClass eq
olcDbIndex: cn eq
olcDbIndex: uid eq