- Python 35.5%
- CSS 31.5%
- HTML 17.6%
- JavaScript 12.6%
- Shell 1.9%
- Other 0.9%
ldap3 does not verify TLS certificates by default, and the app never configured it to. ldaps:// and StartTLS were encrypted but accepted any certificate, so anyone able to intercept LDAP traffic could pose as the server and collect the admin password and every password used to log in. All LDAP servers now use VerifiedTls, which leaves verification to Python's ssl module: certificate chain and hostname, IP literals included (ldap3's own matcher cannot handle IPv6 literals). It also sends SNI and requires TLS 1.2 or newer. LDAP_CA_FILE adds a CA bundle for a private CA. LDAP_TLS_VERIFY=False turns verification off, with a logged warning. A failed StartTLS now always raises instead of continuing in plaintext. New tests against the dev LDAP cover ldaps, StartTLS and ldaps over an IPv6 literal with the CA (and check the socket is encrypted), plain LDAP over IPv6, rejection of an untrusted certificate (ldaps and StartTLS), rejection of a hostname mismatch, and the opt-out. |
||
|---|---|---|
| dal | ||
| deploy/k8s | ||
| dev | ||
| .dockerignore | ||
| .gitignore | ||
| docker-compose.dev.yml | ||
| Dockerfile | ||
| env.sample | ||
| gunicorn.conf.py | ||
| manage.py | ||
| README.md | ||
| requirements.txt | ||
ungleich account webapp
This service runs on account.ungleich.ch. Customers use it to manage their ungleich LDAP account: register (with email confirmation), log in, change their name, email or password, reset a forgotten password, delete the account, and view their OTP seeds. It also offers a small API for creating accounts and fetching OTP seeds.
Django 6.1 on Python 3.15. LDAP access goes through ldap3 only, with no C extensions.
Development
You need Docker and uv (or any Python 3.15). Only LDAP (and optionally PostgreSQL) runs in containers. The app runs natively.
docker compose -f docker-compose.dev.yml up -d --build # OpenLDAP 2.6 on :3389, PostgreSQL on :5433
cp dev/env.dev .env # dev-only settings
uv venv -p 3.15 .venv && uv pip install -r requirements.txt
.venv/bin/python manage.py migrate
.venv/bin/python manage.py runserver
The test LDAP mirrors the production layout (dc=ungleich,dc=ch,
ou=customers, ou=users) and comes with customer1 / customerpass
and staff1 / staffpass. To reset its data:
docker compose -f docker-compose.dev.yml up -d --force-recreate ldap.
Tests
.venv/bin/python manage.py test dal
The tests in dal/tests/ exercise every view over HTTP against the real
test LDAP server, which must be running. Mail and the OTP server are
mocked. Set DATABASE_HOST=localhost DATABASE_PORT=5433 DATABASE_PASSWORD=dev-only to run them on PostgreSQL instead of SQLite.
Configuration
Every setting is an environment variable (a .env file in development).
env.sample lists each one, with its default and whether it
is a secret.
Deployment
The Dockerfile builds a production image: gunicorn running
as non-root, static files served by WhiteNoise, and logs on stdout. To
deploy on Kubernetes, and to test locally with kind, see
deploy/k8s/README.md.
Layout
| Path | Contents |
|---|---|
dal/views.py |
All views |
dal/ungleich_ldap.py |
LDAP writes and searches (LdapManager, server pool) |
dal/backends.py |
LDAP login backend |
dal/health.py |
/healthz and /readyz |
dal/tests/ |
End-to-end tests |
dev/ |
Dev settings and the test OpenLDAP image |
deploy/k8s/ |
Kubernetes manifests (base and kind dev overlay) |