OpenLDAP on FreeBSD: Directory Server Review
OpenLDAP provides a directory service for centralized authentication and authorization. If you manage more than a handful of FreeBSD servers and want consistent user accounts, SSH key distribution, sudo policies, and group management across all of them, LDAP is the answer. This review covers slapd installation on FreeBSD, schema configuration, access control, TLS encryption, replication for high availability, and practical integration with SSH, sudo, and PAM.
For related server security hardening practices, see our hardening FreeBSD server guide.
Why OpenLDAP in 2026
Active Directory dominates enterprise environments. FreeIPA is popular on Linux. But on FreeBSD, OpenLDAP remains the primary choice for LDAP-based directory services:
- FreeIPA does not run on FreeBSD
- OpenLDAP is well-packaged and maintained on FreeBSD
- The
nss_ldapandpam_ldapmodules work reliably - It integrates with everything: SSH, sudo, Postfix, Dovecot, Squid, Samba, and more
- It is lightweight compared to AD or IPA
If your infrastructure is FreeBSD-centric, OpenLDAP is the practical choice.
Installation
Binary Package
shpkg install openldap26-server openldap26-client
This installs slapd (the server), ldapsearch, ldapadd, ldapmodify, and other client tools.
Ports
shcd /usr/ports/net/openldap26-server make config # Enable modules you need: SASL, TLS, overlays make install clean
Initial Configuration
OpenLDAP uses OLC (On-Line Configuration), also called cn=config, as its configuration backend. This replaces the old slapd.conf file. However, for clarity and initial setup, many administrators still use slapd.conf and convert later.
Using slapd.conf (Traditional)
Create /usr/local/etc/openldap/slapd.conf:
shinclude /usr/local/etc/openldap/schema/core.schema include /usr/local/etc/openldap/schema/cosine.schema include /usr/local/etc/openldap/schema/inetorgperson.schema include /usr/local/etc/openldap/schema/nis.schema include /usr/local/etc/openldap/schema/misc.schema # Module path modulepath /usr/local/libexec/openldap moduleload back_mdb # Database definition database mdb maxsize 1073741824 suffix "dc=example,dc=com" rootdn "cn=admin,dc=example,dc=com" rootpw {SSHA}hashed_password_here directory /var/db/openldap-data # Indices for common searches index objectClass eq index uid eq,sub index cn eq,sub index mail eq index memberUid eq index uidNumber eq index gidNumber eq
Generate the root password hash:
shslappasswd -s "YourSecurePassword"
Copy the output into the rootpw line.
Set Permissions and Start
sh# Create data directory mkdir -p /var/db/openldap-data chown ldap:ldap /var/db/openldap-data # Enable and start sysrc slapd_enable="YES" sysrc slapd_flags='-h "ldapi://%2fvar%2frun%2fopenldap%2fldapi/ ldap://0.0.0.0/"' service slapd start
Verify Installation
shldapsearch -x -H ldap://localhost -b "dc=example,dc=com" -D "cn=admin,dc=example,dc=com" -W
Schema Configuration
Standard Schemas
The schemas included above (core, cosine, inetorgperson, nis) cover most use cases:
- core.schema: Basic LDAP object classes
- cosine.schema: X.500 attribute types
- inetorgperson.schema: Person entries with email, phone, etc.
- nis.schema: POSIX account attributes (uid, gid, home directory, shell)
For SSH public key distribution, add the OpenSSH LDAP schema:
shpkg install openssh-portable-ldap-pubkey
Or create a custom schema file:
sh# /usr/local/etc/openldap/schema/openssh-lpk.schema attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'OPENSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DESC 'OPENSSH Public key objectclass' SUP top AUXILIARY MAY ( sshPublicKey $ uid ) )
Include it in slapd.conf:
shinclude /usr/local/etc/openldap/schema/openssh-lpk.schema
Populating the Directory
Base Structure
Create an LDIF file for the initial directory structure:
sh# /tmp/base.ldif dn: dc=example,dc=com objectClass: dcObject objectClass: organization dc: example o: Example Organization dn: ou=People,dc=example,dc=com objectClass: organizationalUnit ou: People dn: ou=Groups,dc=example,dc=com objectClass: organizationalUnit ou: Groups dn: ou=Services,dc=example,dc=com objectClass: organizationalUnit ou: Services
Load it:
shldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f /tmp/base.ldif
Adding Users
sh# /tmp/user.ldif dn: uid=jdoe,ou=People,dc=example,dc=com objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount objectClass: ldapPublicKey uid: jdoe cn: John Doe sn: Doe givenName: John mail: jdoe@example.com uidNumber: 10001 gidNumber: 10001 homeDirectory: /home/jdoe loginShell: /bin/sh userPassword: {SSHA}hashed_password sshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... jdoe@workstation
shldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f /tmp/user.ldif
Adding Groups
sh# /tmp/group.ldif dn: cn=sysadmins,ou=Groups,dc=example,dc=com objectClass: posixGroup cn: sysadmins gidNumber: 10001 memberUid: jdoe dn: cn=developers,ou=Groups,dc=example,dc=com objectClass: posixGroup cn: developers gidNumber: 10002 memberUid: jdoe
shldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -W -f /tmp/group.ldif
Access Control Lists
ACLs control who can read, write, and modify directory entries.
sh# /usr/local/etc/openldap/slapd.conf # Users can read and modify their own password # Admin can modify anything # Anonymous can authenticate (bind) # Authenticated users can read most attributes access to attrs=userPassword by self write by anonymous auth by dn="cn=admin,dc=example,dc=com" write by * none access to attrs=shadowLastChange by self write by dn="cn=admin,dc=example,dc=com" write by * read # SSH keys: readable by the service account access to attrs=sshPublicKey by dn="cn=sshd,ou=Services,dc=example,dc=com" read by self write by dn="cn=admin,dc=example,dc=com" write by * none # Default: authenticated users can read access to * by dn="cn=admin,dc=example,dc=com" write by users read by * none
Create a service account for sshd:
sh# /tmp/service.ldif dn: cn=sshd,ou=Services,dc=example,dc=com objectClass: simpleSecurityObject objectClass: organizationalRole cn: sshd userPassword: {SSHA}service_account_password
TLS Encryption
Never run LDAP without TLS in production. Passwords traverse the wire during bind operations.
Generate Certificates
sh# Using a self-signed CA (use a real CA for production) mkdir -p /usr/local/etc/openldap/certs cd /usr/local/etc/openldap/certs # CA key and cert openssl req -new -x509 -nodes -days 3650 \ -keyout ca-key.pem -out ca-cert.pem \ -subj "/CN=LDAP CA" # Server key and CSR openssl req -new -nodes \ -keyout slapd-key.pem -out slapd.csr \ -subj "/CN=ldap.example.com" # Sign the server cert openssl x509 -req -days 3650 \ -in slapd.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out slapd-cert.pem # Set permissions chown ldap:ldap slapd-key.pem chmod 400 slapd-key.pem
Configure TLS in slapd.conf
shTLSCACertificateFile /usr/local/etc/openldap/certs/ca-cert.pem TLSCertificateFile /usr/local/etc/openldap/certs/slapd-cert.pem TLSCertificateKeyFile /usr/local/etc/openldap/certs/slapd-key.pem TLSProtocolMin 3.3 TLSCipherSuite HIGH:!aNULL:!MD5
Enable LDAPS
Update the slapd flags to listen on LDAPS (port 636):
shsysrc slapd_flags='-h "ldapi://%2fvar%2frun%2fopenldap%2fldapi/ ldap://0.0.0.0/ ldaps://0.0.0.0/"' service slapd restart
Require TLS
To reject non-TLS connections:
sh# In slapd.conf security tls=1
Client TLS Configuration
On client machines, configure /usr/local/etc/openldap/ldap.conf:
shTLS_CACERT /usr/local/etc/openldap/certs/ca-cert.pem TLS_REQCERT demand URI ldaps://ldap.example.com BASE dc=example,dc=com
Replication
For high availability, configure replication between two or more slapd instances.
Provider-Consumer (Master-Slave)
On the provider (master), add to slapd.conf:
shmoduleload syncprov overlay syncprov syncprov-checkpoint 100 10 syncprov-sessionlog 100
On the consumer (slave), add:
shsyncrepl rid=001 provider=ldaps://ldap-master.example.com type=refreshAndPersist searchbase="dc=example,dc=com" scope=sub schemachecking=off bindmethod=simple binddn="cn=admin,dc=example,dc=com" credentials=AdminPassword starttls=critical tls_cacert=/usr/local/etc/openldap/certs/ca-cert.pem tls_reqcert=demand retry="60 +"
Multi-Master Replication
For active-active setups, both servers are providers and consumers of each other:
sh# On server 1 serverID 1 syncrepl rid=001 provider=ldaps://ldap2.example.com # ... (same parameters as above) overlay syncprov # On server 2 serverID 2 syncrepl rid=002 provider=ldaps://ldap1.example.com # ... (same parameters as above) overlay syncprov
Multi-master replication works but requires careful conflict handling. Avoid simultaneous writes to the same entry on different masters.
PAM/NSS Integration
This is where LDAP becomes operationally useful: centralized login across all FreeBSD hosts.
Install Required Packages
shpkg install pam_ldap nss_ldap
Configure NSS
Edit /etc/nsswitch.conf:
shpasswd: files ldap group: files ldap shadow: files ldap
Configure /usr/local/etc/nss_ldap.conf:
shuri ldaps://ldap.example.com base dc=example,dc=com binddn cn=nss,ou=Services,dc=example,dc=com bindpw service_password ssl on tls_cacertfile /usr/local/etc/openldap/certs/ca-cert.pem nss_base_passwd ou=People,dc=example,dc=com nss_base_group ou=Groups,dc=example,dc=com nss_base_shadow ou=People,dc=example,dc=com
Configure PAM
Edit /etc/pam.d/system or specific service files:
sh# /etc/pam.d/sshd auth required pam_opie.so no_warn no_fake_prompts auth requisite pam_opieaccess.so no_warn allow_local auth sufficient /usr/local/lib/pam_ldap.so no_warn auth required pam_unix.so no_warn try_first_pass account sufficient /usr/local/lib/pam_ldap.so account required pam_unix.so password sufficient /usr/local/lib/pam_ldap.so password required pam_unix.so no_warn try_first_pass session required pam_unix.so
Home Directory Creation
Automatically create home directories on first login:
sh# Add to /etc/pam.d/system or /etc/pam.d/sshd session required pam_mkhomedir.so mode=0700
Verify
sh# Test NSS resolution getent passwd jdoe id jdoe # Test SSH login ssh jdoe@localhost
SSH Public Key Integration
Distribute SSH public keys via LDAP instead of managing authorized_keys files.
Configure sshd
Edit /usr/local/etc/ssh/sshd_config (if using openssh-portable):
shAuthorizedKeysCommand /usr/local/bin/ldap-ssh-keys.sh %u AuthorizedKeysCommandUser nobody
Create the key lookup script:
sh#!/bin/sh # /usr/local/bin/ldap-ssh-keys.sh /usr/local/bin/ldapsearch -x -H ldaps://ldap.example.com \ -D "cn=sshd,ou=Services,dc=example,dc=com" \ -w "sshd_service_password" \ -b "ou=People,dc=example,dc=com" \ "(uid=$1)" sshPublicKey | \ sed -n 's/^sshPublicKey: //p'
shchmod 755 /usr/local/bin/ldap-ssh-keys.sh service sshd restart
Now, adding or revoking SSH keys in LDAP takes effect immediately on all servers. No need to touch authorized_keys files.
Sudo Integration
Centralize sudo rules in LDAP.
Add the Sudo Schema
sh# The sudo schema is included with the sudo package pkg install sudo cp /usr/local/share/examples/sudo/schema.OpenLDAP /usr/local/etc/openldap/schema/sudo.schema
Add to slapd.conf:
shinclude /usr/local/etc/openldap/schema/sudo.schema
Add Sudo Rules to LDAP
sh# /tmp/sudo.ldif dn: ou=SUDOers,dc=example,dc=com objectClass: organizationalUnit ou: SUDOers dn: cn=defaults,ou=SUDOers,dc=example,dc=com objectClass: sudoRole cn: defaults sudoOption: !authenticate sudoOption: env_keep += "SSH_AUTH_SOCK" dn: cn=sysadmins,ou=SUDOers,dc=example,dc=com objectClass: sudoRole cn: sysadmins sudoUser: %sysadmins sudoHost: ALL sudoCommand: ALL
Configure Sudo to Use LDAP
Edit /usr/local/etc/sudo-ldap.conf:
shuri ldaps://ldap.example.com sudoers_base ou=SUDOers,dc=example,dc=com binddn cn=sudo,ou=Services,dc=example,dc=com bindpw sudo_service_password ssl on tls_cacert /usr/local/etc/openldap/certs/ca-cert.pem
Edit /usr/local/etc/nsswitch.conf (or use /usr/local/etc/sudo.conf):
shsudoers: ldap files
Backup and Recovery
Database Export
shslapcat -b "dc=example,dc=com" > /backup/ldap-$(date +%Y%m%d).ldif
Database Import
shservice slapd stop rm -rf /var/db/openldap-data/* slapadd -b "dc=example,dc=com" -l /backup/ldap-20260409.ldif chown -R ldap:ldap /var/db/openldap-data service slapd start
Automated Backups
sh# /etc/cron.d/ldap-backup 0 3 * * * root /usr/local/sbin/slapcat -b "dc=example,dc=com" | gzip > /backup/ldap-$(date +\%Y\%m\%d).ldif.gz
Verdict
OpenLDAP on FreeBSD is the right tool for centralized authentication on FreeBSD-centric infrastructure. It is not glamorous, the configuration is verbose, and the learning curve is real. But once deployed, it eliminates the operational burden of managing user accounts, SSH keys, and sudo rules across multiple servers.
The PAM/NSS integration works. SSH key distribution works. Sudo integration works. TLS and replication work. It is battle-tested software that does what it claims.
The main risk is complexity. LDAP is not simple, and misconfiguration can lock you out of your own servers. Always maintain local root accounts as a fallback, and test changes in a staging environment first.
Rating: 7/10 -- Functionally excellent but held back by configuration complexity and the lack of a modern management interface. Consider supplementing with Apache Directory Studio or phpLDAPadmin for directory management.
Frequently Asked Questions
What happens if the LDAP server goes down?
If NSS/PAM cannot reach the LDAP server, users not in local /etc/passwd cannot log in. Mitigate this with:
- Multi-master replication for redundancy
nss_ldap.confsettingbind_timelimit 5to fail fast- Local root accounts that do not depend on LDAP
- Consider
nscd(name service caching daemon) to cache LDAP lookups
Should I use slapd.conf or cn=config (OLC)?
cn=config is the modern approach and supports runtime changes without restart. slapd.conf is easier to understand and version control. For small deployments (under 10 servers), slapd.conf is fine. For larger environments, invest in cn=config.
How do I migrate from NIS to LDAP?
Export NIS maps to LDIF format using ypcat and conversion scripts. The migrationtools package provides scripts for this:
shpkg install migrationtools /usr/local/share/migrationtools/migrate_passwd.pl /etc/passwd > passwd.ldif
Can I use LDAP with FreeBSD jails?
Yes. Configure each jail as an LDAP client. The jail does not need its own LDAP server -- it connects to the host's or a dedicated LDAP server. Ensure the jail has network access to the LDAP server and install pam_ldap and nss_ldap inside the jail.
How do I troubleshoot LDAP authentication failures?
sh# Test LDAP connectivity ldapsearch -x -H ldaps://ldap.example.com -b "dc=example,dc=com" "(uid=jdoe)" # Test bind (authentication) ldapwhoami -x -H ldaps://ldap.example.com -D "uid=jdoe,ou=People,dc=example,dc=com" -W # Check slapd logs # Enable logging in slapd.conf: loglevel stats # Logs go to syslog facility local4 grep slapd /var/log/messages
What is the performance impact of LDAP authentication?
Negligible for authentication (a single LDAP bind per login). NSS lookups (getent passwd, group resolution) can be more frequent. Use nscd to cache results:
shsysrc nscd_enable="YES" service nscd start