Lightweight Directory Access Protocol. An LDAP server basically is a non-relational database which is optimised for accessing, but not writing, data. It is mainly used as an address book (for e.g. email clients) or authentication backend to various services (such as Samba, where it is used to emulate a domain controller, or Linux system authentication, where it replaces /etc/passwd) and basically holds the user data.
Schematic structure
mindmap
root((Base 'dc=local,dc=lan'))
Container object 'Distinquished Name: ou=users,dc=local,dc=lan'
Leaf object 'Distinquished Name: cn=bob,ou=users,dc=local,dc=lan'
Object class 'posixAccount'
Attribute 'uidNumber'
Attribute value of an attribute type '1007'
Leaf object
Container object
Leaf object
Leaf object
1
2
3
4
5
6
7
8
9
| objectclass (
1.3.6.1.1.1.2.0
NAME 'posixAccount'
DESC 'Abstraction of an account with POSIX attributes'
SUP top
AUXILIARY
MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
MAY ( userPassword $ loginShell $ gecos $ description )
)
|
This is the bare minimum to accomplish that OpenLDAP Linux Clients “talks” in an Active-Directory environment.
Installation
1
| apt install slapd ldap-utils ldapscripts nslcd
|
If you missed inputs or mistyped anything you can reconfigure slapd this way.
1
| dpkg-reconfigure -p low slapd
|
Configuration
You can use ldapadd to add entries to your database, but this can be very confusing. Another way is to add your base structure via a file, e.g. called base.ldif.
Building the base structure
Edit base.ldif from anywhere it doesn’t matter and put the following contents inside.
1
2
3
4
5
6
7
8
9
10
11
| dn: ou=users,dc=local,dc=lan
objectClass: organizationalUnit
ou: users
dn: ou=groups,dc=local,dc=lan
objectClass: organizationalUnit
ou: groups
dn: ou=hosts,dc=local,dc=lan
objectClass: organizationalUnit
ou: hosts
|
We now have local.lan as base and inside 3 groups named users, groups and hosts.
Now add this file as base structure to LDAP:
1
| ldapadd -x -D cn=admin,dc=local,dc=lan -w P@ssw0rd -f base.ldif
|
| parameter | description |
|---|
| -x | Use simple authentication |
| -D | bind Distinguished Name (DN) |
| cn=admin […] | DN is like your admin user created on installation |
| -w | bind password for simple authentication |
| -W | Prompt for a bind password |
| -f | read order of operations from file |
The Output should look as following:
1
2
3
4
5
| adding new entry "ou=users,dc=local,dc=lan"
adding new entry "ou=groups,dc=local,dc=lan"
adding new entry "ou=hosts,dc=local,dc=lan"
|
Usage
From hard to easy. :-)
Searching the LDAP-Tree
1
| ldapsearch -x -H ldap://localhost -b "dc=local,dc=lan" -LLL
|
| parameter | description |
|---|
| -x | simple Authentication |
| -H | LDAP URI (Uniform Resource Identifier) |
| -b | Base DN for search |
| -LLL | print responses in LDIF format without comments and version |
Manually adding groups
Create a file called add-new-group.ldif.
1
2
3
4
| dn: cn=it,ou=groups,dc=local,dc=lan
objectClass: posixGroup
cn: it
gidNumber: 3001
|
This order of operation creates a new group it in the Organizational Unit groups with the group ID 3001.
| variable | description |
|---|
| cn | Common Name: name of the entry; don’t make it harder than it is and don’t use any kind of special character |
| gidNumber | Unique group ID This also must not get in conflict with the local /etc/passwd. |
1
| ldapadd -x -D cn=admin,dc=local,dc=lan -w P@ssw0rd -f add-new-group.ldif
|
Output:
1
| adding new entry "cn=it,ou=groups,dc=local,dc=lan"
|
Manually adding users
Create a file called add-new-user.ldif.
1
2
3
4
5
6
7
8
9
10
| dn: uid=bob,ou=users,dc=local,dc=lan
cn: bob
objectClass: account
objectClass: posixAccount
objectClass: shadowAccount
uid: leroy
uidNumber: 2001
gidNumber: 3001
homeDirectory: /home/leroy
loginShell: /usr/bin/bash
|
This order of operation creates a new user bob in the Organizational Unit users with the user ID 2001. He’s also member of the group with the ID 3001 ( it ). His Home directory is /home/leroy and his shell to use is /usr/bin/bash.
| value | description |
|---|
| cn | Common Name: name of the entry; don’t make it harder than it is and don’t use any kind of special character |
| gidNumber | Similar to Active Directory’s “Member of” |
1
| ldapadd -x -D cn=admin,dc=local,dc=lan -w P@ssw0rd -f add-new-user.ldif
|
Output:
1
| adding new entry "uid=bob,ou=users,dc=local,dc=lan"
|
Manually add user to group
Create a file called add-user-to-group.ldif
1
2
3
4
| dn: cn=it,ou=groups,dc=local,dc=lan
changetype: modify
add: memberUid
memberUid: alice
|
1
| ldapmodify -x -D "cn=admin,dc=local,dc=lan" -W -f add-user-to-group.ldif
|
Troubleshooting
Rebuild LDAP:
1
2
| dpkg-reconfigure -p low slapd
dpkg-reconfigure nslcd
|
1
2
3
| systemctl enable slapd
systemctl enable nslcd
systemctl enable nscd
|
1
| systemctl ( start | stop | restart | status ) nslcd
|
Binding clients to a domain
Add clients or server to your LDAP domain.
Installation
1
| apt install nslcd ldap-utils
|
Configuration
During installation you would be asked for which groups to add. Choose passwd, group and shadow.
Edit /etc/nslcd.conf like this:
1
2
3
4
5
| uid nslcd
gid nslcd
uri ldap://ldap.local.lan
base dc=local,dc=lan
tls_cacertfile /etc/ssl/certs/ca-certificates.crt
|
After that restart nslcd service: systemctl restart nslcd.service
Edit /etc/nsswitch.conf like this:
1
2
3
4
5
6
7
8
9
| # /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.
passwd: files systemd ldap
group: files systemd ldap
shadow: files ldap
|
After that restart nslcd service: systemctl restart nscd.service
Usage
Check if you have a successful connection with:
1
2
| getent passwd
getent group
|
LDAP DNS SRV Record
LDAP clients should be able to automatically find a connection to the LDAP server of their respective domain via SRV records. The syntax of the SRV records generally follows the syntax _servicename._tcp/udp.domain.tld.
DNS server configuration
Add this to your local forward zone file in /var/cache/bind/local.lan:
1
2
3
4
| ldap1_server_hostname IN A 10.12.0.2
ldap2_server_hostname IN A 10.12.0.3
_ldap._tcp IN SRV 10 100 389 ldap1_server_hostname
_ldap._tcp IN SRV 20 100 389 ldap2_server_hostname
|
| value | description |
|---|
| 10 | Priority of this server, the lower the value the higher the priority |
| 100 | Answer 100 % of the requests |
| 389 | Port for ldap requests |
LDAP client configuration
Edit /etc/nsswitch.conf:
1
2
3
| passwd: files systemd ldap
group: files systemd ldap
shadow: files systemd ldap
|
In order for nslcd to search for SRV records of the current domain /etc/resolv.conf and /etc/nslcd.conf must be coordinated.
1
2
| domain local.lan # domain.tld
nameserver ip.of.dns.server
|
1
2
3
| [...]
uri DNS
base dc=domain,dc=tld
|
Usage
You can now check if DNS works correctly by just dig domain.tlp.
Or you use ping.
Your DNS server should answer with the IP of your LDAP server.
LDAPscripts
The filling of such a database manually is actually not comfy, but it can be easily automated, there are already scripts that perform exactly this task. For the scripts to work, however, the basic structure must be there.
Configuration
Edit /etc/ldapscripts/ldapscripts.conf.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| SERVER="ldap://localhost"
SUFFIX="dc=local,dc=lan"
GSUFFIX="ou=groups"
USUFFIX="ou=users"
MSUFFIX="ou=hosts"
BINDDN="cn=admin,dc=local,dc=lan"
USHELL="/bin/bash"
UHOMES="/home/%u"
CREATEHOMES="yes"
HOMESKEL="/etc/skel"
BINDPWDFILE="/etc/ldapscripts/ldapscripts.passwd"
GIDSTART="10000" # Group ID
UIDSTART="10000" # User ID
MIDSTART="20000" # Machine ID
GCLASS="posixGroup" # Leave "posixGroup" here if not sure !
PASSWORDGEN="pwgen"
RECORDPASSWORDS="yes"
PASSWORDFILE="/var/log/ldapscripts_passwd.log"
LOGTOFILE="yes"
LOGFILE="/var/log/ldapscripts.log"
LOGTOSYSLOG="no"
SYSLOGFACILITY="local4"
SYSLOGLEVEL="info"
LDAPSEARCHBIN="/usr/bin/ldapsearch"
LDAPADDBIN="/usr/bin/ldapadd"
LDAPDELETEBIN="/usr/bin/ldapdelete"
LDAPMODIFYBIN="/usr/bin/ldapmodify"
LDAPMODRDNBIN="/usr/bin/ldapmodrdn"
LDAPPASSWDBIN="/usr/bin/ldappasswd"
LDAPSEARCHOPTS="-o ldif-wrap=no"
GETENTPWCMD=""
GETENTGRCMD=""
GTEMPLATE=""
UTEMPLATE=""
MTEMPLATE=""
|
Additionally you have to save the LDAP admin password in the /etc/ldapscripts/ldapscripts.passwd file.
1
| echo -n "P@ssw0rd" > /etc/ldapscripts/ldapscripts.passwd
|
The password must be stored without the new-line character. Therefore the parameter -n is set.
Usage
Add a new group.
1
| Successfully added group it to LDAP
|
Add a new user.
1
2
| ldapadduser bob it
ldapadduser alice it
|
1
2
3
4
5
| Successfully added user bob to LDAP
Successfully set password for user bob
Successfully added user bob to LDAP
Successfully set password for user alice
|
You have to manually set a new password for former created users.
1
2
3
| New Password:
Retype New Password:
Successfully set password for user uid=alice,ou=users,dc=local,dc=lan
|
This command just overwrites the users password.
Testing
On the LDAP server or any client you see if LDAP works correctly if you query the passwd or groups for example.
getent means Get entities.
1
2
3
4
5
6
7
| [...]
user:x:1000:1000:xinux,,,:/home/user:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
tcpdump:x:106:113::/nonexistent:/usr/sbin/nologin
openldap:x:107:114:OpenLDAP Server Account,,,:/var/lib/ldap:/bin/false
bob:*:10000:10000:leroy:/home/bob:/bin/bash
alice:*:10000:10000:leroy:/home/alice:/bin/bash
|
You should see the new user id’s starting at 10000 like configured in ldapscripts.conf.
1
2
3
4
5
6
| [...]
user:x:1000:
systemd-coredump:x:999:
tcpdump:x:113:
openldap:x:114:
it:*:10001:
|
You should see the new group id’s starting also at 10000 like configured in ldapscripts.conf.