Skip to content

LDAP and Active Directory

LDAP (Lightweight Directory Access Protocol) is the protocol used to query and modify directory services. Active Directory (AD) is Microsoft’s directory service that uses LDAP as its access protocol. Understanding both is essential for enterprise authentication, authorisation, and identity management.


A directory service is a hierarchical database optimised for reads, storing information about network objects - users, computers, groups, printers, and services.

Directory Information Tree (DIT):
Root DSE
DC=example,DC=com ← Domain Component (DNS namespace)
┌──────┴──────┐
OU=Users OU=Computers
│ ┌────┐ │ ┌────────┐
│ │CN= │ │ │CN= │
│ │alice│ │ │WS001 │
│ └────┘ │ └────────┘
│ │
CN=bob CN=SRVWEB01

Key terminology:

TermMeaningExample
DN (Distinguished Name)Unique path to an objectCN=alice,OU=Users,DC=example,DC=com
CN (Common Name)Object’s name within its parentalice, SRVWEB01
OU (Organizational Unit)Container for organising objectsOU=Users, OU=Finance
DC (Domain Component)DNS domain labelDC=example, DC=com
RDN (Relative Distinguished Name)Single component of a DNCN=alice
objectClassLDAP schema typeperson, inetOrgPerson, computer
AttributeKey-value property of an objectmail: [email protected]

LDAP operates over TCP port 389 (plaintext) and 636 (LDAPS - TLS). Port 3268/3269 are Global Catalog ports in AD.

OperationPurpose
BindAuthenticate to the directory server
SearchQuery the directory with filters
AddCreate a new entry
ModifyChange attributes of an existing entry
DeleteRemove an entry
ModifyDNRename or move an entry
UnbindClose the connection
Basic filters:
(attribute=value) → exact match
(attribute=val*) → prefix match (wildcard)
(attribute=*val*) → contains match
(attribute=*) → attribute present (any value)
(!(attribute=value)) → NOT match
Compound:
(&(filter1)(filter2)) → AND
(|(filter1)(filter2)) → OR
Common filters:
(objectClass=person) → all person objects
(objectClass=user) → all AD user objects
(sAMAccountName=alice) → AD user with login name "alice"
(&(objectClass=user)(enabled=TRUE)) → enabled users only
(memberOf=CN=Admins,OU=Groups,DC=...) → members of Admins group
(mail=*@example.com) → users with an @example.com email

Terminal window
# Install LDAP client tools
apt install ldap-utils # Debian/Ubuntu
# Anonymous bind + search (many directories allow anonymous reads)
ldapsearch -x -H ldap://ldap.example.com -b "dc=example,dc=com" "(objectClass=person)"
# Authenticated bind (simple bind - sends credentials over connection)
ldapsearch -x -H ldap://ldap.example.com \
-D "cn=admin,dc=example,dc=com" \
-w 'password' \
-b "dc=example,dc=com" \
"(objectClass=user)"
# Use LDAPS (TLS) - port 636
ldapsearch -x -H ldaps://ldap.example.com \
-D "cn=admin,dc=example,dc=com" -W \
-b "dc=example,dc=com" "(objectClass=person)"
# SASL/GSSAPI bind (Kerberos ticket - for AD with Kerberos)
ldapsearch -Y GSSAPI -H ldap://dc.example.com \
-b "dc=example,dc=com" "(objectClass=user)"
Terminal window
BASE="dc=example,dc=com"
DC="ldap://dc.example.com"
AUTH="-D 'cn=ldapread,ou=svc,dc=example,dc=com' -W"
# Find a specific user
ldapsearch -x -H $DC $AUTH -b "$BASE" "(sAMAccountName=alice)" \
dn mail displayName userAccountControl
# List all users in an OU
ldapsearch -x -H $DC $AUTH -b "ou=Users,$BASE" "(objectClass=user)" sAMAccountName mail
# Find disabled accounts (userAccountControl bit 2 = disabled)
ldapsearch -x -H $DC $AUTH -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2))" \
sAMAccountName displayName
# Find users with no password expiry (userAccountControl bit 65536)
ldapsearch -x -H $DC $AUTH -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))" \
sAMAccountName
# Find all computer objects
ldapsearch -x -H $DC $AUTH -b "$BASE" "(objectClass=computer)" \
cn operatingSystem lastLogonTimestamp
# List members of a group
ldapsearch -x -H $DC $AUTH -b "$BASE" \
"(cn=Domain Admins)" member
# Find all groups a user is member of
ldapsearch -x -H $DC $AUTH -b "$BASE" \
"(sAMAccountName=alice)" memberOf
# Find service accounts (usually have specific naming conventions)
ldapsearch -x -H $DC $AUTH -b "ou=Service Accounts,$BASE" \
"(objectClass=user)" sAMAccountName description
Terminal window
# Add a user to a group (LDIF format)
ldapmodify -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'
dn: cn=Developers,ou=Groups,dc=example,dc=com
changetype: modify
add: member
member: cn=alice,ou=Users,dc=example,dc=com
EOF
# Remove from group
ldapmodify -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'
dn: cn=Developers,ou=Groups,dc=example,dc=com
changetype: modify
delete: member
member: cn=alice,ou=Users,dc=example,dc=com
EOF
# Change a user attribute
ldapmodify -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'
dn: cn=alice,ou=Users,dc=example,dc=com
changetype: modify
replace: mail
EOF
# Add a new user entry (LDIF)
ldapadd -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'
dn: cn=bob,ou=Users,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
cn: bob
sn: Smith
uid: bob
uidNumber: 1002
gidNumber: 1000
homeDirectory: /home/bob
loginShell: /bin/bash
userPassword: {SSHA}hashedpasswordhere
EOF

