OpenLDAP with TLS, ppolicy and master-master replication on RHEL6.3

This post has been dusting on a draft shelf for too long. No reason to keep it there any loger.
Below is the list of instructions which once followed would help anyone to end up with an OpenLDAP server on RHEL 6.1 (should work perfectly well with CentOS too). There is nothing special about it but if you’re looking for a tip on how to configure OpenLDAP, encrypt its database, setup PPolicy, use TLS and a two-way (master-master) replication then hope that you could take away something useful from this post.
That was an quick and short introduction.

The instructions listed below would assume that we have two LDAP servers:

LDAP1 – ldap1.example.com
LDAP2 – ldap2.example.com

And our test DN would be, what a surprise, dc=example,dc=com

Lets start from ldap1.example.com

  1. Install packages:
  2. yum -y install openldap-servers.x86_64 openldap-clients.x86_64
    
  3. Configure OpenLDAP:
  4. 2.1 Specify olcRootDN and olcRootPW for olcDatabase: {0}config and olcDatabase: {2}bdb databases:

    Use slappasswd to generate {SSHA} hashed passwords and add them into:

    vi /etc/openldap/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif:
    
    olcRootDN: cn=config
    olcRootPW: ##HASHED_PASSWORD##
    
    /etc/openldap/slapd.d/cn\=config/olcDatabase\=\{2\}bdb.ldif respectively:
    
    olcSuffix: dc=example,dc=com
    olcRootDN: cn=manager,dc=example,dc=com
    olcRootPW: ##HASHED_PASSWORD##
    
    vi /etc/openldap/slapd.d/cn\=config/olcDatabase\=\{1\}monitor.ldif
    olcAccess: {0}to *  by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=externa
     l,cn=auth" read  by dn.base="cn=manager,dc=example,dc=com" read  by * none
     

    2.2 Specify Database configuration options and enable encryption:

    cat bdb.ldif
    
    dn: olcDatabase={2}bdb,cn=config
    changetype: modify
    replace: olcDbConfig
    olcDbConfig: set_cachesize 0 268435456 1
    olcDbConfig: set_lg_regionmax 262144
    olcDbConfig: set_lg_bsize 2097152
    olcDbConfig: set_flags DB_LOG_AUTOREMOVE
    -
    replace: olcDbCryptKey
    olcDbCryptKey: ##YOU_CRYPT_KEY##
    
    ldapmodify -h 127.0.0.1 -x -W -D “cn=config” -f ./bdb.ldif
    Enter LDAP Password:
    modifying entry "olcDatabase={2}bdb,cn=config"
    

    After that stop LDAP server, remove DB files and start LDAP server again:

    /etc/init.d/slapd stop
    rm -f /var/lib/ldap/*
    /etc/init.d/slapd start
    

    One LDAP server is restarted DB files will be recreated and encrypted.

  5. Global configuration Options
  6. 3.1 Generate CRS, signed it and copy to a directory of your choice. Please keep in mind that if you’re gong to configure a replication the paths and the names must be identical on both ldap nodes. Otherwise, when you bring a replication up it would overwrite your settings and TLS on one of the servers would be broken:

    cat ./config.ldif
    
    dn: cn=config
    changetype: modify
    delete: olcTLSCACertificatePath
    -
    replace: olcTLSCACertificateFile
    olcTLSCACertificateFile: ##PATH_TO_CA_FILE##
    -
    replace: olcTLSCertificateFile
    olcTLSCertificateFile: ##PATH_TO_CERTIFICATE_FILE##
    -
    replace: olcTLSCertificateKeyFile
    olcTLSCertificateKeyFile: ##PATH_TO_KEY_FILE##
    -
    replace: olcTLSCipherSuite
    olcTLSCipherSuite: HIGH:MEDIUM:!ADH:-SSLv2:+SSLv3
    -
    replace: olcSaslSecProps
    olcSaslSecProps: noanonymous,noplain,minssf=112
    -
    replace: olcDisallows
    olcDisallows: bind_anon
    -
    replace: olcIdleTimeout
    olcIdleTimeout: 120
    
    
    ldapmodify -h 127.0.0.1 -x -W -D “cn=config” -f ./config.ldif
    Enter LDAP Password:
    modifying entry "cn=config"
    

    Disable all LDAP access schemas in /etc/sysconfig/ldap and leave only LDAPS enabled and set the following option:

    SLAPD_OPTIONS="-g ldap"
    

    Time to restart LDAP server to make sure everything is still fine:

    /etc/init.d/slapd restart
    
  7. Global Database Options
  8. cat ./frontend.ldif
    
    dn: olcDatabase={-1}frontend,cn=config
    changetype: modify
    add: olcPasswordHash
    olcPasswordHash: {SSHA}
    -
    add: olcRequires
    olcRequires: LDAPv3 authc
    ldapmodify -H ldaps://ldap1.example.com -x -W -D “cn=config” -f ./frontend.ldif
    Enter LDAP Password:
    modifying entry "olcDatabase={-1}frontend,cn=config"
    
  9. Add your domain object (example.com in our case):
  10. cat ./example_com.ldif
    
    dn: dc=example,dc=com
    objectClass: dcObject
    objectClass: organization
    dc: example
    o: Example Com
    description: Example Com
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D “cn=manager,dc=example,dc=com” -f ./example_com.ldif
    Enter LDAP Password:
    adding new entry "dc=example,dc=com"
    
  11. Enable PPolicy
  12. 6.1. Load policy module

    cat ./module.ldif
    dn: cn=module,cn=config
    objectClass: olcModuleList
    cn: module
    olcModuleLoad: ppolicy.la
    olcModulePath: /usr/lib64/openldap
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D “cn=config” -f ./module.ldif
    Enter LDAP Password:
    adding new entry "cn=module,cn=config"
    

    Restart LDAP with /etc/init.d/slapd restart

    6.2 Add PPolicy Overlay:

    cat ./ppolicy-overlay.ldif
    dn: olcOverlay=ppolicy,olcDatabase={2}bdb,cn=config
    objectClass: olcPPolicyConfig
    olcOverlay: ppolicy
    olcPPolicyDefault: cn=ppolicy,ou=policies,dc=example,dc=com
    olcPPolicyUseLockout: TRUE
    olcPPolicyHashCleartext: TRUE
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D "cn=config" -f ./ppolicy-overlay.ldif
    Enter LDAP Password:
    adding new entry "olcOverlay=ppolicy,olcDatabase={2}bdb,cn=config"
    

    6.3 Create default PPolicy and its rules:

    cat ./default_ppolicy.ldif
    dn: ou=policies,dc=example,dc=com
    objectClass: top
    objectClass: organizationalUnit
    ou: policies
    
    dn: cn=ppolicy,ou=policies,dc=example,dc=com
    objectClass: top
    objectClass: device
    objectClass: pwdPolicyChecker
    objectClass: pwdPolicy
    cn: ppolicy
    pwdAttribute: userPassword
    pwdInHistory: 8
    pwdMinLength: 8
    pwdMaxFailure: 3
    pwdFailureCountInterval: 1800
    pwdCheckQuality: 0
    pwdMustChange: TRUE
    pwdGraceAuthNLimit: 0
    pwdMaxAge: 7776000
    pwdExpireWarning: 1209600
    pwdLockoutDuration: 900
    pwdLockout: TRUE
    
    ldapadd -H ldaps://smsk01gw01 -x -W -D “cn=manager,dc=example,dc=com” -f ./default_ppolicy.ldif
    Enter LDAP Password:
    adding new entry "ou=policies,dc=example,dc=com"
    
    adding new entry "cn=ppolicy,ou=policies,dc=example,dc=com"
    
  13. Enable Audit overlay
  14. mkdir /var/log/slapd/
    chown ldap:ldap /var/log/slapd/
    
    echo “local4.*        /var/log/slapd/slapd.log” >> /etc/rsyslog.conf && /etc/init.d/rsyslog restart
    
    cat ./audit.ldif
    
    dn: cn=module{0},cn=config
    changetype: modify
    add: olcModuleLoad
    olcModuleLoad: {1}auditlog
    
    dn: olcOverlay=auditlog,olcDatabase={2}bdb,cn=config
    changetype: add
    objectClass: olcOverlayConfig
    objectClass: olcAuditLogConfig
    olcOverlay: auditlog
    olcAuditlogFile: /var/log/slapd/auditlog.log
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D “cn=config” -f ./ppolicy.ldif
    
  15. Add group and people OUs:
  16. cat ./groups.ldif
    
    dn: ou=group,dc=example,dc=com
    objectClass: top
    objectclass: organizationalunit
    ou: group
    
    dn: cn=users,ou=group,dc=example,dc=com
    cn: users
    objectClass: posixGroup
    gidNumber: 10000
    
    dn: ou=people,dc=example,dc=com
    objectClass: top
    objectclass: organizationalunit
    ou: people
    
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D “cn=manager,dc=example,dc=com” -f ./groups.ldif
    Enter LDAP Password:
    adding new entry "ou=group,dc=examlple,dc=com"
    
    adding new entry "cn=users,ou=group,dc=example,dc=com"
    
    adding new entry "ou=people,dc=example,dc=com"
    
  17. Add ACLs, proxy LDAP account, SSSD and PAM configuration:
  18. 9.1 Proxy LDAP account

    cat ./svc_ldp_proxy.ldif
    
    dn: cn=svc_ldp_proxy,dc=example,dc=com
    objectClass: simpleSecurityObject
    objectClass: organizationalRole
    cn: svc_ldp_proxy
    userPassword:
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D “cn=manager,dc=example,dc=com” -f ./svc_ldp_proxy.ldif
    

    9.2 ACLs

    If you’re not interested in configuring replicatoion you could skip replicator.ldif part otherwise you should create a separate DN that will be used to bind to a remote LDAP server and to replicate the data. Strictly speaking that’s not required but it’s a good idea to have a dedicated account just to run the replication part.

    cat ./replicator.ldif
    dn: cn=replicator,dc=example,dc=com
    objectClass: simpleSecurityObject
    objectClass: organizationalRole
    cn: replicator
    userPassword: ##REPLICATOR_PASSWORD##
    
    ldapadd -H ldaps://ldap1.example.com -x -W -D "cn=manager,dc=example,dc=com" -f ./replicator.ldif
    Enter LDAP Password:
    adding new entry "cn=replicator,dc=example,dc=com"
    
    cat ./acl.ldif
    
    dn: olcDatabase={2}bdb,cn=config
    changetype: modify
    replace: olcAccess
    olcAccess: to attrs=userPassword by self write by dn="cn=replicator,dc=example,dc=com" write by anonymous auth
    olcAccess: to * by dn="cn=svc_ldp_proxy,dc=example,dc=com" read by dn="cn=replicator,dc=example,dc=com" write by self read by users read by anonymous auth by * none
    
    ldapmodify -H ldaps://ldap1.example.com -x -W -D “cn=config” -f ./acl.ldif
    Enter LDAP Password:
    modifying entry "olcDatabase={2}bdb,cn=config"
    

    9.3 SSSD

    cat /etc/sssd/sssd.conf
    [sssd]
    config_file_version = 2
    services = nss, pam
    domains = LDAP
    
    [nss]
    filter_groups = root
    filter_users = root
    
    [pam]
    pam_verbosity = 2
    
    [domain/LDAP]
    access_provider = simple
    simple_allow_groups = users
    tls_reqcert = never
    id_provider = ldap
    auth_provider = ldap
    chpass_provider = ldap
    use_fully_qualified_names = false
    ldap_uri = ldaps://ldap1.example.com:636/,ldaps://ldap2.example.com:636/
    ldap_search_base = dc=example,dc=com
    ldap_default_bind_dn = cn=svc_ldp_proxy,dc=example,dc=com
    ldap_default_authtok_type = password
    ldap_default_authtok = ##PASSWORD##
    ldap_tls_cacert = ##PATH_TO_CA_FILE##
    ldap_id_use_start_tls = true
    ldap_pwd_policy = none
    enumerate = false
    cache_credentials = false
    

    It’s a good idea to configure ldap.conf as well:

    cat /etc/openldap/ldap.conf
    
    URI ldaps://ldap1.example.com:636/ ldaps://ldap2.example.com:636/
    BASE dc=example,dc=com
    TLS_CACERT ##PATH_TO_CA_FILE##
    

    9.4 NSS and PAM (Only for RHEL6 or simialr distros). For RHEL5 the settings would be slightly different and I provided them in the very end.

    To be able to authenticate to the system and change a password using passwd command /etc/nsswitch.conf and /etc/pam.d/system-auth files must be updated respectively:

    Verify that /etc/pam.d/system-auth has the following line:

    password sufficient pam_sss.so use_authtok

    /etc/nsswitch.conf must have the following settings:

    passwd: files sss
    shadow: files sss
    group: files sss

  19. At this point your first LDAP server should be configured. But you must repeat all of the above steps to setup the second LDAP and only after that proceed further.
  20. LDAP Replication
  21. ldap1.example.com

    cat ./repl-module.ldif
    dn: cn=module,cn=config
    objectClass: olcModuleList
    cn: module
    olcModulePath: /usr/lib64/openldap
    olcModuleLoad: syncprov.la
    
    cat ./repl-config.ldif
    
    dn: cn=config
    changetype: modify
    replace: olcServerID
    olcServerID: 1 ldaps://ldap1.example.com
    olcServerID: 2 ldaps://ldap2.example.com
    
    dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
    changetype: add
    objectClass: olcOverlayConfig
    objectClass: olcSyncProvConfig
    olcOverlay: syncprov
    
    dn: olcDatabase={0}config,cn=config
    changetype: modify
    add: olcSyncRepl
    olcSyncRepl: rid=001 provider=ldaps://ldap1.example.com binddn="cn=config" bindmethod=simple credentials=##PASSWORD## searchbase="cn=config" type=refreshAndPersist retry="5 5 300 5" timeout=1
    olcSyncRepl: rid=002 provider=ldaps://ldap2.example.com binddn="cn=config" bindmethod=simple credentials=##PASSWORD## searchbase="cn=config" type=refreshAndPersist retry="5 5 300 5" timeout=1
    -
    add: olcMirrorMode
    olcMirrorMode: TRUE
    
    ldapmodify -H ldaps://ldap1.example.com/ -x -W -D “cn=config” -f ./repl-config.ldif
    Enter LDAP Password:
    modifying entry "cn=config"
    
    adding new entry "olcOverlay=syncprov,olcDatabase={0}config,cn=config"
    
    modifying entry "olcDatabase={0}config,cn=config"
    

    Add the above to ldap2.example.com too.

    ldapmodify -H ldaps://ldap2.example.com/ -x -W -D “cn=config” -f ./repl-config.ldif
    

    If everything has been done correctly the replication of config database should be up and running.

  22. Finally add actual data replication. This should be done on ldap1.example.com only since all the changes would be replicated to ldap2.example.com:
  23. cat ./bdb-repl.ldif 
    
    
    dn: olcDatabase={2}bdb,cn=config
    changetype: modify
    replace: olcLimits
    olcLimits: dn.exact="cn=manager,dc=example,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
    -
    add: olcSyncRepl
    olcSyncRepl: rid=004 provider=ldaps://ldap1.example.com binddn="cn=replicator,dc=example,dc=com" bindmethod=simple credentials=##PASSWORD## searchbase="dc=example,dc=com" type=refreshAndPersist retry="5 5 5 +" timeout=3
    olcSyncRepl: rid=005 provider=ldaps://ldap2.example.com binddn="cn=replicator,dc=example,dc=com" bindmethod=simple credentials=##PASSWORD## searchbase="dc=example,dc=com" type=refreshAndPersist retry="5 5 5 +" timeout=3
    -
    add: olcMirrorMode
    olcMirrorMode: TRUE
    
    dn: olcOverlay=syncprov,olcDatabase={2}bdb,cn=config
    changetype: add
    objectClass: olcOverlayConfig
    objectClass: olcSyncProvConfig
    olcOverlay: syncprov
    
    ldapmodify -H ldaps://ldap1.example.com -x -W -D “cn=config” -f ./bdb-repl.ldif
    Enter LDAP Password:
    modifying entry "olcDatabase={2}bdb,cn=config"
    
    adding new entry "olcOverlay=syncprov,olcDatabase={2}bdb,cn=config"
    

    As the last step update /etc/sysconfig/ldap and set all schemas to “no” leaving only SLAPD_URLS to look like a below:

    SLAPD_URLS=”ldaps://ldap1.example.com ldaps://ldap2.example.com”

  24. Restart LDAP servers on both servers and check the logs to make sure that binding was successful and the replication has been established.
    To be on the safe side, try to add a new DN in ou=people,dc=example,dc=com from ldap2.example.com, e.g. uid=testuser,ou=people,dc=example,dc=com, to make sure that this new record will be propagated to ldap1.example.com


RHEL5 client configuration section

  1. LDAP:
  2. cat /etc/ldap.conf
    
    base dc=example,dc=com
    uri ldaps://ldap1.example.com:636/ ldaps://ldap2.example.com:636/
    binddn cn=svc_ldp_proxy,dc=example,dc=com
    bindpw ##PASSWORD##
    scope sub
    timelimit 120
    bind_timelimit 120
    idle_timelimit 3600
    ldap_version 3
    
    nss_base_group          dc=example,dc=com
    nss_base_netgroup       dc=example,dc=com
    
    # Just assume that there are no supplemental groups for these named users
    nss_initgroups_ignoreusers root,ldap,named,avahi,haldaemon,dbus,radvd,tomcat,radiusd,news,mailman
    
    pam_password exop
    pam_lookup_policy yes
    
    tls_cacertfile ##PATH_TO_CA_FILE##
    
  3. PAM:
  4. cat /etc/pam.d/system-auth-ac
    #%PAM-1.0
    auth        required      pam_env.so
    auth        sufficient    pam_unix.so nullok try_first_pass
    auth        sufficient    pam_ldap.so use_first_pass
    auth        requisite     pam_succeed_if.so uid >= 500 quiet
    auth        required      pam_deny.so
    
    account     required      pam_unix.so
    account     required      pam_access.so
    account     sufficient    pam_succeed_if.so uid < 500 quiet
    account [default=bad success=ok user_unknown=ignore]    pam_ldap.so
    account     required      pam_permit.so
    
    password    required      pam_passwdqc.so min=disabled,disabled,disabled,8,8 retry=3 enforce=everyone
    password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
    password    sufficient    pam_ldap.so use_authtok
    password    required      pam_deny.so
    
    session     optional      pam_keyinit.so revoke
    session     required      pam_limits.so
    session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
    session     sufficient    pam_ldap.so
    session     required      pam_unix.so
    session     required      pam_mkhomedir.so umask=0077
    
  5. NSS:
  6. Do not forget to configure /etc/nsswitch.conf appropriately depending on whether you are going to use ldap or compat.

Please, don't hesitate to let me know if you notice and mistakes or unfortunate typos that have cropped up.

36 thoughts on “OpenLDAP with TLS, ppolicy and master-master replication on RHEL6.3

  1. Pingback: Flagword.net » How to setup Solaris 10 ldap client and glue it with ssh

  2. Hi:
    I follow your guideline to load module.ldif, ppolicy_overlay.ldif and ppolicy.ldif, but somehow the account is not locked even the pwdAccountLockedTime does show the timestamp in it. Any help how to debug it?
    Thank for the help.

    • Hi Greg,

      If you see that pwAccountLockedTIme was set then from the OpenLDAP server’s point of view everything works just fine an account has been locked indeed.
      From “man slapo-ppolicy”:

      pwdAccountLockedTime
      This attribute contains the time that the user’s account was locked.

      You just need to enforce the password policy from the client’s side – a server where a user tries to log into – by updating /etc/ldap.conf (if you run RHEL5 or any other distro which uses pam_ldap) and adding “pam_lookup_policy yes” and “pam_password exop”. The latter option is required to specify a password change protocol to use.
      For RHEL6, which uses sssd to manage access to remote directories and authentication mechanisms, no extra actions are required since it respects pwdAccountLockedTime attribute: – [SSSD] Support for pwdAccountLockedTime
      For password expiration “ldap_pwd_policy” option could be added to [domain/LDAP] section in /etc/sssd/sssd.conf which is set to “none” by default. From “man sssd-ldap”:

      ldap_pwd_policy
      ldap_pwd_policy (string)
      Select the policy to evaluate the password expiration on the client side. The following values are allowed:

      none – No evaluation on the client side. This option cannot disable server-side password policies.
      shadow – Use shadow(5) style attributes to evaluate if the password has expired.
      mit_kerberos – Use the attributes used by MIT Kerberos to determine if the password has expired. Use chpass_provider=krb5 to
      update these attributes when the password is changed.

      Default: none

      Hope that helps.

      Kind regards,
      Sergey

      Thank you.

  3. Hi:
    Thanks for the reply. I will try it on the client. I am running CentOS 6.4 and using /etc/pam_ldap.conf and /etc/nslcd.conf, but not using sssd. Will that make any difference? Also, how to delete module.ldif inside cn=config directory. I use ldapadd multiple times and create multiple duplicate. I also create multiple duplicate ppolicy.ldif inside olcDatabase={1}bdb directory. Can I just rm those files without creating error or I have to use other ldap command to delete them? Thank you.

  4. Hi:
    One more problem. When I test the failures login, the timestamp is around 20140211153648, but it shows pwdAccountLockedTime 20140211233648Z which is 8 more hours later. Which setting causes that? The slapd also hung after it finds the ppolicy_bind: Setting warning for password expiry for cn=……,dc=com = 0 seconds. Thank you for the help.

    • Hi Greg,

      I am running CentOS 6.4 and using /etc/pam_ldap.conf and /etc/nslcd.conf, but not using sssd. Will that make any difference?

      No, definitely not as it should work with both good old pam_ldap and sssd.

      Also, how to delete module.ldif inside cn=config directory. I use ldapadd multiple times and create multiple duplicate. I also create multiple duplicate ppolicy.ldif inside olcDatabase={1}bdb directory. Can I just rm those files without creating error or I have to use other ldap command to delete them?

      Frankly speaking I’ve never done that before but think you should be able to remove all the duplicates simply by using “rm”. Note that by doing so, a server restart might be required to force it to re-read its configuration. Personally, I’d prefer to use ldapmodify instead.
      Run the following two commands to get a list of all ppolicy overlays and olcModuleList:

      ldapsearch -x -H ldaps://your_ldap_server -b "cn=config" -D "cn=config" -LLL -w your_password "objectClass=olcModuleList"
      ldapsearch -x -H ldaps://your_ldap_server -b "cn=config" -D "cn=config" -LLL -w your_password "objectClass=olcPPolicyConfig"
      

      From the output pick DNs you’d like to remove, e.g:

      dn: cn=module{1},cn=config
      dn: olcOverlay={0}ppolicy,olcDatabase={2}bdb,cn=config
      

      Create a simple text file, lets say rmduplicates.ldif, with the following content:

      dn: cn=module{1},cn=config
      changetype: delete
      
      dn: olcOverlay={0}ppolicy,olcDatabase={2}bdb,cn=config
      changetype: delete
      

      And run ldapmodify:

      ldapmodify -H ldaps://your_ldap_server -x -W -D "cn=manager,dc=...,dc=..." -f ./rmduplicates.ldif
      

      After that run ldapsearch as mentioned previously to confirm that there are no more duplicates.

      it shows pwdAccountLockedTime 20140211233648Z which is 8 more hours later.

      Short answer – it’s UTC which is 8 hours ahead of your local time zone.
      pwdAccountLockedTime stores time in a generalizedtime format which “…consists of a string value representing the calendar date, as defined in ISO 8601..” (from wikipedia). ISO 8601 defines time zone designators and that capital “Z” at the end means UTC.

      The slapd also hung after it finds the ppolicy_bind: Setting warning for password expiry for cn=……,dc=com = 0 seconds.

      Do you run multiple LDAP servers in a mirror mode or just a single LDAP server? Are there any error messages in the LDAP server’s log file.
      You migh need to setup a syslog and forward local4 syslog facility (used by OpenLDAP by default) to a local file:

      local4.*        /var/log/slapd/slapd.log
      

      Anyway, lets clean all the duplicates first and configure log output if that hasn’t been already done.

      Cheers.

      BR,
      Sergey

  5. Hi:
    Thanks for the help. Is there a way to change the time not 8 hours later? I will like to see the immediate effect. Also, thanks for the help on rm the duplicate ppolicy. I think I am close to make this work. Thank you for the help.

    • Hi Greg,

      You’re absolutely welcome.

      Is there a way to change the time not 8 hours later? I will like to see the immediate effect.

      The net result is immediate, meaning that an account is locked without an 8 hours delay, it’s just because pwdAccountLockedTime is stored in a generalizedtime format (and OpenLDAP uses UTC) you see it as 8 hours ahead and you can’t change that since this is how it’s defined in a ppolicy schema:

       (  1.3.6.1.4.1.42.2.27.8.1.17
                    NAME ’pwdAccountLockedTime’
                    DESC ’The time an user account was locked’
                    SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
                    EQUALITY generalizedTimeMatch
                    ORDERING generalizedTimeOrderingMatch
                    SINGLE-VALUE
                    NO-USER-MODIFICATION
                    USAGE directoryOperation)
      

      Cheers.

      BR,
      Sergey

  6. Hi:
    I saw this log in the server side
    ppolicy_bind: Setting warning for password expiry for cn=someuser,ou=People,dc=example,=com = 0 seconds
    and I add these two lines in /etc/pam_ldap.conf
    pam_password exop
    pam_lookup_policy yes

    But the client side is not enforced to lock the user account.
    pwdAccountLockedTime and pwdFailureTime all shows timestamp on it. Do I have to chance other client file?
    Thank you for the help.

  7. dn: cn=module{2}{1},cn=config

    dn: cn=module{4}{2},cn=config

    dn: cn=module{3},cn=config

    dn: olcOverlay={1}ppolicy,olcDatabase={1}bdb,cn=config

    dn: olcOverlay={2}ppolicy,olcDatabase={1}bdb,cn=config

    dn: olcOverlay={3}ppolicy,olcDatabase={1}bdb,cn=config

    I have those duplicates and I try your suggestion to delete, but run into this error.

    Enter LDAP Password:
    deleting entry “cn=module{2}{1},cn=config”
    ldap_delete: Server is unwilling to perform (53)

    Sorry that I have so many errors that I don’t know how to solve. Really thank you for your help.

    • Hi Greg,

      deleting entry “cn=module{2}{1},cn=config”
      ldap_delete: Server is unwilling to perform (53)

      Didn’t know myself that deleting from cn=config is not supported until OpenLDAP 2.5. See here and here.
      So the only way for you to delete the duplicates would be to stop slapd and manually delete them from /etc/openldap/slapd.d/cn\=config directory.
      Regarding the problem with password policy not being enforced, could, please, email the content of the following files to sergeyt at flagword.net:

      • /etc/pam_ldap.conf
      • /etc/pam.d/sshd
      • /etc/pam.d/password-auth
      • /etc/nsswitch.conf

      Don’t forget to strip out sensitive information from pam_ldap.conf, i.e. binddn and bindpw.

      Cheer.

      BR,
      Sergey

  8. Hi:
    I finally make the ppolicy working in my openldap 2.4.23 on centos 6.4. I use sssd instead of pam_ldap and nslcd and modify the schemas, and then voila it works. But I still have one minor thing that needs to be done, which is the automount in sssd.conf. The automount worked when I use pam_ldap and nslcd. Can you help a little bit on this? This is my autofs.ldif that worked in pam_ldap/ nslcd.

    dn: ou=autofs,ou=Services,dc=lux,dc=com
    description: Automount maps
    objectClass: top
    objectClass: organizationalUnit
    ou: AutoFS

    dn: ou=auto.home,ou=autofs,ou=Services,dc=lux,dc=com
    objectClass: top
    objectClass: automountMap
    ou: auto.home

    dn: cn=/,ou=auto.home,ou=autofs,ou=Services,dc=lux,dc=com
    automountinformation: filer:/export/home/&
    cn: /
    objectClass: automount

    dn: ou=auto.master,ou=autofs,ou=Services,dc=lux,dc=com
    objectClass: top
    objectClass: automountMap
    ou: auto.master

    dn: cn=/home,ou=auto.master,ou=autofs,ou=Services,dc=lux
    ,dc=com
    automountinformation: ldap:filer:ou=auto.home
    cn: /home
    objectClass: top
    objectClass: automount

    I appreciate it for your help. This automount is my final piece to make it work. Thank you.

    • Hi Greg,

      I finally make the ppolicy working in my openldap 2.4.23 on centos 6.4.

      Great to hear that! Don’t forget to add pam_sss.so to /etc/pam.d/password-auth if you want to enforce password policy for ssh logins.

      But I still have one minor thing that needs to be done, which is the automount in sssd.conf

      Frankly speaking, I’ve never configured autofs with sssd myself but from what I’ve googled out it seems pretty easy and explained in details on RedHat’s portal.

      Cheers.

      BR,
      Sergey

  9. Hi:
    I also manage the automount to work through sssd.conf now. But it seems that I still have a little problem using su when ldapuser login and try su – ldapuser2, the error is like this pam_sss(sshd:auth): received for user ldapuser2: 9 (Authentication service cannot retrieve authentication info). Have you seen this before? The automount and ppolicy all works now. Thank yu for the tips and hints.

    • Hi Greg,
      Good to know you’re making steady progress.
      Regarding your issue, (quick guess) it looks like pam_sss.so is missing in /etc/pam.d/su file. Could you please show its content?

      Thanks.

      BR,
      Sergey

  10. Thanks
    cat /etc/pam.d/su
    #%PAM-1.0
    auth sufficient pam_rootok.so
    # Uncomment the following line to implicitly trust users in the “wheel” group.
    #auth sufficient pam_wheel.so trust use_uid
    # Uncomment the following line to require a user to be in the “wheel” group.
    #auth required pam_wheel.so use_uid
    auth include system-auth
    account sufficient pam_succeed_if.so uid = 0 use_uid quiet
    account include system-auth
    password include system-auth
    session include system-auth
    session optional pam_xauth.so

  11. I figure it out the su and passwd issue now. It was the /etc/pam.d/system-auth-ac file that I modified it wrong. Everything works well now. Thank you for this great blog that helps a lot.

    • Hi Greg,

      That’s very flattering to hear that this modest blog post helped you during OpenLdap setup. Keep up the good work.

      Thank you.
      BR,
      Sergey

  12. Hi:
    How to make PAM display warning of password expiring or account being locked out when the user tries to log-in at the client side?
    Thank you.

    • Hi Greg,

      You should define pwdExpireWarning in your LDAP ppolicy:

      pwdExpireWarning

      This attribute contains the maximum number of seconds before a password is due to expire that expiration warning messages will be
      returned to a user who is authenticating to the directory. If this attribute is not present, or if the value is zero (0), no warnings
      will be sent.

      ( 1.3.6.1.4.1.42.2.27.8.1.7
      NAME ’pwdExpireWarning’
      EQUALITY integerMatch
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
      SINGLE-VALUE )

      Cheers.

  13. I did it and I also add pam_pwd_expiration_warning inside the sssd.conf, but nothing displays on the client side. Have you tried it before? Thanks for the suggestion.

  14. Hi Greg,

    Try to increase LDAP debug level in sssd.conf by adding debug_level = 10 in [domain/LDAP] section. After that try to authenticate using an account with soon to expire password, check /var/log/sssd/sssd_LDAP.log log file, search for “Password Policy Response” string and show its content.

    Thanks.

  15. Pingback: OpenLDAP ppolicy problem

  16. Why, when you load syncprov, do you use “olcModuleLoad: syncprov.la” and when you load ppolicy, you use “olcModuleLoad: ppolicy” (without the .la)?

    • Hi James,

      Good catch. Somehow the missing .la extension when I load ppolicy slipped through my eyes and that certainly should be fixed.
      But what is more exciting, is that it seems the version of OpenLdap which I’m running is smart enough and loads the module properly even when the extensions is omitted. Will take a look at the source code to confirm this hunch.

      Thanks.

      • As promised, I checked the source code and could confirm that .la part could be omitted when doing olcModuleLoad till OpenLDAP project uses lt_dlopenext and not lt_dlopen.

        A module is loaded by

        int module_load(const char* file_name, int argc, char *argv[])

        and its implementation could be found in module.c
        This function mostly does a number of sanity checks whilst the heavylifting is done by libltdl library from the GNU Portable Library Tool:

        if ((module->lib = lt_dlopenext(file)) == NULL) {
                        error = lt_dlerror();

        Function: lt_dlhandle lt_dlopenext (const char *filename)
        The same as lt_dlopen, except that it tries to append different file name extensions to the file name. If the file with the file name filename cannot be found libltdl tries to append the following extensions:
        1.the libtool archive extension .la
        2.the extension used for native dynamically loadable modules on the host platform, e.g., .so, .sl, etc.

        Cheers.

  17. Hi Sergey,

    I used this document to setup an ldap server with password policy. It worked perfectly. Thank you very much for wonderful document.

    However when I try to add a user using Apache DS LDAP browser my password policy does not come in to effect. i.e. it creates user without considering parameters set in password policy. Can you please help?

    Thanks a lot
    Mahe

    • Hi Mahe,

      Thank you very much for your generous comment – this is very rewarding.
      Regarding your question.

      it creates user without considering parameters set in password policy.

      How did you check that?
      Basically, if the default policy was configured there is nothing else that must be done and it will be applied to any LDAP user by default. But keep in mind that if you want to check ppolicy operational attributes for a certain user you must specify them deliberately in your ldapsearch query otherwise they will not be displayed, for example:

      ldapsearch -H ldaps://my_ldap_server:636 -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -x -W uid=someuser pwdChangedTime
      

      And don’t forget that OS must also enforce the password policy from its side.

      Cheers.
      Sergey

  18. Hi,

    This document is perfect and i am able to configure the feature mentioned in the post. I see a small bug in the point no.12
    error
    cat ./bdb-repl.ldif
    olcDatabase={2}bdb,cn=config

    correct
    cat ./bdb-repl.ldif
    dn: olcDatabase={2}bdb,cn=config

    I have a query as well, where we can see the replication logs and where we are defining synchronizing frequency?

    • Hi Shamjith,
      Thank you for spotting the misprint – this is fixed now.

      Regarding your questions. I haven’t done that myself but you could try to set olcLogLevel as described on this page – slapdconf2 (search for 16384).
      Regarding the frequency, as far as I know, you can’t change it with refreshAndPersist type of replication since it’s push based and all changed propagated immidiately from a provider to a consumer.
      From OpenLDAP Admin Guide:

      In its refreshAndPersist mode of synchronization, the provider uses a push-based synchronization. The provider keeps track of the consumer servers that have requested a persistent search and sends them necessary updates as the provider replication content gets modified.

      You could find more information here – http://www.openldap.org/doc/admin24/replication.html

      Cheers.

      WBR,
      Sergey

    • Hi,
      Could you, please, confirm that ppolicy was properly configured? This could be easily done by running a simple ldapsearch query:

      ldapsearch -H ldaps://your_ldap_server:636 -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -x -W uid=some_uid pwdChangedTime
      

      It should provide the output similar to the one listed below:

      # extended LDIF
      #
      # LDAPv3
      # base  with scope subtree
      # filter: uid=some_uid
      # requesting: pwdChangedTime
      #
      
      # some_uid, people, example.com
      dn: uid=some_uid,ou=people,dc=example,dc=com
      pwdChangedTime: 20141115164334Z
      
      # search result
      search: 2
      result: 0 Success
      
      # numResponses: 2
      # numEntries: 1
      

      Cheers.

  19. Hi Admin, Thanks for the detailed info. Can we make OpenLDAP to forward auth requests to Windows AD? any procedure will help us. Thanks.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.