OpenLDAP Workshop
Jump to navigation
Jump to search
Course plan ⌘
- Day 1
- Introduction
- How to install OpenLDAP
- Note: the setup on CentOS will be INSECURE - all passwords exposed to anonymous
- What to store there
- Day 2
- How to configure, secure and fine-tune it
- Day 3
- Various multi-server use cases
LDAP compared to databases ⌘
- Designed with "directory services" in mind
- Borrows a lot from X.500
- Hierarchical
- Decentralized
- Through referrals and replication
- Schema-driven
- ASN1-based binary protocol
LDAP RFCs ⌘
- Old RFCs were RFC2251 - RFC2256 and others
- Developed in 1997
- The big clarification and rewrite of LDAP RFCs happened in 2006
LDAP servers ⌘
- Michigan-based
- OpenLDAP
- ReOpenLDAP
- 389 Directory Server
- Java-based
- ApacheDS
- OpenDJ
- Platform-specific
- ActiveDirectory
- SAMBA 4
- There are others
Installation of OpenLDAP ⌘
- Always use the system's package manager
- apt install slapd
- yum install openldap-servers
- This way, you'll get timely security updates
- OpenLDAP developers don't always release new versions if the vulnerability is only minor
- Your distribution's maintainers are subscribed to the private "distros" list and get notified in advance
- There is no way for you to subscribe
- Recent vulnerabilities
- That's mostly about crashes
- Your distribution has an old version?
- Doesn't really matter, as long as security patches are applied on top
- Changelog lists mostly crashes in conditions that you won't hit
Installation from source ⌘
- Just a ./configure --lots-of-options && make && make install
- See how major distros do it
Known legal issues ⌘
- OpenLDAP BDB and HDB backends use Berkeley DB
- Berkeley DB version 6.0 has switched to AGPL
- Requires you to make the source code available if you run a service accessible by others
- Not acceptable
- Berkeley DB version 6.0 has switched to AGPL
- OpenLDAP libraries link to SSL/TLS libraries
- OpenSSL license is incompatible with GPL
- Problem for GPLed applications that use LDAP libraries
- Solutions:
- Declare OpenSSL to be a system library (legally questionable but done in Arch, Fedora and CentOS)
- Link to GnuTLS (done in Debian)
- Link to Mozilla NSS (was done before in Fedora & CentOS, no longer supported)
- OpenSSL license is incompatible with GPL
Configuration mechanisms ⌘
- /etc/openldap/slapd.conf: the configuration file
- Obsolete, not even supplied by most distributions by default
- Still handy because configuration works in terms of the desired state
- /etc/openldap/slapd.d/: the "configuration" directory
- Initially empty on Arch, pre-populated in other distributions
- Not intended for editing manually – use ldapmodify with cn=config
- Changes apply without restarting – that's the supposed benefit
- Counter-intuitive: you formulate desired changes, not the desired state itself
- /etc/openldap/slapd.ldif (on Arch): file that you can use for such import (placed elsewhere in other distributions)
- CentOS 7: /usr/share/openldap-servers/slapd.ldif
- /etc/openldap/schema/: the schema files
- *.schema are for including into slapd.conf, *.ldif is for importing into cn=config
Package contents ⌘
- /usr/bin/ldap{search,add,modify,delete,...}: command-line clients
- They talk to the LDAP server via network
- /usr/bin/slapd: the server
- Listens to the network, reads/modifies its database
- The database is typically in /var/lib/openldap/ or /var/lib/ldap/
- Listens to the network, reads/modifies its database
- /usr/bin/slap{add,cat,index,...}: command-line tools
- They interact with the database directly
- /usr/lib/openldap/*.so: overlays, backends and plugins as loadable modules
- /usr/lib/libldap{,_r}.so: client libraries
- _r means "reentrant"
- /usr/lib/liblber.so: library that implements LDAP Basic Encoding Rules
- Mostly an implementation detail
- /usr/include/{ldap,ldif,lber}.h: C headers
- Finally, there are manual pages
Packages in CentOS 7 ⌘
- openldap: contains libraries
- openldap-devel: headers
- openldap-servers: servers, modules and slap* tools
- openldap-clients: ldap* clients
Default configuration in CentOS 7 ⌘
- Based on cn=config
- pre-existing slapd.conf converted automatically
- template slapd.conf not even supplied
- If you try to apply your old habits, restore slapd.conf, and remove slapd.d, it will be converted again during the first package upgrade
- Other distributions are not that evil
- Mentions things like dc=my-domain,dc=com that we'll need to change
- Does not specify the password
slapd.conf... ⌘
- slapd.conf
- Changes require restart
- Friendly to configuration management
- Can be "replicated" manually if needed
- Can be converted to cn=config at any time
- mkdir slapd.d ; slaptest -f slapd.conf -F slapd.d (do it somewhere in your home directory)
- Many documented examples exist
- Easy to undo mistakes
- If you are sure that nothing in your database depends on them
- Will stop working in OpenLDAP 2.5
...vs cn=config ⌘
- cn=config
- Many (but not all) changes can be applied dynamically
- Configuration management tools have to be taught how to apply changes
- May be non-trivial — the tools are about copying the desired configuration files, not about applying changes
- Can be replicated automatically
- No way back to slapd.conf except creating one manually, converting, and trying to match
- A lot of documentation is available only in slapd.conf format
- Some mistakes (like an unneeded module) cannot be undone at all
- Because your database likely depends on them already
- Is our dark future
If you still want to use slapd.conf in CentOS 7 ⌘
- You have to write it yourself
- Good template (still needs some minor manual cleanup and addition of schema files):
sed 's@^\(#\?\)olc\([^:]*\):@\1\L\2@' slapd.ldif | grep -v ':' > slapd.conf
- Alternatives:
- Get from a CentOS 6 RPM
- Or from upstream (requires path tweaks)
- You have to keep a dummy /etc/openldap/slapd.d/cn=config.ldif file
- Keep it invalid so that tools fall back to slapd.conf
# Dynamic configuration is not used dn: cn=config
- Add to /etc/sysconfig/slapd:
SLAPD_OPTIONS="-f /etc/openldap/slapd.conf"
Initial database creation ⌘
- This step is fully automated in all distributions
- Useful only if you screw up your dev server and want a fresh start
- Also this is how you restore backups
mkdir -m 0700 /etc/openldap/slapd.d slapadd -F /etc/openldap/slapd.d -n 0 -l /path/to/slapd.ldif slapadd -F /etc/openldap/slapd.d -l /path/to/data.ldif chown -R ldap:ldap /etc/openldap/slapd.d /var/lib/openldap
- Adjust the paths as needed on your distribution
- Here is the default upstream slapd.ldif
- Needs directories adjusted
- A copy is usually installed somewhere in either /usr or /etc by your distribution
- You can tweak it before adding to slapd.d
Starting the server ⌘
- Assuming that the configuration is good-enough:
- service slapd start
- This, essentially, runs /usr/bin/slapd -u ldap -g ldap
- Use -h "ldap://10.0.3.45 ldap://192.0.2.1 ldapi://" to listen on specific sockets
- The default port is 389
- "ldapi" means UNIX-domain socket, commonly used for editing cn=config
- "ldaps" means "LDAP over TLS on port 636", deprecated in favor of STARTTLS on port 389
- Options are usually adjustable in a distribution-specific startup file
- The default configuration in CentOS 7 is good enough to reconfigure the server using ldapmodify
- The default database is empty, so we can freely change the domain
Changing the domain ⌘
- Create an LDIF file describing the change
dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=training,dc=com - replace: olcRootDN olcRootDN: cn=Manager,dc=training,dc=com
- Apply the modifications using ldapmodify as root
# ldapmodify -Y EXTERNAL -H ldapi:// -f change-domain.ldif
Setting root password ⌘
- Hash the password (optional)
# slappasswd
- Create an LDIF file describing the change
dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}..... -
- Apply the modifications using ldapmodify as root
# ldapmodify -Y EXTERNAL -H ldapi:// -f set-password.ldif
Making a hole in the firewall ⌘
- LDAP uses port 389
# firewall-cmd --permanent --add-service=ldap
- We'll also use Apache for phpLDAPadmin
# firewall-cmd --permanent --add-service=http # firewall-cmd --permanent --add-service=https
- Allow Apache to connect to LDAP
# setsebool httpd_can_connect_ldap on # setsebool -P httpd_can_connect_ldap on
- Apply the changes
# firewall-cmd --reload
Notes on root DN and password ⌘
- The DN does not need to actually exist in order to work
- If it does exist, both the password from its userPassword attribute and from olcRootPW work!
- Think about olcRootPW (or maybe even as olcRootDN) as a backdoor
- Such temporary backdoor is useful for initial population of the database
- An alternative is to do the initial population via slapadd
LDAP data model ⌘
- Each entry has attributes
- Mandatory or optional – defined by the schema
- Actually, by objectClasses
- Multiple values permitted unless the schema says no
- Type depends on the name
- Mandatory or optional – defined by the schema
- Typically, one of the attributes is chosen as a "relative distinguished name" (RDN)
- Format: name=value, e.g.: uid=john
- There is exactly one parent, except for roots
- Full distinguished name of the record = RDN + "," + parent's DN
- E.g.: uid=john, ou=employees, dc=acme, dc=org
Attribute types, syntaxes and object classes ⌘
- Parts of the schema
- Syntaxes define what values are valid for an attribute
- Matching rules specify how to compare attribute values
- Both are ultimately encoded by OIDs
- attributeTypes define attribute OID and name, and describe the syntax that the attribute uses, as well as matching rules
- objectClasses define class OID and name, and describe the mandatory and permissible attributes
- They can be either structural or auxiliary
- Inheritance (SUP) is supported
- An entry must have exactly one structural objectClass that is not a SUP of other structural objectClasses used in this entry
Populating the directory with fake data ⌘
- Use LDIF Generator (in Java)
- Generates inetOrgPerson and organizationalUnit
- Or another LDIF Generator (in C)
- Generates POSIX accounts and groups
- There are other alternatives
- You can write your own
General-purpose graphical clients ⌘
- Apache Directory Studio
- phpLDAPadmin (suffers from bitrot due to new PHP)
- JXplorer
Special-purpose clients ⌘
Other useful tools ⌘
- ldapvi
- Handy on cn=config, too!
- ldapvi -Y EXTERNAL -h ldapi:/// -b cn=config --may
- tcpdump and wireshark
Most common use cases ⌘
- Authentication backend
- OwnCloud, Drupal, UNIX accounts, mail servers, ...
- Address book
- Thunderbird, other clients
- Storage for variable data
- For DHCP leases, DNS zones, ...
LDAP operations ⌘
- RFC4511 defines the following operations:
- Bind (supplies authentication credentials)
- Unbind (closes connection - not the reverse of Bind)
- Unsolicited Notification (not really an operation...)
- Search
- Modify
- Add
- Delete
- Modify DN
- Compare
- Abandon (i.e. cancel a pending operation)
- Extended Operation (catch-all for protocol extensions)
- StartTLS
Searching from command line ⌘
- Use ldapsearch
- Use authentication-related parameters from the next slides
- Specify the protocol and host using "-H"
- Specify the base DN using "-b"
- Specify the scope using "-s" ("base", "one", "sub")
- Then, specify the filter and the wanted attributes
- The default filter is "(objectclass=*)" which matches everything, and by default all "user attributes" (i.e. those in the schema, and not those made up by the LDAP server) are returned
Filters ⌘
- A filter is a tree of attribute value assertions connected by boolean operations
- Defined by RFC4515
- An attribute value assertion is written as: (attribute op value)
- E.g.: (uid=1000)
- Available operators
- Comparison with the value: attr=val attr>=val attr<=val
- Approximate matching: attr~=val
- Substring testing: attr=a*b*c*d
- Testing attribute presence: attr=*
- To combine several assertions using a logical OR, write: (|(uid=1000)(uid=1001)(uid=1002))
- Logical AND becomes "&", logical NOT looks like this: (!(uid=1000))
- On the wire, filters are represented differently
Three-valued logic ⌘
- An attribute value assertion may be true, false, or undefined
- The assertion is true if it is satisfied by at least one value of the attribute
- The assertion is undefined if the attribute is not in the schema, the asserted value is invalid, or something else does not make sense
- The assertion is false if the attribute is known, but none of the values satisfy it
Attribute specification ⌘
- Just name the wanted attributes
- Operational or not
- Or say "dn" if you only want the DN
- "*" means all non-operational attributes
- "+" means all operational attributes
Authentication ⌘
- The client supplies credentials during the "Bind" operation
- Anonymous bind: just that
- ldapsearch -x
- Simple bind: with DN and password (the common case)
- ldapsearch -x -D <dn> -W
- OpenLDAP will verify the password against the "userPassword" attribute of the given DN
- The attribute is multivalued, here is how to have multiple passwords per account
- SASL bind: think of it as "something complex" or "something external"
- See next slide
- Not commonly supported by clients
- Anonymous bind: just that
SASL Authentication ⌘
- Available mechanisms (selectable by -Y):
- LOGIN and PLAIN
- username (-U) and password (-W) sent in clear text
- no benefit over simple bind
- CRAM-MD5 and DIGEST-MD5
- password not sent in clear text
- but stored as clear text on the server (even worse!)
- DIGEST-MD5 is deprecated by RFC6331
- password not sent in clear text
- Kerberos (GSSAPI)
- Out of scope for this workshop
- EXTERNAL
- Usually means "uid+gid" on UNIX-domain sockets, or client TLS certificate
- Most commonly used with cn=config, to express the notion "root can edit config"
- LOGIN and PLAIN
Authentication in your application ⌘
- So, your application wants to authenticate users using LDAP
- This is typically a two-step process
- First, given a username, construct a full DN
- Use the application-specific service account and search for this user
- Alternatively, just blindly construct the DN if you know where all your users are
- Then, attempt to bind with the just-obtained DN and the user-entered password
- If the password is not empty, and the bind works, then the user has authenticated successfully
- Forgetting about the empty password is a common vulnerability — it sometimes results in a successful anonymous bind
- If the password is not empty, and the bind works, then the user has authenticated successfully
- There are applications (e.g. Magento) that employ different logic
- They keep their own password attribute that they know how to verify
- If it doesn't exist, they can try to bind as described
Password security ⌘
- Simple bind operation sends password in clear text
- That's OK over TLS (both legacy ldaps:// and ldap:// + STARTTLS)
- OK over a trusted wired network
- OK over UNIX-domain socket
- But on UNIX-domain sockets, we know the local user and thus can apply EXTERNAL SASL authentication
- SASL bind operation does not send password in clear text
- But often requires password storage in clear text
Importance of specifying base DN ⌘
- ldapsearch -b "" will find nothing
- The base DN must be within one of supported naming contexts
- The list of naming contexts can sometimes be autodiscovered:
- ldapsearch -H ldap://ldap.example.com -x -s base -b "" "*" "+"
- This entry is called rootDSE
- rootDSE = root DSA-specific Entry, DSA = Directory System Agent
- This entry is called rootDSE
- ldapsearch -H ldap://ldap.example.com -x -s base -b "" "*" "+"
Autodiscovery of the schema ⌘
- Mostly used by general-purpose clients and for debugging
- "cn=Subschema" comes from "subschemaSubentry: cn=Subschema" on the rootDSE
ldapsearch -o ldif-wrap=no -H ldap://ldap.example.com -x -s base -b 'cn=Subschema' \ -D cn=admin,dc=example,dc=com -W '*' '+'
LDIF ⌘
- LDAP Data Interchange Format
- Defined by RFC2849
- Describes both data and desired changes
- Used when importing data or performing modifications using command-line clients and administrator-friendly tools
- Does not travel over the wire
Adding new entries ⌘
- Use ldapadd
- Prepare a LDIF file containing the records
- Start each record with its DN
- Then list objectClass values
- Then list other attributes
- Terminate the record with a blank line
Deleting entries ⌘
- Use ldapdelete
- You can provide DNs on the command line or on standard input
Renaming entries ⌘
- Use ldapmodrdn
- Caveats
- It accepts the old DN and the new RDN on the command line
- Or in a file, as pairs of lines
- The old RDN is kept, use "-r" to remove it
- Use "-s newparentdn" to move the entry to the new parent
- It accepts the old DN and the new RDN on the command line
Modifications ⌘
- Separate kind of LDIF
- Accepted by ldapmodify
- Best illustrated by examples from RFC2849
- Yes, this can be used for adding new entries, deleting old ones, and renames as well
Deleting attributes and values ⌘
- This means "delete all descriptions":
dn: ... changetype: modify delete: description -
- This means "delete this particular description"
- Works only when there is a matching rule in the schema
dn: ... changetype: modify delete: description description: something misleading -
- Commonly-hit ldapvi bug: cannot delete single-valued attributes without a matching rule
- Save ldif, delete the line about the value manually, apply with ldapmodify
Common object classes ⌘
- dcObject (something to have "dc" on)
- country, organization, organizationalUnit (to represent structure)
- inetOrgPerson (to store information about people)
- simpleSecurityObject (stores a password)
- groupOfNames (LDAP way of representing groups, put DNs in the "member" attribute)
- Used in ACLs by OpenLDAP itself, or in applications such as NextCloud
- groupOfUniqueNames (less used variant)
- alias, referral (LDAP lookalikes of symlinks)
- extensibleObject (can store any attribute defined on the server)
- More in RFC4519
Extending the schema ⌘
- With slapd.conf, that's easy: just "include" the schema file
- With cn=config, you have to convert schema to ldif first
- For standard schema files, this has been done already
- For custom schema files, you have to convert them yourself using slaptest
- Here is a script
- See also the big comment at the top of /etc/openldap/schema/openldap.ldif
On empty groups ⌘
- RFC4519 says:
objectclass ( 2.5.6.9 NAME 'groupOfNames' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) )
- You see — at least one member is mandatory
- Empty groups do not exist according to the RFC
- They don't exist in OpenLDAP
- Some other LDAP servers violate the RFC and include "member" as "MAY"
- Common workarounds:
- Do the same RFC violation (not recommended)
- Create a dummy member (possibly of some silly objectClass)
- Use groupOfMembers (from RFC2307bis)
- Use namedObject (schema here)
- Do not create a group
On nested groups ⌘
- It is perfectly valid to add a group as a member of another group
- Common interpretation: all members of one group should be treated as members of another
- It's up to the client to interpret the situation like that
- This interpretation is optional
Aliases and referrals ⌘
- Similar to symlinks
- Aliases are for referencing entries on the same server
- Dereferenced by the server
- The objectClass is "alias", the attribute is "aliasedobjectname"
- Referrals are for redirectling clients to other servers
- Usually dereferenced by the client
- The objectClass is "referral", the attribute is "ref"
- In both cases, please use extensibleObject so that the naming attribute can be stored
Referrals and authentication ⌘
- The server returned a referral
- The client's credentials do not necessarily apply to the referred-to server
- There is no simple solution
Deleting referrals ⌘
- If you try it naively, you'll get a referral
- "Please delete it there"
- Obviously not what you mean
- Solution: manageDsaIT control
- Meaning: return referrals as normal objects
- ldapmodify (or ldapdelete) -M
- No special flag is needed to delete aliases
Using LDAP for Apache Basic Authentication ⌘
- authnz_ldap module must be loaded
- Enable TLS: LDAPTrustedMode TLS
- Finding the DN based on username
- If you have to search:
- do it as this DN: AuthLDAPBindDN dn
- use this password: AuthLDAPBindPassword password
- search according to this URL: AuthLDAPURL ldap://host:port/basedn?attribute?scope?filter
- The actual filter will be constructed as (&(<attribute>=...)(<filter>))
- The filter has to return exactly one dn
- If you want to construct the DN instead of searching:
- AuthLDAPInitialBindAsUser on
- AuthLDAPInitialBindPattern (.+) uid=$1,ou=...,
- If you have to search:
- Making the authorization decision
- Require valid-user
- Require ldap-user "John Doe" "..."
- Require ldap-group cn=...,ou=...
Keeping POSIX users and groups in LDAP ⌘
- Originally specified in RFC2307
- That's what comes with OpenLDAP in a schema file
- Then amended in a draft specification RFC2307bis
- Not fully adopted yet
- Expired in 2010
- And now there is also DBIS!
- Claims to be a successor of RFC2307 and RFC2307bis
- Has a design paper
- Not in Ubuntu
- Not supported by SSSD
- No idea who uses it
POSIX objectClasses ⌘
- posixAccount
- best practice: use uid as RDN
- AUXILIARY — use account as a structural objectClass
- Also combines well with inetOrgPerson
- shadowAccount
- combine with posixAccount
- posixGroup
- best practice: use cn as RDN
- STRUCTURAL in RFC2307, AUXILIARY in RFC2307bis
- combine with groupOfMembers, also defined in RFC2307bis
- other rarely-used classes
Evolution of NSS LDAP service ⌘
- Originally written as nss_ldap and pam_ldap by PADL Software
- Had a huge drawback: LDAP connections were opened from NSS libraries - which means "from any application"
- Unexpected file descriptors
- Interference with fork()
- Relied on nscd for caching
- Relied on pam_ccreds (of the same company) for disconnected operation
- Had a huge drawback: LDAP connections were opened from NSS libraries - which means "from any application"
- Cleaned up by Arthur de Jong, result: nss-pam-ldapd
- NSS and PAM modules query a central "nslcd" daemon which queries LDAP servers
- Still relies on nscd
- RedHat's SSSD project
- Supports caching, offline operation, authentication against AD
Writing custom schema ⌘
- Get a base OID for your organization or project from a legitimate source
- Get Private Enterprise Number from IANA, then use 1.3.6.1.4.1.X
- From your national authorities
- From thin air (using UUID)
- Plan how to use your OID space
- Reserve a branch for attributes, a branch for objectClasses
- Register your attribute/objectClass/matchingRule/syntax/etc names with IANA
- Use x-projectname- prefix if you don't yet want to do that
- Familiarize yourself with standard OIDs for booleans, integers, strings, etc.
- That's in RFC4517
- Use these numbers
LDAP URLs ⌘
- Encapsulate address, port, and search parameters
- ldap://host:port/basedn?attr1,attr2?scope?(filter)?extension1,extension2
- E.g. get CN of all groups under ou=Groups,dc=training,dc=com:
- ldap://training.com/ou=Groups,dc=training,dc=com?cn?sub?(objectClass=groupOfNames)
Server structure ⌘
- Global settings
- Logging
- TLS
- Loadable modules
- Backends representing different database types
- Databases representing different naming contexts
- including cn=config
- also there is cn=monitor
- and one HDB-backed database
- you can add another
- Overlays
- Provide additional functionality on top of databases
Configuration tree and objectClasses ⌘
- cn=config (objectClass: olcGlobal) - global options
- cn=module{X} (objectClass: olcModuleList) - loadable modules
- cn=schema (objectClass: olcSchemaConfig) - schema definition
- olcBackend=<databasetype> (objectClass: olcBackendConfig) - options specific to a backend type (not really used)
- olcDatabase=frontend (objectClass: olcFrontendConfig) - frontend database configuration
- olcDatabase={X}<databasetype> (objectClass: olcDatabaseConfig, objectClass: olc<databasetype>Config) - backend database configuration
- olcOverlay={X}<overlaytype> (objectClass: olcOverlayConfig, objectClass: olc<overlaytype>Config) - overlays
Logging ⌘
- cn=config
- olcLogLevel: <some integer> or <list of strings>
- Sum of flags representing what should be logged to syslog
- Example: olcLogLevel: 129
- Strings work just as well
- Example: olcLogLevel: stats sync
- Default: olcLogLevel: stats
- man slapd-config
TLS ⌘
- The modern way is to use StartTLS on port 389
- The obsolete (?) way is to use TLS on port 636
- Still handy for debugging certificate issues with openssl s_client
- And the only way to convince Thunderbird to encrypt its LDAP traffic (bugzilla entry)
- Obsolete? Not sure anymore. See Design Considerations from RFC 8314 (but that's about email, not LDAP)
- You will need a domain name, a server certificate, its chain, and the corresponding private key
- Two ways to deal with it:
- Self-signed (snakeoil) certificates, or private CAs
- Commercial certificates
- Let's try both
Create a snakeoil TLS certificate with OpenSSL ⌘
- We'll do it this way: CA signs end-entity certificates
- Well, in the real world, there is an intermediate CA certificate, but we'll skip it
# CA openssl genrsa -out rootCA.key 2048 openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
# Server openssl genrsa -out server.example.key 2048 openssl req -new -key server.example.key -out server.example.csr openssl x509 -req -in server.example.csr \ -CA rootCA.pem -CAkey rootCA.key -CAcreateserial \ -out server.example.crt -days 500 -sha256
- Even better tutorial exists, with intermediate certificate, CRL and OCSP
Get a trusted DV TLS certificate in 10 minutes ⌘
- Register a free 3rd-level domain specifically for this training here
- Create A or AAAA records for IPv4 or IPv6 addresses of your LDAP servers
- We need something externally accessible to verify the ownership of the domain
- No IPv6, and stuck with private IPv4 addresses? https://pkgs.org/download/miredo
- Install httpd with mod_ssl
- This will create a snakeoil certificate
- We need a web server for certbot
- Install certbot and python2-certbot-apache, so that we can get valid free TLS certificates from LetsEncrypt
- Create virtual hosts on one of the servers
- So that certbot has an idea which files to modify
- Run certbot --apache
- OK, now we have a valid certificate!
- And a private key, and a chain too! All in /etc/letsencrypt/live
Private key and certificate storage in CentOS 7 ⌘
- Please ignore this slide on other systems!
- CentOS 7, historically, stored certificates in nssdb, in /etc/openldap/certs
- Then they decided to switch to OpenSSL for maintainability reasons
- But they still need to support people who were using nssdb before
- Thus: convert nssdb to a directory with PEM files on startup
- By default, there is a snakeoil certificate in nssdb that we need to replace
- Recommendation: just store PEM files in /etc/openldap/certs
- Don't move them elsewhere, or SELinux may bite you
- After a suitable adjustment of slapd configuration, you can remove nssdb files
- Ignore old tutorials that mention pk12util and certutil, that's just extra unneeded complexity
- Recommendation: just store PEM files in /etc/openldap/certs
More on TLS trust flags ⌘
- With Mozilla NSS backend, when importing a CA certificate, these flags can be specified:
- T = trust as a CA that can sign TLS client certificates
- C = trust as a CA that can sign TLS server certificates
- Nice separation of authority
- Authoritative documentation
- With other backends, no such flags exist
- Your server CA can go rogue and issue a client certificate that you don't want!
- Solutions:
- Use your private CA that surely won't do that
- olcTLSVerifyClient: never
- ...which is the default, and which means that TLS client certificates are not used
Actually enabling TLS ⌘
- Modify the following attributes of cn=config
- olcTLSCACertificateFile
- either the nickname of the CA certificate in NSS DB (quoted if it contains spaces), or a filename with concatenated certificates in PEM format
- Multiple values should be separated by commas
- olcTLSCACertificatePath
- Directory with either OpenSSL-style CA certificates or Mozilla NSS database (used by other TLS options)
- olcTLSCertificateFile
- either the nickname of the server certificate in NSS DB or a path to a PEM file with such certificate
- olcTLSCertificateKeyFile
- Either a file with a password for the NSS DB, or a private key file in PEM format
- olcTLSCACertificateFile
- Options that mention files are still valid on CentOS
- "systemctl restart slapd" to apply new settings
Using TLS from ldapsearch ⌘
- ldapsearch -Z = try STARTTLS, but continue if it fails
- ldapsearch -ZZ = require TLS
- Set TLS_CACERT or TLS_CACERTDIR in /etc/openldap/ldap.conf
- Otherwise the root CA will be rejected as self-signed
- This also applies to non-command-line clients, such as PHP!
- And also to slapd's built-in LDAP replication client
SASL identity mangling ⌘
- SASL uses usernames or something like that - not DNs, so there is a mapping
- slapd initially constructs a DN of the form: uid=<username>,cn=<mechanism>,cn=auth
- Or, if the realm was also provided: uid=<username>,cn=<realm>,cn=<mechanism>,cn=auth
- Finally, the value of olcAuthzRegexp/authz-regexp is used to map this to the final DN
- If you can construct the final DN from the initial one using regular expressions:
- olcAuthzRegexp: uid=([^,]*),cn=.*,cn=auth dn:uid=$1,ou=People,dc=example,dc=com
- olcAuthzRegexp can have multiple values, you can add {X} in front of the value for sorting, the first matching value wins
- An alternative mechanism is olcAuthIDRewrite
- The other option is to search
- olcAuthzRegexp: uid=([^,]*),cn=.*,cn=auth ldap:///...$1...
- The filter must return exactly one entry
- The search is done as the authentication identity, ACLs apply, "auth" permission needed
Password-based SASL mechanisms ⌘
- LOGIN, PLAIN, DIGEST-MD5, CRAM-MD5
- Install cyrus-sasl library
- Install subpackages for SASL mechanisms that you would like to expose
- cyrus-sasl-plain, cyrus-sasl-md5, ...
- Configure identity mangling
- The mechanism would be "plain" or "login" or ...
- Restart slapd (changes DO NOT apply on-the-fly)
- Verify that supportedSASLMechanisms in rootDSE are correct
- Test the result
- ldapsearch -Y PLAIN -ZZ -H ldap://ldap.example.com/ -U user -b dc=example,dc=com
- -ZZ is needed because LOGIN and PLAIN are not exposed on non-SSL connections
Modules ⌘
- cn=module{X},cn=config
- X is an integer
- You don't specify the X when populating the database
- The only meaningful attributes are olcModulePath and olcModuleLoad
- Modules are loaded in batches
- Sorted according to X
- olcModuleLoad is multivalued, but new values cannot be added at runtime
- No such problem with slapd.conf
Backend evolution ⌘
- First, there was BDB
- Then, HDB (the default in most distributions)
- "H" stands for "hierarchical" (structure of internal data)
- Stores DNs more efficiently
- Supports subtree renames
- still needs tuning (next slide)
- "H" stands for "hierarchical" (structure of internal data)
- The new default is MDB
- Lightning Memory-Mapped Database
- not based on Berkeley DB
- needs no tuning at all - relies on page cache
HDB files ⌘
- Data files
- __db.001, __db.002, etc
- *.bdb
- Logs
- log.0000000001, etc
- may be needed for recovery, including point-in-time recovery
- useful for incremental backup
HDB tuning ⌘
- Maybe skip it and just use MDB?
- OK, DB_CONFIG in the directory with the database
- Defaults can be set in slapd.conf, using "dbconfig" option
- Ignored if DB_CONFIG already exists
- Crucial settings:
- cachesize (in entries, ideally should cover the whole working set, default = 1000)
- idlcachesize = 3 * cachesize
- dncachesize (default = unlimited, ideally should cover the whole database)
- checkpoint !!!
- Defaults can be set in slapd.conf, using "dbconfig" option
Don't forget to index your entries ⌘
- Indexes help when application uses them
- Any search not using indexes is logged
bdb_equality_candidates: (gidNumber) not indexed
- After any modifications of the "index" statement, rebuild the indexes
- systemctl stop slapd
- slapindex
- chown -R ldap:ldap /var/lib/openldap
- systemctl start slapd
If HDB complains about locks ⌘
- Just give it more
# Number of objects that can be locked at the same time. dbconfig set_lk_max_objects 1500 # Number of locks (both requested and granted) dbconfig set_lk_max_locks 1500 # Number of lockers dbconfig set_lk_max_lockers 1500
Changing DB_CONFIG settings ⌘
- Make sure they actually propagate into DB_CONFIG
- systemctl stop slapd
- Run db_recover in the directory with the database
- Fix ownership (should be ldap:ldap)
- systemctl start slapd
If slapd eats too much memory ⌘
- Check whether you have too high "ulimit -n"
- Was a common problem in Docker containers
- Switch to MDB backend
- Tune the cache size down (losing performance) for HDB if you can't use MDB
On checkpointing ⌘
- BDB checkpointing terminates the transaction
- Logs containing only completed transactions can be archived
- Read: removed
- dbconfig set_flags DB_LOG_AUTOREMOVE
- Makes recovery impossible
- By default, in CentOS checkpointing is disabled
- Consequence: logs cannot be removed, and will eat all disk space
- Berkeley DB loses all data since the last checkpoint if it runs out of disk space. Without checkpoints, this means "loses all data".
- No such trap in Debian and Ubuntu
- Consequence: logs cannot be removed, and will eat all disk space
- Automatic checkpointing is possible
- checkpoint <kbyte> <min>
Access control ⌘
- Default policy without ACL statements: only rootdn can perform updates, everyone (including anonymous) can read anything
- Including (hashed or possibly unhashed) passwords
- Debian and Ubuntu fix this by adding ACL statements that protect passwords
- ACLs can be added to backend databases, or to the frontend
- Frontend ACL is appended to backend
- olcAccess attribute or "access" statement in slapd.conf
What can be written to ⌘
- If you allow your users to write data, think about security
- Can they create additional (unauthorized) accounts with valid passwords?
- Can they set their group ID to "sudo" or user id to 0?
ACL syntax ⌘
- {X}access to <what> by <whom> <action> by <whom1> <action1> ...
- The first access line with a matching <what> is applied, unless there is a "break"
- Examples of "what":
- dn.sub=...
- attrs=...
- filter=(...)
- *
- Examples of "whom":
- anonymous, users, self
- self.level{1}, self.level{-1}
- dn.base=...
- group=...
- dnattr=... (use case: manager of a group)
- Examples of "action":
- none disclose auth compare search read write/add/delete manage
- Each privilege implies all preceding ones
- After an action, a "break" can be written
- Causes futher ACLs to be consulted
- none disclose auth compare search read write/add/delete manage
- man slapd.access
ACLs and nested groups ⌘
- Doable via the undocumented "set" feature
- Allow access to members of members of members... of "cn=that" group
- by set="[cn=that,...]/member* & user" write
- Allow access to manager of manager of manager... of the entry itself
- by set.expand="[$0]/manager* & user" manage
- or, by set="this/manager* & user" manage
- Allow access to members of members of members... of "cn=that" group
- $0 means "what's matched by <what>"
- * means "recursively expand"
Testing ACLs ⌘
- Use "slapacl"
- slapacl -b <to-what> -D <by-whom>
- On CentOS 7 with slapd.conf, add -f /etc/openldap/slapd.conf to prevent conversion
Overlays ⌘
- Provide additional functionality to a database
- Access log in the database itself
- Used by delta-syncrepl
- Audit log on disk
- Password policies
- Including the ability to lock out a user
- Chaining (i.e. expansion of referrals on the server)
- Arbitrary regex-based constraints on attribute values
- Attribute uniqueness checks
- Referential integrity
- Don't use together with access logging — will deadlock the server
- Maintenance of memberOf
- Dynamic lists based on the labeledURI attribute
- dynamic lists and memberOf interfere, don't use them together
- Multi-valued attribute value sorting
- Caching of remote backends
- Synchronization provider
- Access log in the database itself
- Order of overlays is important
memberOf ⌘
- We are talking about LDAP groups, not POSIX groups, here
- Normally, groups list DNs of their members in the "member" attribute
- It is possible to search for groups that have a certain member
- That's slow
- Some applications rely on the ability to get the list of such groups without a search
- They use the "memberOf" operational attribute
- It is not provided by default
- They use the "memberOf" operational attribute
- Overlay, by default, acts upon groupOfNames and the "member" attribute, can be reconfigured
moduleload memberof ... database ... overlay memberof
- Every group created before this module is enabled has to be deleted and restored from backup
unique ⌘
- Ensures that values of a certain attribute are unique
- Useful e.g. for POSIX user and group IDs
moduleload unique ... database ... overlay unique unique_uri ldap:///?uidNumber?sub?(objectClass=posixAccount) ldap:///?gidNumber?sub?(objectClass=posixGroup)
Referential integrity ⌘
- Some attributes reference other DNs
- If you delete these entries, dangling links are left by default
- The refint overlay cleans them up
moduleload refint ... database ... overlay refint refint_attributes memberof member manager owner
Replication ⌘
- Standards-based — described in RFC4533
- Object-based — retransmits the whole entry even if only one attribute changed
- Optimizations available in the form of Delta-syncrepl
- Two modes:
- refreshOnly (connects to the master periodically, downloads updates)
- A cookie is used to keep the knowledge what's already downloaded
- refreshAndPersist (connects, syncs the snapshot, gets notified about updates immediately)
- refreshOnly (connects to the master periodically, downloads updates)
- Master-master replication can be obtained by having two nodes replicate from each other
Replication: configuration on the master ⌘
- Need syncprov overlay
moduleload syncprov ... database ... overlay syncprov # How often to write the contextCSN from memory to database? # the first number is the number of operations, the second is time in minutes syncprov-checkpoint 100 10 # How many operations to log in memory? syncprov-sessionlog 1000
- Need an ACL that allows reading absolutely everything by some DN (used by slaves)
- For Delta-syncrepl, the accesslog overlay must be also enabled, and syncprov-reloadhint must be set to TRUE
Replication: configuration on the slave ⌘
- Need rootdn
- Need the long "syncrepl" option in the database
syncrepl rid=001 provider=ldap://master.example.com type=refreshAndPersist retry="5 5 300 +" searchbase="dc=example,dc=com" attrs="*,+" bindmethod=simple binddn="uid=replication,dc=example,dc=com" credentials=verylongpassword starttls=critical
- A good idea is to define updateref so that writes are directed to the master
Master-master replication ⌘
- Just two databases replicating from each other
- Both should have unique serverID
- Both should have mirrormode on
- Otherwise, both will be read-only
Rewriting content on the fly ⌘
- rwm overlay
- Experimental
- Can remap objectClass and attribute names
- Can change DN suffix
- Can perform other regular-expression based rewriting
- Has been successfully used for translation between ActiveDirectory and RFC2307 schema