Active Directory extends LDAP with:

  • Kerberos authentication (see aaa-authentication-authorization-accounting.mdx)
  • Group Policy Objects (GPOs) - policy pushed to domain-joined machines
  • Trusts - allow users in one domain to access resources in another
  • Forests and domains - organizational boundaries for security and policy
AttributeAD nameDescription
UsernamesAMAccountNameLegacy login name (used for NTLM/older auth)
Login nameuserPrincipalName[email protected] (UPN - modern)
Account controluserAccountControlBitmask for enabled/disabled/never expires/etc
Last logonlastLogonTimestampReplicated; updated every ~14 days
Password setpwdLastSetWhen password was last changed
Account createdwhenCreatedObject creation timestamp
Group membershipmemberOfDNs of groups the user belongs to
ManagermanagerDN of user’s manager
Terminal window
# Import Active Directory module
Import-Module ActiveDirectory
# --- User queries ---
# Get a specific user
Get-ADUser -Identity alice -Properties *
# All users in an OU
Get-ADUser -Filter * -SearchBase "OU=Users,DC=example,DC=com" |
Select-Object SamAccountName, EmailAddress, Enabled
# Disabled accounts
Get-ADUser -Filter {Enabled -eq $false} -SearchBase "OU=Users,DC=example,DC=com" |
Select-Object SamAccountName, DistinguishedName
# Users who haven't logged in for 90 days
$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter {LastLogonDate -lt $cutoff -and Enabled -eq $true} |
Select-Object SamAccountName, LastLogonDate | Sort-Object LastLogonDate
# Users with password never expires
Get-ADUser -Filter {PasswordNeverExpires -eq $true} |
Select-Object SamAccountName
# --- Group operations ---
Get-ADGroup -Identity "Domain Admins" -Properties Members
Get-ADGroupMember -Identity "Domain Admins" | Select-Object SamAccountName
Get-ADPrincipalGroupMembership alice | Select-Object Name
Add-ADGroupMember -Identity "Developers" -Members "alice"
Remove-ADGroupMember -Identity "Developers" -Members "alice"
# --- Account management ---
New-ADUser -Name "Bob Smith" -SamAccountName bob -UserPrincipalName bob@example.com `
-Path "OU=Users,DC=example,DC=com" -AccountPassword (Read-Host -AsSecureString "Password") `
-Enabled $true
Disable-ADAccount -Identity alice
Enable-ADAccount -Identity alice
Set-ADAccountPassword -Identity alice -Reset -NewPassword (Read-Host -AsSecureString "NewPwd")
Unlock-ADAccount -Identity alice # unlock after lockout
# --- Computer objects ---
Get-ADComputer -Filter * -Properties OperatingSystem, LastLogonDate |
Select-Object Name, OperatingSystem, LastLogonDate | Sort-Object LastLogonDate -Descending
# Stale computer accounts (not logged in for 90 days)
Get-ADComputer -Filter {LastLogonDate -lt $cutoff} | Select-Object Name, LastLogonDate
Terminal window
# List all GPOs
Get-GPO -All | Select-Object DisplayName, GpoStatus, CreationTime
# Show which GPOs are linked to an OU
Get-GPInheritance -Target "OU=Users,DC=example,DC=com"
# View GPO settings report (HTML)
Get-GPOReport -Name "Security Baseline" -ReportType HTML -Path "C:\gpo-report.html"
# Force GPO update on a remote computer
Invoke-GPUpdate -Computer WORKSTATION01 -Force
# Check resultant set of policy for a user/computer (what's actually applied)
gpresult /Scope User /r # cmd: shows applied GPOs for current user
Get-GPResultantSetOfPolicy -ReportType HTML -Path "C:\rsop.html"
Terminal window
# Find members of privileged groups (audit regularly)
$privilegedGroups = "Domain Admins","Enterprise Admins","Schema Admins","Administrators"
foreach ($group in $privilegedGroups) {
Write-Host "=== $group ==="
Get-ADGroupMember -Identity $group -Recursive | Select-Object SamAccountName, ObjectClass
}
# Find accounts with Kerberoastable SPNs (common attack target)
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
Select-Object SamAccountName, ServicePrincipalName
# Find accounts with AS-REP Roasting vulnerability (no Kerberos pre-auth)
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} | Select-Object SamAccountName
# Find unconstrained delegation (dangerous - allows any service impersonation)
Get-ADComputer -Filter {TrustedForDelegation -eq $True} | Select-Object Name, DNSHostName
# Find accounts with AdminCount=1 but NOT in admin groups (leftover admin rights)
Get-ADUser -Filter {AdminCount -eq 1} | Where-Object {
(Get-ADPrincipalGroupMembership $_) -notmatch "Admins"
}

