# ldapdock *_a configurable secure openLDAP based container_* Step by step approach on how to setup and run an openLDAP server on a systemd-less docker image container ## _1- Creating the ldapdock image container_ build ldapdock from the dockerfile and run into it, creating the proper volumes to save databases data, config data, and certs data ``` > docker build -t ldapdock --build-arg LDAP_HOST=example.com . ``` do a "nuclear clean" of our volumes (except the export cert) since we are building again ``` > sudo rm -rf ldap_data/* ldap_config/* ldap_certs/* ``` ``` > docker run -i -t -p 389:389 -p 636:636 -p 80:80 -p 443:443 -h ${LDAP_HOST:-example.com} -v ldap_data:/var/lib/ldap -v ldap_config:/etc/ldap/slapd.d -v ldap_certs:/etc/ldap/certs -v $(pwd)/host-certs:/export-certs ldapdock ``` ## _2- Run the openLDAP server and populate a directory_ Use the following command to start openLDAP ``` root@example:/# slapd -h "ldap:/// ldapi:/// ldaps:///" -g openldap -u openldap ``` Create some groups and users to populate a directory ``` root@example:/# cat > add_content.ldif << EOF dn: ou=People,dc=example,dc=com objectClass: organizationalUnit ou: People dn: ou=Groups,dc=example,dc=com objectClass: organizationalUnit ou: Groups dn: cn=mages,ou=Groups,dc=example,dc=com objectClass: posixGroup cn: mages gidNumber: 5000 memberUid: marisa dn: uid=marisa,ou=People,dc=example,dc=com objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: marisa sn: Kirisame givenName: Marisa cn: Marisa Kirisame displayName: Marisa Kirisame uidNumber: 10000 gidNumber: 5000 userPassword: {CRYPT}x gecos: Marisa Kirisame loginShell: /bin/bash homeDirectory: /home/marisa EOF ``` When creating the groups and users, we will be asked the openLDAP root password (default: admin) ``` root@example:/# ldapadd -x -D cn=admin,dc=example,dc=com -W -f add_content.ldif ``` Notice the userPassword is invalid, let's set a proper one ``` root@example:/# ldappasswd -x -D cn=admin,dc=example,dc=com -w admin -s qwerty uid=marisa,ou=people,dc=example,dc=com ``` ## _3- Load and enable policies module_ Write the .ldif file and load the ppolicy.so module that comes with Debian libraries ``` root@example:/# cat > modify_ppolicy_module.ldif << 'EOF' dn: cn=module{0},cn=config changetype: modify add: olcModuleLoad olcModuleLoad: ppolicy.so EOF` ``` ``` ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f modify_ppolicy_module.ldif ``` Restart slapd to load the module (copy and paste the following as a single line) ``` root@example:/# slapd -h "ldap:/// ldapi:/// ldaps:///" -u openldap -g openldap & sleep 3 ``` Write the .ldif file to setup ppolicy.so on the openLDAP server ``` root@example:/# cat > enable_ppolicy.ldif << 'EOF' dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config changetype: add objectClass: olcOverlayConfig objectClass: olcPPolicyConfig olcOverlay: ppolicy EOF ``` ``` ldapadd -Q -Y EXTERNAL -H ldapi:/// -f enable_ppolicy.ldif ``` ## _4- Add schemas_ Let's add one of the policy schemas that comes with openLDAP, these files can be found in /etc/ldap/schema/. The pre-installed schemas exists in both converted .ldif files that can be loaded directly, as well native .schema formats which can be converted to .ldif files with the package schema2ldif (not loaded by default in this container) if neccesary. ``` root@example:/# ldapadd -Q -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/corba.ldif adding new entry "cn=corba,cn=schema,cn=config" ``` The following schemas will be loaded by default: ``` root@example:/# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn dn: cn=schema,cn=config dn: cn={0}core,cn=schema,cn=config dn: cn={1}cosine,cn=schema,cn=config dn: cn={2}nis,cn=schema,cn=config dn: cn={3}inetorgperson,cn=schema,cn=config dn: cn={4}corba,cn=schema,cn=config ``` ## _5- Configure TLS/SSL certificates_ Create cert directories and generate certificates ``` root@example:/# mkdir -p /etc/ldap/certs root@example:/# cd /etc/ldap/certs ``` CA key ``` root@example:/etc/ldap/certs# certtool --generate-privkey --bits 4096 --outfile ca-key.pem ``` CA template ``` root@example:/etc/ldap/certs# cat > ca.info < ldap01.info < ldap01_slapd_cert_full.pem root@example:/etc/ldap/certs# chown root:openldap ldap01_slapd_cert_full.pem root@example:/etc/ldap/certs# chmod 640 ldap01_slapd_cert_full.pem ``` \ Restart slapd (copy and paste as a single line) ``` root@example:/etc/ldap/certs# slapd -h "ldap:/// ldapi:/// ldaps:///" -u openldap -g openldap & sleep 3 ``` Re-apply TLS config ``` root@example:/etc/ldap/certs# cat > /tmp/certinfo.ldif <> ~/.bashrc root@example:/etc/ldap/certs# source ~/.bashrc ``` ## _6- Connect to OpenLDAP server via StartTLS/SSL_ Vital checks of different levels to test **openLDAP's StartTLS and SSL**:\ 1.Check StartTLS and SSL, both should output "anonymous" ``` root@example:/# ldapwhoami -x -ZZ -H ldap://${LDAP_HOST} anonymous root@example:/# ldapwhoami -x -H ldaps://${LDAP_HOST} anonymous ``` \ 2.Check direct connection via openssl to confirm certificates are working properly: ``` root@example:/# openssl s_client -connect ${LDAP_HOST}:389 -starttls ldap -servername ${LDAP_HOST} #StartTLS CONNECTED(00000003) depth=1 CN = Example Company CA verify return:1 depth=0 O = Example Company, CN = example.com verify return:1 ... SSL handshake has read 2977 bytes and written 424 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) root@example:/# openssl s_client -connect ${LDAP_HOST}:636 -servername ${LDAP_HOST} #SSL CONNECTED(00000003) depth=1 CN = Example Company CA verify return:1 depth=0 O = Example Company, CN = example.com verify return:1 ... SSL handshake has read 2963 bytes and written 393 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) ``` The output of both of these commands should be similar. Also, both will show the openLDAP's server CN (example.com in this case). You can terminate the connection with Ctrl+C. 3.A very important check is to make sure connections as users from the OpenLDAP's tree other than admin works: ``` root@example:/# ldapwhoami -x -D "uid=marisa,ou=People,dc=example,dc=com" -w MarisaNewPass2025 -H ldap://127.0.0.1 #StartTLS dn:uid=marisa,ou=People,dc=example,dc=com root@example:/# ldapwhoami -x -D "uid=marisa,ou=People,dc=example,dc=com" -w MarisaNewPass2025 -H ldap://127.0.0.1 #SSL dn:uid=marisa,ou=People,dc=example,dc=com ``` To connect to the server via `STARTTLS`, use port 389, to connect to the server via `SSL`, use port 636, both auth method Simple. If asked, accept the certificate as with any certificate, or copy the CA file that resides inside ldapdock from out of the container to our host system certificate trust directory (/usr/local/share/ca-certificates/ works for any Debian based distribution): ``` > sudo docker cp ldapdock:/etc/ldap/certs/ca-cert.pem ./mycacert.crt > sudo cp mycacert.crt /usr/local/share/ca-certificates/ > sudo update-ca-certificates ``` In both cases, providing -h ${LDAP_HOST}, by default the login "user" and password are:\ As admin: BIND DN="cn=admin,dc=example,dc=com"\ BIND password=admin As marisa: BIND DN="uid=marisa,ou=People,dc=example,dc=com"\ BIND password=MarisaNewPass2025