OpenLDAP Password Policy Overlay

By | 2019-09-01

The OpenLDAP password policy overlay allows administrators to implement password policies such as minimum length requirements and expirations. This guide explains how to use it and demonstrates with a few examples.

The password policy overlay configuration is a bit different from other overlays. The configuration of most overlays takes place in the overlay entry of configuration directory. Most of the configuration in this overlay takes place in the back end it applies to.

Load Module

First, you must load the overlay module. If this step fails, it is possible that your installation has it compiled into the server daemon.

See if you have any modules already loaded:

[root@ldap openldap]# slapcat -n 0 | grep olcModuleLoad
olcModuleLoad: {0}back_mdb.la
olcModuleLoad: {1}pw-sha2.la
[root@ldap openldap]# 

If you have any output from the command above, use ldapmodify to load the module:

[root@ldap openldap]# ldapmodify -Q -Y EXTERNAL -H ldapi:///
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la

modifying entry "cn=module{0},cn=config"

Otherwise, use ldapadd. Replace the highlighted portion with what is relevant to your environment. If you aren’t sure where your modules are located, consult the table below.

[root@ldap openldap]# ldapadd -Y EXTERNAL -Q -H ldapi:///
dn: cn=module,cn=config
cn: module 
objectClass: olcModuleList
olcModulePath: /opt/openldap-current/libexec/openldap
olcModuleLoad: ppolicy.la

adding new entry "cn=module,cn=config"
OS PATH
CentOS 7 /usr/lib64/openldap
openSUSE /usr/lib64/openldap
Debian (Stretch) /usr/lib/ldap
Source (Default) /usr/local/libexec/openldap
Source (Tyler’s Guides) /opt/openldap-current/libexec/openldap

Apply The Overlay

Use ldapadd to create entries in your configuration directory for each backend you wish to apply the overlay to. I recommend reading the rest of this section along with the next section before applying the overlay or attempting to configure it.

[root@ldap openldap]# ldapadd -Q -Y EXTERNAL -H ldapi:///
dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
olcPPolicyDefault: cn=default,ou=policies,dc=tylersguides,dc=com

The olcPPolicyDefault attribute is optional. It is used to create a default password policy to fall back on if no other policies apply to a user. I recommend doing so. If you don’t create a default policy, you will have to add the pwdPolicySubentry to each entry you want your password policy enforced on. The value of this attribute must match the DN of an entry with the pwdPolicy class. If it doesn’t match, there will be no default policy.

Configure Overlay

In order to apply password policies you have to do one of three things:

  1. Create a default password policy. I recommend this approach. Once you configure it, you don’t have to think about it anymore and you don’t have to worry about users slipping through the cracks.
  2. Add the pwdPolicySubentry with the DN of an entry that has a policy you wish to use. I recommend this if you want to use different rules for different types of users.
  3. Add the auxiliary class pwdPolicy to each entry you wish to apply a policy too. You must then add the appropriate attributes for the policy.

If you plan on using one of the first two approaches, I recommend creating an OU to help you organize your policies. Here is an example:

# ldapadd -Y EXTERNAL -Q -H ldapi:///
dn: ou=policies,dc=tylersguides,dc=com
objectClass: organizationalUnit
ou: policies

adding new entry "ou=policies,dc=tylersguides,dc=com"

Creating a Default Policy

To create a default policy, you need to create an entry for the overlay to reference. The policy attributes are part of an auxiliary class, so you will need a structural class. I use the organizationalRole class for this because it only requires the cn attribute.

Here is an example:

# ldapadd -Y EXTERNAL -Q -H ldapi:///
dn: cn=default,ou=policies,dc=tylersguides,dc=com
objectClass: pwdPolicy
objectClass: organizationalRole
cn: default
pwdAttribute: userPassword
pwdMinLength: 3
pwdCheckQuality: 2

adding new entry "cn=default,ou=policies,dc=tylersguides,dc=com"

I’ll explain some of the options in a bit, but first I want to demonstrate the policy being enforced.

# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s a \
"uid=testuser,ou=users,dc=tylersguides,dc=com" 
Result: Constraint violation (19)
Additional info: Password fails quality checking policy
# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s abc \
"uid=testuser,ou=users,dc=tylersguides,dc=com" 

Notice how the first attempt to set a password for testuser failed and the second attempt succeeded. This is because the default policy in the example requires passwords to be at least 3 characters long.

This table explains some common password policy options.

Attribute Description
pwdAttribute Specifies the attribute the policies apply to. This is typically userPassword.
pwdMinAge How many seconds must elapse between password changes. The default is 0, or the password can be changed at any time.
pwdMaxAge How long in seconds since the last password change a password is good for. I.e. this allows you to set an expiration period. Once the password is expired, the user won’t be allowed to login. The default is passwords never expire.
pwdInHistory This is how many old passwords are stored. If a user attempts to set a password to one that is in their history, they will receive an error. The default is 0, which means a user can keep re-using the same password indefinitely.
pwdMinLength This allows you to require all passwords to be a minimum length. The default is no minimum length requirement. If you are going to use this, you MUST set pwdCheckQuality to 1 or 2. If pwdCheckQuality isn’t set, the length requirement will not be enforced.
pwdCheckQuality This controls how the server enforces password quality checks. The default, which is 0, is to not check the quality of the password. If it is set to 2, the server always enforces the quality checks; if it is unable to check it, the password will be rejected. You can also set it to 1, which is to accept a password if the server is unable to check it for some reason. I recommend setting this to 2.
pwdMaxFailure How many times a user can fail to authenticate before they are locked out. In order for this to take affect, the pwdLockout attribute must be set to TRUE. The default is 0, which means the user can try as many times as they like.
pwdLockout This must be set to TRUE for the pwdMaxFailure setting to take affect. If it is missing or set to FALSE, pwdMaxFailure is ignored.
pwdLockoutDuration How many seconds before an account that has been locked will be automatically unlocked by the server. The default is 0. If it is pwdLockout, the user will remain locked out until an administrator resets their password.
pwdMustChange When this is set to TRUE and an administrator resets a users password, they are forced to reset it themselves on the first login. The default is FALSE.
pwdPolicySubentry This is how you set which password policy to apply. Its value should be the DN of the applicable password policy entry. The default is the olcPPolicyDefault attribute of the configuration entry used to apply the overlay. If olcPPolicyDefault doesn’t exist and this attribute is missing, no policies will be enforced.

