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.