linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
Index
Categories
All entries
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
home/linux/kb/Protocols/ldap-basics

kb/protocols ── Protocols ── intermediate

LDAP: directory services fundamentals

LDAP is a query against a hierarchical directory. A DN is the coordinate of an object (cn=user,ou=People,dc=example,dc=com), bind is authentication, schema defines object classes and attributes. OpenLDAP/389-DS on Linux.

view as markdownaka: ldap, openldap, ds-389, ldap-search, active-directory-ldap

Why LDAP

Lightweight Directory Access Protocol (RFC 4511) is the standard for access to a hierarchical directory: users, groups, hosts, certificates, application configs. It appeared in the 1990s as a simplification of X.500. Today it is used for:

  • Centralized auth: Active Directory (Microsoft), FreeIPA (Red Hat), OpenLDAP / 389-DS (linux-only)
  • Address book for email servers (ou=People for autocomplete in Outlook/Thunderbird)
  • Application configs and DNS data in a single tree
  • Storage of user SSH keys (with patches/openssh-ldap)

Not a general-purpose database. Reads are fast, writes are rare. It is optimized for "many reads, few writes".

Hierarchical structure

A tree of objects (DIT, Directory Information Tree):

dc=example,dc=com                      ← root = domain
├─ ou=People
│   ├─ uid=alice         (objectClass: posixAccount, inetOrgPerson)
│   ├─ uid=bob
│   └─ uid=carol
├─ ou=Groups
│   ├─ cn=admins
│   └─ cn=developers
└─ ou=Services
    └─ cn=ldap-replica
  • DN (Distinguished Name), the full "coordinate": uid=alice,ou=People,dc=example,dc=com
  • RDN (Relative DN), the last component: uid=alice
  • dc (domainComponent), ou (organizationalUnit), uid, cn (commonName), the attributes that form the DN

Read right to left: dc=com → dc=example → ou=People → uid=alice.

Bind, authentication

Before issuing queries the client performs a bind:

  • Anonymous bind, no credentials, access only to public data
  • Simple bind, DN + password in plaintext (requires TLS!)
  • SASL bind, via Kerberos/EXTERNAL/DIGEST-MD5/PLAIN
bash
# Anonymous (for tests)
ldapsearch -x -H ldap://ldap.example.com -b "dc=example,dc=com" "(uid=alice)"
# Simple bind
ldapsearch -x -D "uid=alice,ou=People,dc=example,dc=com" -W \
           -H ldaps://ldap.example.com -b "dc=example,dc=com" "(objectClass=*)"