Terminal window
# Install OpenLDAP server
apt install slapd ldap-utils
# Reconfigure (set domain and admin password)
dpkg-reconfigure slapd
# Verify service
ldapwhoami -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W
# Load an LDIF file (e.g., create OUs, import users)
ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f structure.ldif
# Backup OpenLDAP database
slapcat -n 1 > /backup/ldap-backup.ldif # export to LDIF
# Restore
slapadd -n 1 -F /etc/ldap/slapd.d < /backup/ldap-backup.ldif
# Check OpenLDAP configuration
slaptest -F /etc/ldap/slapd.d/ -v # validate configuration
Terminal window
# /etc/ldap/ldap.conf - client TLS settings
TLS_CACERT /etc/ssl/certs/ca-cert.pem
TLS_REQCERT demand # demand = require valid cert (don't use 'never' in production)
# OpenLDAP server TLS config (/etc/ldap/slapd.d)
# Add via ldapmodify:
ldapmodify -H ldapi:// -Y EXTERNAL << 'EOF'
dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/ca-cert.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/ldap-server.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/ldap-server.key
EOF
# Test LDAPS connection
openssl s_client -connect ldap.example.com:636 </dev/null 2>/dev/null \
| openssl x509 -noout -subject -dates
ldapwhoami -H ldaps://ldap.example.com -x -D "cn=admin,dc=example,dc=com" -W

Terminal window
# Join a Linux host to an AD domain using realmd + sssd
apt install realmd sssd sssd-tools adcli
# Discover the domain
realm discover example.com
# Join the domain (requires AD admin credentials)
realm join example.com -U Administrator
# Verify join
realm list # shows joined domain and permitted logins
id [email protected] # should return UID/GID from AD
# Allow specific AD groups to log in
realm permit -g "Linux [email protected]"
# Configure sudo for AD group (/etc/sudoers.d/ad-admins)
echo '%linux\ [email protected] ALL=(ALL:ALL) ALL' > /etc/sudoers.d/ad-admins
# sssd configuration (/etc/sssd/sssd.conf)
[sssd]
services = nss, pam
domains = example.com
[domain/example.com]
id_provider = ad
auth_provider = ad
access_provider = ad
ad_domain = example.com
krb5_realm = EXAMPLE.COM
realmd_tags = manages-system joined-with-adcli
# Restart and verify
systemctl restart sssd
sssctl domain-status example.com