OpenLDAP has a dynamically loadable module which can enforce password policies. It allows to define policies for the userPassword
attribute. Policies can define the maximum login attempts with the wrong password, maximum age of a password and many more.
Here is a short introduction into this module. If you want to read about it in detail, see the link collection at the end of this page.
Note: The connection parameters and DN parameters deeply depend on your setup, the examples here need to be adjusted to your setup.
Configuration of the ppolicy overlay
The basic configuration depends on your OpenLDAP version. Newer versions store their configuration in a so-called Online Configuration Database (OLC), older ones use a configuration file called slapd.conf
OpenLDAP with OLC
- Load the ppolicy schema into OLC:
ldapmodify -D "cn=root,cn=config" -W -a -f /etc/openldap/schema/ppolicy.ldif
- Load the module:
ldapmodify -D "cn=root,cn=config" -W -a -f ppolicymodule.ldif
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModuleLoad: ppolicy.la
- Configure ppolicy overlay:
ldapmodify -D "cn=root,cn=config" -W -a -f ppolicyoverlay.ldif
dn: olcOverlay=ppolicy,olcDatabase={1}bdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
olcPPolicyDefault: cn=passwordDefault,ou=Policies,dc=mydomain,dc=tld
olcPPolicyHashCleartext: FALSE
olcPPolicyUseLockout: FALSE
olcPPolicyForwardUpdates: FALSE
OpenLDAP with slapd.conf
If you have an older version of OpenLDAP, the configuration goes into slapd.conf
:
#-- Load schema
include /etc/openldap/schema/ppolicy.schema
#-- Load module
moduleload ppolicy.la
The next snippet should come somewhere after the database
definition:
#-- Load overlay
overlay ppolicy
ppolicy_default "cn=passwordDefault,ou=Policies,dc=mydomain,dc=tld"
This means the default policy is located under cn=passwordDefault,ou=Policies,dc=mydomain,dc=tld
Definition of a password policy
In the overlay configuration we specified the default policy, so we add it now using the following LDIF:
dn: ou=Policies,dc=mydomain,dc=tld
ou: Policies
objectClass: organizationalUnit
dn: cn=passwordDefault,ou=Policies,dc=mydomain,dc=tld
objectClass: pwdPolicy
objectClass: person
objectClass: top
cn: passwordDefault
sn: passwordDefault
pwdAttribute: userPassword
pwdCheckQuality: 0
pwdMinAge: 0
pwdMaxAge: 0
pwdMinLength: 8
pwdInHistory: 5
pwdMaxFailure: 3
pwdFailureCountInterval: 0
pwdLockout: TRUE
pwdLockoutDuration: 0
pwdAllowUserChange: TRUE
pwdExpireWarning: 0
pwdGraceAuthNLimit: 0
pwdMustChange: FALSE
pwdSafeModify: FALSE
All these parameters are described in detail at Chapter 6 OpenLDAP password policy overlay / pwdPolicy ObjectClass and Attributes.
This policy applies to all userPassword
attributes. If an object needs a different policy, just define the differing policy under another name and reference the policy with the pwdPolicySubentry
attribute. Example:
dn: cn=My User,ou=People,dc=mydomain,dc=tld
changetype: modify
add: pwdPolicySubentry
pwdPolicySubentry: cn=passwordSpecial,ou=Policies,dc=mydomain,dc=tld
Usage and behaviour
Query all locked accounts
If an object has the pwdAccountLockedTime
attribute: it is locked since then. Simply issue the following query:
ldapsearch <MYCONNECTIONPARAMS> -b "ou=People,dc=mydomain,dc=tld" "pwdAccountLockedTime=*" pwdAccountLockedTime
Unlock an account
There are two variants. For the first one you simply delete the pwdAccountLockedTime
attribute which unlocks the account immediately:
dn: cn=My User,ou=People,dc=mydomain,dc=tld
changetype: modify
delete: pwdAccountLockedTime
The second variant adds the attribute pwdReset
which basically means: The user can only login again after changing it's password:
dn: cn=My User,ou=People,dc=mydomain,dc=tld
changetype: modify
add: pwdReset
pwdReset: TRUE
If the user tries other operations than changing its password, the OpenLDAP server responds with bind: Operations are restricted to bind/unbind/abandon/StartTLS/modify password
Changing an LDAP password can be done f.e. with the ldappasswd tool:
ldappasswd <MYCONNECTIONPARAMS> -D "cn=My User,ou=People,dc=mydomain,dc=tld" -W -S "cn=My User,ou=People,dc=mydomain,dc=tld"
Behaviour of some policy settings
A short overview of how some of the policies behave (not all covered here):
pwdMinAge
Result: Constraint violation (19)
Additional info: Password is too young to change
pwdMaxAge
ldap_bind: Invalid credentials (49)
in the logfile: ppolicy_bind: Entry cn=My User,ou=People,dc=mydomain,dc=tld has an expired password: 0 grace logins
pwdGraceAuthNLimit
in the log: ppolicy_bind: Entry cn=My User,ou=People,dc=mydomain,dc=tld has an expired password: 1 grace logins
pwdInHistory
Result: Constraint violation (19)
Additional info: Password is in history of old passwords
pwdAllowUserChange
Result: Insufficient access (50)
Additional info: User alteration of password is not allowed
Storage location of the policy data
Policy data (f.e. number of failed login attempts) is stored as Operational Attributes on each object. In a normal ldapsearch query operational attributes are not returned. To make them visible, add a "+" to the end of the query. Example: ldapsearch <MYCONNECTIONPARAMS> -b "ou=People,dc=mydomain,dc=tld" "+"
Considerations when using LDAP replication
If you replicate from an LDAP master to LDAP slave(s) and your users are authenticating against slaves, take into consideration that the policy data needs to be synced somehow back to the master (f.e. number of failed login attempts).
The ppolicy module already knows about it. You basically need to set the
configuration value olcPPolicyForwardUpdates
(OLC style) / ppolicy_forward_updates
(slapd.conf). Furthermore chaining must be configured, including syncrepl. This is very well documented at Linuxtopia.
Further links
Here are some links with more detailed information than this short overview:
- Chapter 6 OpenLDAP password policy overlay
- 12.10. Password Policies
- man 5 slapo-ppolicy