# SASL Kerberos (when a TGT is present)
ldapsearch -Y GSSAPI -H ldap://ldap.example.com -b "dc=example,dc=com" "(uid=alice)"
  • -x, simple bind
  • -D, bind DN
  • -W, prompt for the password interactively
  • -H, URL (ldap://, ldaps://)
  • -Y, SASL mechanism

Objects and attributes

Every object has one or more objectClass values (defined by the schema), which set the required and optional attributes:

ldif
dn: uid=alice,ou=People,dc=example,dc=com
objectClass: top
objectClass: posixAccount             # uidNumber, gidNumber, homeDirectory
objectClass: inetOrgPerson            # mail, sn, givenName, telephoneNumber
objectClass: shadowAccount            # shadowLastChange and the like
uid: alice
cn: Alice Wonderland
sn: Wonderland
givenName: Alice
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/alice
loginShell: /bin/bash
mail: alice@example.com
userPassword: {SSHA}xxxx              # hashed

The main classes for a user on Linux:

  • posixAccount, uid/gid, so that NSS+PAM can use the account
  • inetOrgPerson, mail, name
  • shadowAccount, password expiry

LDAP filters

Search uses an LDAP Filter (RFC 4515):

(uid=alice)                          # exact match
(cn=Alice*)                          # wildcard
(mail=*@example.com)                 # suffix
(&(uid=alice)(memberOf=cn=admins,ou=Groups,dc=example,dc=com))   # AND
(|(uid=alice)(uid=bob))              # OR
(!(loginShell=/bin/false))           # NOT
(uidNumber>=1000)                    # >= (int only)
(objectClass=posixAccount)

Every special character in a value must be escaped (\28 for ().

ldapsearch, the core utility

bash
# All posixAccount entries with uidNumber >= 1000
ldapsearch -x -H ldaps://ldap -b "dc=example,dc=com" \
           "(&(objectClass=posixAccount)(uidNumber>=1000))" uid uidNumber cn
# Only specific attributes
ldapsearch ... "(uid=alice)" mail telephoneNumber
# EVERYTHING present on the object
ldapsearch ... "(uid=alice)" "+"                # operational attributes
# Size of the tree
ldapsearch -x -H ldap://srv -b "" -s base "(objectclass=*)"  # rootDSE

LDIF, the export/import format

LDIF (LDAP Data Interchange Format) is a text format:

ldif
dn: ou=Test,dc=example,dc=com
changetype: add
objectClass: organizationalUnit
ou: Test
dn: uid=alice,ou=Test,dc=example,dc=com
changetype: add
objectClass: posixAccount
objectClass: inetOrgPerson
uid: alice
cn: Alice
sn: Wonderland
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/alice
bash
ldapadd -D "cn=admin,dc=example,dc=com" -W -f users.ldif
ldapmodify -f changes.ldif
ldapdelete "uid=alice,ou=People,dc=example,dc=com"

OpenLDAP vs 389-DS vs FreeIPA vs AD

TraitOpenLDAP (slapd)389-DSFreeIPAActive Directory
Default onDebian/UbuntuRHELRHEL/FedoraWindows Server
Configcn=config (LDIF)dse.ldif + consoleweb UI + CLIWindows GUI
Replicationmirror/N-way multimastermulti-suppliersuppliers + RO replicasDC + RODC
Kerberosseparate (MIT)possiblebuilt in + DNS + Kerberos + CAbuilt in
Setup difficultymediummediumlow (all-in-one)low (on Windows)
Linux clientssssd / nss-ldapsssdsssd (native integration)sssd via realmd

For a new self-hosted deployment on Linux, use FreeIPA (if RHEL/Fedora) or 389-DS standalone. OpenLDAP is legacy but still widespread.

TLS, mandatory

Without TLS, simple-bind passwords travel in plaintext and tcpdump will grab them. The options:

  • ldaps:// on 636, implicit TLS
  • STARTTLS on 389, opportunistic, though the client may not request it

A modern server should accept only ldaps:// or forbid simple-bind without STARTTLS:

ldif
# OpenLDAP
olcSecurity: ssf=128

When something goes wrong

  • Invalid credentials (49), wrong DN or password. It also happens when the entry has no userPassword at all.
  • Insufficient access rights (50), bind is fine, but an ACL rule forbids the read or write. Check olcAccess.
  • Server is unwilling to perform (53), the operation is forbidden by config (for example a write on a read-only replica).
  • Constraint violation (19), the password failed the pwd-policy (length, history).
  • Object class violation (65), you add an attribute that is not in the objectClass values, or you delete a required one.
  • Painfully slow search, no index on the attribute. Run slapindex after olcDbIndex: cn,uid eq.
  • TLS handshake fails, old ciphers on one side or a self-signed CA missing from the client trust store.

Alternatives and related

  • sssd, the Linux client, caches LDAP/AD/Kerberos for NSS+PAM
  • nslcd / nss-ldap, the old client, deprecated in favor of sssd
  • Apache Directory Studio, a GUI for browsing and editing the tree
  • OpenID Connect / OAuth2, modern user-facing SSO (applications move away from LDAP authentication toward OIDC, but LDAP remains as the backing user store)

§ команды

bash
ldapsearch -x -H ldaps://ldap -b 'dc=example,dc=com' '(uid=alice)' mail uidNumber

Search for a specific user, limited set of attributes

bash
ldapsearch -x -H ldap://srv -b '' -s base '+'

rootDSE, metadata about the server (SASL support, TLS, naming contexts)

bash
ldapadd -x -D 'cn=admin,dc=example,dc=com' -W -f users.ldif

Import LDIF, a typical bulk-add

bash
ldapmodify -x -D 'cn=admin,dc=example,dc=com' -W <<EOF\ndn: uid=alice,ou=People,dc=example,dc=com\nchangetype: modify\nreplace: mail\nmail: alice@new.com\nEOF

Modify an attribute via heredoc, faster than editing an LDIF file

bash
slapcat -n 1 > backup.ldif

Back up OpenLDAP database N1 to LDIF, the offline method

bash
getent passwd alice

NSS check, the LDAP user is visible to the OS (if sssd/nss-ldap are configured)

bash
openssl s_client -connect ldap.example.com:636 -showcerts

Check the TLS certificate of the LDAPS server, debug handshake problems

§ см. также

  • pamPAM: Pluggable Authentication ModulesPAM is the authentication framework in Linux. Programs (sudo, login, sshd) do not check passwords themselves. They call PAM, which decides whether to let you in through a stack of modules in `/etc/pam.d/<service>`.
  • kerberosKerberos: network single sign-onKerberos is an SSO system built on tickets and time-based cryptography. You enter your password once (`kinit`) and get a TGT. After that the KDC issues service tickets in exchange for it. Core of AD and FreeIPA.
  • smtp-mtaSMTP: MTA and Email DeliverySMTP is a text-based mail delivery protocol. Port 25/tcp is server-to-server, 587 is submission (client with auth), 465 is implicit-TLS legacy. MX record in DNS, STARTTLS+SPF+DKIM+DMARC is the standard stack.
  • tls-handshakeTLS HandshakeTLS is the encryption layer above TCP. Before data flows, both sides run a handshake: they exchange keys, verify the certificate, and agree on a cipher.
  • sshSSH: Secure ShellSSH is an encrypted channel to a remote host: shell, file copy, port-forwarding. Standard port 22, authentication by keys or password.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies