OpenLDAP Directory¶
OpenLDAP provides a supplementary LDAP directory for services that require LDAP-based authentication or group lookups. Access is Tailscale-only. The web UI (phpLDAPadmin) is at
https://openldap.shared.cwiq.io.
Connection Details¶
| Property | Value |
|---|---|
| LDAP URI | ldap://openldap-shared-cwiq-io:389 |
| Base DN | dc=cwiq,dc=io |
| Admin DN | cn=admin,dc=cwiq,dc=io |
| People OU | ou=People,dc=cwiq,dc=io |
| Groups OU | ou=Groups,dc=cwiq,dc=io |
| phpLDAPadmin | https://openldap.shared.cwiq.io |
| Access | Tailscale-only (openldap-shared-cwiq-io) |
Tailscale-only access
OpenLDAP is not accessible from the public internet. All clients must connect via Tailscale using the MagicDNS hostname openldap-shared-cwiq-io. LDAP binds from outside the tailnet will be refused.
Directory Structure¶
dc=cwiq,dc=io
├── ou=People ← user accounts (inetOrgPerson)
├── ou=Groups
│ ├── ou=cwiq ← CWIQ org groups
│ │ ├── cn=admins
│ │ ├── cn=team-leaders
│ │ └── cn=members
│ └── ou=default ← default org groups
│ ├── cn=admins
│ ├── cn=team-leaders
│ └── cn=members
└── ou=ServiceAccounts ← service bind accounts
Org-scoped groups live under ou=Groups,dc=cwiq,dc=io. Each organization slug gets its own sub-OU with the three standard groups (admins, team-leaders, members).
Docker Stack¶
| Container | Image | Ports | Purpose |
|---|---|---|---|
openldap-slapd |
bitnami/openldap:2.6.9 |
389, 636 | LDAP directory (slapd) |
openldap-phpldapadmin |
osixia/phpldapadmin:0.9.0 |
internal 8080 | Web management UI |
openldap-nginx |
nginx:1.27-alpine |
443, 80 | SSL termination |
All images pull through Nexus proxy (nexus.shared.cwiq.io:8444).
Common LDAP Operations¶
Test Connection¶
# Verify LDAP server is responding (anonymous bind)
ldapsearch -x \
-H ldap://openldap-shared-cwiq-io:389 \
-b "" -s base namingContexts
Search All Users¶
ldapsearch -x \
-H ldap://openldap-shared-cwiq-io:389 \
-D "cn=admin,dc=cwiq,dc=io" -W \
-b "ou=People,dc=cwiq,dc=io" \
"(objectClass=inetOrgPerson)" uid cn mail
Search All Groups¶
ldapsearch -x \
-H ldap://openldap-shared-cwiq-io:389 \
-D "cn=admin,dc=cwiq,dc=io" -W \
-b "ou=Groups,dc=cwiq,dc=io" \
"(objectClass=groupOfNames)" cn member
Search Specific User¶
ldapsearch -x \
-H ldap://openldap-shared-cwiq-io:389 \
-D "cn=admin,dc=cwiq,dc=io" -W \
-b "ou=People,dc=cwiq,dc=io" \
"(uid=john.doe)"
Service Operations¶
Check Container Status¶
ssh openldap@openldap-shared-cwiq-io
sudo -u openldap docker compose -f /data/openldap/docker-compose.yml ps
View Logs¶
Restart¶
Stop¶
Deployment¶
All configuration is in group_vars/all.yml.template. The template must be copied to inventory/shared/group_vars/all.yml and all CHANGE_ME password values set before running the playbook.
Key Configuration Variables¶
| Variable | Description |
|---|---|
openldap_domain |
LDAP domain (cwiq.io → dc=cwiq,dc=io) |
openldap_base_dn |
Base DN (dc=cwiq,dc=io) |
openldap_admin_username |
LDAP admin username |
openldap_admin_password |
Admin password (must change from CHANGE_ME) |
openldap_org_names |
Org slugs — each gets a sub-OU under ou=Groups |
openldap_users |
Seed users |
openldap_org_groups |
Org-scoped groups |
openldap_service_accounts |
Service bind accounts |
Deploy¶
ssh ansible@ansible-shared-cwiq-io
ansible-helper
# Provision server
ansible-playbook openldap/setup.yml -i openldap/inventory/shared.yml
# Deploy SSL certificate (from cert-server)
ansible-playbook cert-server/ssl-deploy-all.yml --limit openldap-shared-cwiq-io
# Deploy OpenLDAP stack
ansible-playbook openldap/deploy-openldap.yml -i openldap/inventory/shared.yml
SSL Certificate¶
The SSL certificate (openldap.shared.cwiq.io) is managed by cert-server:
- Key type: ECDSA
- Cert path on server:
/data/ssl/openldap.shared.cwiq.io/ - Reload command:
docker restart openldap-nginx - Renewal: automatic via
ssl-renew-deploy.timeron the cert-server
Related Documentation¶
- SSL: Architecture — How OpenLDAP SSL is managed
- SSL: Inventory — OpenLDAP in the 23-host certificate inventory