forked from ungleich-public/ungleich-user
Serve LDAPS, StartTLS and IPv6 from the dev LDAP
Production and the client's test server use ldaps://, so the dev server now offers TLS as well: ldaps on 636 (localhost:3636) and StartTLS on 389, with both ports also published on [::1]. A throwaway CA and server certificate are generated on every start, with the full CA extensions that Python 3.13+ strict verification requires. The CA certificate is copied to dev/ldap/export/ca.crt (gitignored). The server certificate covers localhost, the service names and ::1, but not 127.0.0.1, so tests can show that hostname mismatches are rejected.
This commit is contained in:
parent
c7dd2be8bc
commit
b9c043dcd4
5 changed files with 42 additions and 5 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -39,3 +39,6 @@ secret-key
|
|||
*.mo
|
||||
|
||||
venv/
|
||||
|
||||
# dev LDAP CA certificate, regenerated on every start
|
||||
dev/ldap/export/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ RUN apk add --no-cache \
|
|||
openldap \
|
||||
openldap-back-mdb \
|
||||
openldap-clients \
|
||||
openssl \
|
||||
&& mkdir -p /run/openldap /var/lib/openldap/openldap-data /etc/openldap/slapd.d \
|
||||
&& rm -rf /etc/openldap/slapd.conf /etc/openldap/slapd.ldif
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ COPY config.ldif openssh-lpk.ldif seed.ldif /bootstrap/
|
|||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 389
|
||||
EXPOSE 389 636
|
||||
HEALTHCHECK --interval=5s --timeout=3s --retries=10 \
|
||||
CMD ldapsearch -x -H ldap://localhost -b "" -s base namingContexts | grep -q "dc=ungleich,dc=ch" || exit 1
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ objectClass: olcGlobal
|
|||
cn: config
|
||||
olcArgsFile: /run/openldap/slapd.args
|
||||
olcPidFile: /run/openldap/slapd.pid
|
||||
olcTLSCACertificateFile: /etc/openldap/tls/ca.crt
|
||||
olcTLSCertificateFile: /etc/openldap/tls/server.crt
|
||||
olcTLSCertificateKeyFile: /etc/openldap/tls/server.key
|
||||
olcTLSProtocolMin: 3.3
|
||||
|
||||
dn: cn=module,cn=config
|
||||
objectClass: olcModuleList
|
||||
|
|
|
|||
|
|
@ -8,6 +8,28 @@ DATA=/var/lib/openldap/openldap-data
|
|||
|
||||
rm -rf "${CONF:?}"/* "${DATA:?}"/*
|
||||
|
||||
# Throwaway CA and server certificate, new on every start. The CA cert is
|
||||
# copied to /export (mounted from the host) so clients can verify the
|
||||
# server. 127.0.0.1 is left out on purpose: tests use it to prove that
|
||||
# hostname verification rejects a name the certificate does not cover.
|
||||
TLS=/etc/openldap/tls
|
||||
mkdir -p "$TLS"
|
||||
# Full CA extensions: Python 3.13+ verifies strictly by default and
|
||||
# rejects a CA without keyUsage.
|
||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 3650 \
|
||||
-subj "/CN=ungleich-user dev CA" -keyout "$TLS/ca.key" -out "$TLS/ca.crt" \
|
||||
-addext "basicConstraints=critical,CA:TRUE" \
|
||||
-addext "keyUsage=critical,keyCertSign,cRLSign" \
|
||||
-addext "subjectKeyIdentifier=hash" 2>/dev/null
|
||||
openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
|
||||
-subj "/CN=localhost" -keyout "$TLS/server.key" -out "$TLS/server.csr" 2>/dev/null
|
||||
printf 'subjectAltName=%s\nextendedKeyUsage=serverAuth\nkeyUsage=critical,digitalSignature\nbasicConstraints=CA:FALSE\nsubjectKeyIdentifier=hash\nauthorityKeyIdentifier=keyid\n' \
|
||||
"DNS:localhost,DNS:ldap,DNS:ldap1,DNS:ldap2,DNS:v6-ldap,IP:::1" > "$TLS/ext.cnf"
|
||||
openssl x509 -req -in "$TLS/server.csr" -CA "$TLS/ca.crt" -CAkey "$TLS/ca.key" \
|
||||
-CAcreateserial -days 3650 -extfile "$TLS/ext.cnf" -out "$TLS/server.crt" 2>/dev/null
|
||||
rm -f "$TLS/ca.key"
|
||||
if [ -d /export ]; then cp "$TLS/ca.crt" /export/ca.crt; fi
|
||||
|
||||
ADMIN_PW_HASH=$(slappasswd -s "${LDAP_ADMIN_PASSWORD:-admin}")
|
||||
|
||||
# cn=config: modules, schemas, database, ACLs
|
||||
|
|
@ -18,11 +40,11 @@ slapadd -n 0 -F "$CONF" -l /bootstrap/openssh-lpk.ldif
|
|||
# Directory tree and seed users
|
||||
slapadd -n 1 -F "$CONF" -l /bootstrap/seed.ldif
|
||||
|
||||
chown -R ldap:ldap "$CONF" "$DATA" /run/openldap
|
||||
chown -R ldap:ldap "$CONF" "$DATA" /run/openldap "$TLS"
|
||||
|
||||
# slapd sizes its descriptor table by the open-file limit; under some
|
||||
# runtimes (kind/containerd) that is ~10^9, and slapd is OOM-killed.
|
||||
ulimit -n 4096
|
||||
|
||||
# -d keeps slapd in the foreground; "stats" logs every operation to stdout.
|
||||
exec slapd -u ldap -g ldap -F "$CONF" -h "ldap:///" -d "${SLAPD_LOGLEVEL:-stats}"
|
||||
exec slapd -u ldap -g ldap -F "$CONF" -h "ldap:/// ldaps:///" -d "${SLAPD_LOGLEVEL:-stats}"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# Dev dependencies only. The Django app runs natively (no container) and
|
||||
# talks to LDAP on ldap://localhost:3389 and, if DATABASE_HOST=localhost is
|
||||
# set, to PostgreSQL on localhost:5433 (SQLite is used otherwise).
|
||||
# talks to LDAP on ldap://localhost:3389 (StartTLS) and ldaps://localhost:3636,
|
||||
# and, if DATABASE_HOST=localhost is set, to PostgreSQL on localhost:5433
|
||||
# (SQLite is used otherwise). The LDAP CA certificate is written to
|
||||
# dev/ldap/export/ca.crt on every start.
|
||||
#
|
||||
# docker compose -f docker-compose.dev.yml up -d --build
|
||||
# docker compose -f docker-compose.dev.yml up -d --force-recreate ldap # reset data
|
||||
|
|
@ -12,6 +14,11 @@ services:
|
|||
LDAP_ADMIN_PASSWORD: admin
|
||||
ports:
|
||||
- "127.0.0.1:3389:389"
|
||||
- "127.0.0.1:3636:636"
|
||||
- "[::1]:3389:389"
|
||||
- "[::1]:3636:636"
|
||||
volumes:
|
||||
- ./dev/ldap/export:/export
|
||||
|
||||
postgres:
|
||||
image: postgres:18-alpine
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue