chldapasswd/README.md
Pierre-Olivier Mercier 69c307e7d6 docs: add README and dex custom theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 14:02:21 +07:00

5.8 KiB

chldapasswd

A self-hosted web portal for LDAP account management. Users can log in, view their profile, change their password, recover a lost password, and manage mail aliases — all without requiring direct LDAP access.

Features

  • Password change — authenticated users can update their LDAP password (SHA-512 crypt, minimum 12 chars, requires upper/lower/digit)
  • Lost password recovery — sends a one-time reset link via email (token expires in 1 hour)
  • Profile view — displays LDAP attributes after login
  • Mail alias management — create/delete auto-generated email aliases stored as mailAlias in LDAP, exposed via an addy.io-compatible API
  • HTTP Basic Auth endpoint (/auth) — validates credentials against LDAP, forwards X-Remote-User header; suitable for use with nginx auth_request
  • Docker registry anonymous read — optionally allows unauthenticated GET/HEAD on registry image paths via X-Special-Auth header
  • Altcha PoW CAPTCHA — proof-of-work challenge on sensitive forms, no third-party service required
  • CSRF protection — token-based on state-changing forms
  • Rate limiting — per-IP on login, password change, lost password, and alias API endpoints
  • Security headers — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy

Building

go build -ldflags="-s -w" -o chldapasswd

Requires Go 1.23+. A Drone CI pipeline builds and pushes nemunaire/chldapasswd:latest on each push to master.

Usage

chldapasswd [flags] [serve]
chldapasswd [flags] generate-lost-password-link <uid>

Flags

Flag Default Description
-bind 127.0.0.1:8080 Listen address
-baseurl / URL prefix (for reverse-proxy subpath deployment)
-config (none) Path to a JSON config file for LDAP settings
-public-url (none) Base URL used in password reset emails
-brand-name chldapasswd Brand name shown in the UI
-brand-logo (none) URL of a logo image shown in the UI
-addy-api-secret (none) HMAC secret for the alias API
-dev false Development mode: disables HSTS and cookie Secure flag

Environment variables

All LDAP and SMTP settings can be provided via environment variables (they override CLI flags and config file values):

Variable Description
LDAP_HOST LDAP server hostname
LDAP_PORT LDAP server port
LDAP_STARTTLS Enable STARTTLS (1/on/true)
LDAP_SSL Use LDAPS (1/on/true)
LDAP_BASEDN Base DN for searches
LDAP_SERVICEDN DN of the service account
LDAP_SERVICE_PASSWORD Password of the service account
LDAP_SERVICE_PASSWORD_FILE Path to a file containing the service password
SMTP_HOST SMTP server (leave empty to use local sendmail)
SMTP_PORT SMTP port
SMTP_USER SMTP username
SMTP_PASSWORD SMTP password
SMTP_PASSWORD_FILE Path to a file containing the SMTP password
SMTP_FROM Sender address for recovery emails
PUBLIC_URL Public base URL (overrides -public-url)
BRAND_NAME Brand name (overrides -brand-name)
BRAND_LOGO Brand logo URL (overrides -brand-logo)
ADDY_API_SECRET HMAC secret for the alias API
ALIAS_ALLOWED_DOMAINS Comma-separated list of domains users may create aliases under
DOCKER_REGISTRY_SECRET Shared secret for anonymous Docker registry read access

JSON config file

The -config flag accepts a JSON file whose fields map directly to the LDAP struct:

{
  "Host": "auth.example.com",
  "Port": 636,
  "Ssl": true,
  "BaseDN": "dc=example,dc=com",
  "ServiceDN": "cn=svc,ou=services,dc=example,dc=com",
  "ServicePassword": "secret",
  "MailHost": "smtp.example.com",
  "MailPort": 587,
  "MailUser": "mailer",
  "MailPassword": "secret",
  "MailFrom": "noreply@example.com"
}

HTTP endpoints

Method Path Description
GET/POST / or /change Password change form
GET/POST /login Login form — shows user profile on success
GET/POST /lost Lost password form
GET/POST /reset Password reset via token (from email link)
GET/POST /auth HTTP Basic Auth validation for reverse-proxy use
POST /api/v1/aliases Create a mail alias (addy.io-compatible)
DELETE /api/v1/aliases/{alias} Delete a mail alias
GET /altcha-challenge Fetch a PoW challenge for the Altcha widget

Mail alias API

The alias API is compatible with the addy.io API format. Tokens are HMAC-SHA224 signed and encoded in Base32:

# Create alias
curl -X POST https://auth.example.com/api/v1/aliases \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com"}'

# Delete alias
curl -X DELETE https://auth.example.com/api/v1/aliases/abc123%40example.com \
  -H "Authorization: Bearer <token>"

The token for a given uid is: base32(uid + ":" + HMAC-SHA224(apiSecret, uid)).

Alias creation is disabled unless ALIAS_ALLOWED_DOMAINS is set.

Templates

HTML templates are embedded from the static/ directory at build time. To customise the UI, edit the files in static/ before building.

The dextpl/ directory contains a matching theme for Dex to keep a consistent look across the SSO stack.

Docker

docker run -d \
  -e LDAP_HOST=auth.example.com \
  -e LDAP_PORT=636 \
  -e LDAP_SSL=true \
  -e LDAP_BASEDN=dc=example,dc=com \
  -e LDAP_SERVICEDN=cn=svc,ou=services,dc=example,dc=com \
  -e LDAP_SERVICE_PASSWORD=secret \
  -e SMTP_HOST=smtp.example.com \
  -e SMTP_PORT=587 \
  -e SMTP_FROM=noreply@example.com \
  -e PUBLIC_URL=https://auth.example.com \
  -p 8080:8080 \
  nemunaire/chldapasswd