Per Entry Policies

To set a policy on an individual entry, add the pwdPolicy along with the relevant attributes. Suppose you want testuser to have a minimum length requirement of 4 characters. To do this, you would modify the entry as follows.

# ldapmodify -Q -Y EXTERNAL -H ldapi:///
dn: uid=testuser,ou=users,dc=tylersguides,dc=com
changetype: modify
add: objectClass
objectClass: pwdPolicy
-
add: pwdAttribute
pwdAttribute: userPassword
-
add: pwdMinLength
pwdMinLength: 4
-
add: pwdCheckQuality
pwdCheckQuality: 2
-
add: pwdPolicySubentry
pwdPolicySubentry: uid=testuser,ou=users,dc=tylersguides,dc=com

modifying entry "uid=testuser,ou=users,dc=tylersguides,dc=com"

The attribute pwdPolicySubentry may seem strange. This attribute is how the overlay determines which policy to apply to an entry. If it is absent, the default policy is applied. So if you want to create a password policy on a per entry basis, set its value to the DN of the entry you are adding the policy to.

Referencing Existing Policies

If you wish to have multiple policies, this is probably what you want to do. This is why I created an OU in the default policy example. Below I will create two policies and demonstrate using them.

ldapadd -Q -Y EXTERNAL -H ldapi:///
dn: cn=example1,ou=policies,dc=tylersguides,dc=com
cn: example1
objectClass: organizationalRole
objectClass: pwdPolicy
pwdAttribute: userPassword
pwdMinLength: 5
pwdCheckQuality: 2

adding new entry "cn=example2,ou=policies,dc=tylersguides,dc=com"

dn: cn=example2,ou=policies,dc=tylersguides,dc=com
cn: example2
objectClass: organizationalRole
objectClass: pwdPolicy
pwdAttribute: userPassword
pwdMinLength: 6
pwdCheckQuality: 2

adding new entry "cn=example2,ou=policies,dc=tylersguides,dc=com"

Now we will apply these policies to entries by adding the pwdPolicySubentry attribute to them:

# ldapmodify -Q -Y EXTERNAL -H ldapi:///
dn: uid=testuser,ou=users,dc=tylersguides,dc=com
changetype: modify
replace: pwdPolicySubentry
pwdPolicySubentry: cn=example1,ou=policies,dc=tylersguides,dc=com

modifying entry "uid=testuser,ou=users,dc=tylersguides,dc=com"

dn: uid=testuser2,ou=users,dc=tylersguides,dc=com
changetype: modify
add: pwdPolicySubentry
pwdPolicySubentry: cn=example2,ou=policies,dc=tylersguides,dc=com

modifying entry "uid=testuser2,ou=users,dc=tylersguides,dc=com"

The modified entries now look like this:

dn: uid=testuser,ou=users,dc=tylersguides,dc=com
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: pwdPolicy
cn: First Name
sn: Last Name
uid: testuser
uidNumber: 5000
gidNumber: 5000
homeDirectory: /home/testuser
loginShell: /bin/sh
gecos: Full Name
structuralObjectClass: inetOrgPerson
pwdAttribute: userPassword
pwdMinLength: 4
pwdCheckQuality: 2
pwdPolicySubentry: cn=example1,ou=policies,dc=tylersguides,dc=com

dn: uid=testuser2,ou=users,dc=tylersguides,dc=com
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
cn: First Name
sn: Last Name
uid: testuser2
uidNumber: 5001
gidNumber: 5001
homeDirectory: /home/testuser2
loginShell: /bin/sh
gecos: Comments
pwdPolicySubentry: cn=example2,ou=policies,dc=tylersguides,dc=com

Now try to change the passwords on these entries:

# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s abcd \
uid=testuser,ou=users,dc=tylersguides,dc=com
Result: Constraint violation (19)
Additional info: Password fails quality checking policy
# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s abcde \
uid=testuser,ou=users,dc=tylersguides,dc=com
# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s abcde \
uid=testuser2,ou=users,dc=tylersguides,dc=com
Result: Constraint violation (19)
Additional info: Password fails quality checking policy
# ldappasswd -Q -Y EXTERNAL -H ldapi:/// -s abcdef \
uid=testuser2,ou=users,dc=tylersguides,dc=com

Notice how even though uid=testuser has its own policy attributes, the entry referenced by pwdPolicySubentry is the one enforced.

Example Policy

Here is a simple example policy that I would feel comfortable using on a real system.

dn: cn=example,ou=policies,dc=tylersguides,dc=com
cn: example
objectClass: organizationalRole
objectClass: pwdPolicy
pwdAttribute: userPassword
pwdMinLength: 12
pwdCheckQuality: 2
pwdMaxFailure: 10
pwdLockout: TRUE
pwdLockoutDuration: 600
pwdInHistory: 5
pwdMustChange: TRUE

See Also

References