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.
Directory Services - Concepts
Section titled “Directory Services - Concepts”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=SRVWEB01Key terminology:
| Term | Meaning | Example |
|---|---|---|
| DN (Distinguished Name) | Unique path to an object | CN=alice,OU=Users,DC=example,DC=com |
| CN (Common Name) | Object’s name within its parent | alice, SRVWEB01 |
| OU (Organizational Unit) | Container for organising objects | OU=Users, OU=Finance |
| DC (Domain Component) | DNS domain label | DC=example, DC=com |
| RDN (Relative Distinguished Name) | Single component of a DN | CN=alice |
| objectClass | LDAP schema type | person, inetOrgPerson, computer |
| Attribute | Key-value property of an object | mail: [email protected] |
LDAP Protocol Basics
Section titled “LDAP Protocol Basics”LDAP operates over TCP port 389 (plaintext) and 636 (LDAPS - TLS). Port 3268/3269 are Global Catalog ports in AD.
LDAP Operations
Section titled “LDAP Operations”| Operation | Purpose |
|---|---|
| Bind | Authenticate to the directory server |
| Search | Query the directory with filters |
| Add | Create a new entry |
| Modify | Change attributes of an existing entry |
| Delete | Remove an entry |
| ModifyDN | Rename or move an entry |
| Unbind | Close the connection |
Search Filter Syntax
Section titled “Search Filter Syntax”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 emailldap-utils - Practical Commands
Section titled “ldap-utils - Practical Commands”# Install LDAP client toolsapt 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 636ldapsearch -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)"Common Search Queries
Section titled “Common Search Queries”BASE="dc=example,dc=com"DC="ldap://dc.example.com"AUTH="-D 'cn=ldapread,ou=svc,dc=example,dc=com' -W"
# Find a specific userldapsearch -x -H $DC $AUTH -b "$BASE" "(sAMAccountName=alice)" \ dn mail displayName userAccountControl
# List all users in an OUldapsearch -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 objectsldapsearch -x -H $DC $AUTH -b "$BASE" "(objectClass=computer)" \ cn operatingSystem lastLogonTimestamp
# List members of a groupldapsearch -x -H $DC $AUTH -b "$BASE" \ "(cn=Domain Admins)" member
# Find all groups a user is member ofldapsearch -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 descriptionLDAP Modify Operations
Section titled “LDAP Modify Operations”# 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=comchangetype: modifyadd: membermember: cn=alice,ou=Users,dc=example,dc=comEOF
# Remove from groupldapmodify -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'dn: cn=Developers,ou=Groups,dc=example,dc=comchangetype: modifydelete: membermember: cn=alice,ou=Users,dc=example,dc=comEOF
# Change a user attributeldapmodify -x -H $DC -D "cn=admin,dc=example,dc=com" -W << 'EOF'dn: cn=alice,ou=Users,dc=example,dc=comchangetype: modifyreplace: mailmail: [email protected]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=comobjectClass: inetOrgPersonobjectClass: posixAccountcn: bobsn: Smithuid: bobuidNumber: 1002gidNumber: 1000homeDirectory: /home/bobloginShell: /bin/bashmail: [email protected]userPassword: {SSHA}hashedpasswordhereEOFActive Directory Concepts
Section titled “Active Directory Concepts”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
AD-Specific Attributes
Section titled “AD-Specific Attributes”| Attribute | AD name | Description |
|---|---|---|
| Username | sAMAccountName | Legacy login name (used for NTLM/older auth) |
| Login name | userPrincipalName | [email protected] (UPN - modern) |
| Account control | userAccountControl | Bitmask for enabled/disabled/never expires/etc |
| Last logon | lastLogonTimestamp | Replicated; updated every ~14 days |
| Password set | pwdLastSet | When password was last changed |
| Account created | whenCreated | Object creation timestamp |
| Group membership | memberOf | DNs of groups the user belongs to |
| Manager | manager | DN of user’s manager |
Windows AD Management - PowerShell
Section titled “Windows AD Management - PowerShell”# Import Active Directory moduleImport-Module ActiveDirectory
# --- User queries ---# Get a specific userGet-ADUser -Identity alice -Properties *
# All users in an OUGet-ADUser -Filter * -SearchBase "OU=Users,DC=example,DC=com" | Select-Object SamAccountName, EmailAddress, Enabled
# Disabled accountsGet-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 expiresGet-ADUser -Filter {PasswordNeverExpires -eq $true} | Select-Object SamAccountName
# --- Group operations ---Get-ADGroup -Identity "Domain Admins" -Properties MembersGet-ADGroupMember -Identity "Domain Admins" | Select-Object SamAccountNameGet-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 aliceEnable-ADAccount -Identity aliceSet-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, LastLogonDateGroup Policy (GPO)
Section titled “Group Policy (GPO)”# List all GPOsGet-GPO -All | Select-Object DisplayName, GpoStatus, CreationTime
# Show which GPOs are linked to an OUGet-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 computerInvoke-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 userGet-GPResultantSetOfPolicy -ReportType HTML -Path "C:\rsop.html"Security Audit - AD Red Flags
Section titled “Security Audit - AD Red Flags”# 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"}OpenLDAP - Linux Directory Server
Section titled “OpenLDAP - Linux Directory Server”# Install OpenLDAP serverapt install slapd ldap-utils
# Reconfigure (set domain and admin password)dpkg-reconfigure slapd
# Verify serviceldapwhoami -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 databaseslapcat -n 1 > /backup/ldap-backup.ldif # export to LDIF
# Restoreslapadd -n 1 -F /etc/ldap/slapd.d < /backup/ldap-backup.ldif
# Check OpenLDAP configurationslaptest -F /etc/ldap/slapd.d/ -v # validate configurationLDAPS (LDAP over TLS)
Section titled “LDAPS (LDAP over TLS)”# /etc/ldap/ldap.conf - client TLS settingsTLS_CACERT /etc/ssl/certs/ca-cert.pemTLS_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=configchangetype: modifyadd: olcTLSCACertificateFileolcTLSCACertificateFile: /etc/ssl/certs/ca-cert.pem-add: olcTLSCertificateFileolcTLSCertificateFile: /etc/ssl/certs/ldap-server.crt-add: olcTLSCertificateKeyFileolcTLSCertificateKeyFile: /etc/ssl/private/ldap-server.keyEOF
# Test LDAPS connectionopenssl s_client -connect ldap.example.com:636 </dev/null 2>/dev/null \ | openssl x509 -noout -subject -datesldapwhoami -H ldaps://ldap.example.com -x -D "cn=admin,dc=example,dc=com" -WIntegrating Linux with Active Directory
Section titled “Integrating Linux with Active Directory”# Join a Linux host to an AD domain using realmd + sssdapt install realmd sssd sssd-tools adcli
# Discover the domainrealm discover example.com
# Join the domain (requires AD admin credentials)realm join example.com -U Administrator
# Verify joinrealm list # shows joined domain and permitted logins
# Allow specific AD groups to log in
# Configure sudo for AD group (/etc/sudoers.d/ad-admins)
# sssd configuration (/etc/sssd/sssd.conf)[sssd]services = nss, pamdomains = example.com
[domain/example.com]id_provider = adauth_provider = adaccess_provider = adad_domain = example.comkrb5_realm = EXAMPLE.COMrealmd_tags = manages-system joined-with-adcli
# Restart and verifysystemctl restart sssdsssctl domain-status example.com