LDAPGate
Lightweight LDAP/AD authentication gateway for Python web apps. Install it, configure it, done.
Features
- Two deployment modes — standalone reverse proxy or drop-in FastAPI middleware
- WebDAV + browser in one — browsers get a login form; WebDAV clients (Windows, macOS Finder, curl) get a Basic auth challenge — same endpoint, no extra config
- Pure Python LDAP — no OS-level libs required, uses
ldap3 - Signed cookie sessions — stateless, no server-side session storage
- OpenLDAP and Active Directory —
uid=andsAMAccountName=out of the box - Optional group gating — restrict access to members of a specific LDAP group
- Header injection — injects
X-Forwarded-Userfor downstream apps - Bundled login form — responsive, dark/light mode, customisable, works air-gapped
Install
pip install ldapgate
Config file
Both modes share the same ldapgate.yaml:
ldap:
url: ldaps://dc.example.com:636
bind_dn: CN=svc,CN=Users,DC=example,DC=com
bind_password: secret
base_dn: DC=example,DC=com
user_filter: "(sAMAccountName={username})" # AD; OpenLDAP: (uid={username})
# LDAPGate requires at least one authorization rule by default:
group_dn: CN=app-users,CN=Users,DC=example,DC=com # restrict by group
allowed_users: # or restrict by local allowlist
- alice
- bob
timeout: 10 # LDAP operation and total login deadline
tls_validate: REQUIRED # NONE | OPTIONAL | REQUIRED
tls_ca_cert_file: /etc/ssl/certs/internal-ca.pem # optional — custom CA bundle
tls_client_cert_file: /etc/ssl/certs/client.pem # optional — mutual TLS client cert
tls_client_key_file: /etc/ssl/private/client.key # optional — mutual TLS client key
proxy:
listen_host: 0.0.0.0
listen_port: 9000
backend_url: http://localhost:8080
secret_key: change-me-to-something-random
session_ttl: 3600
idle_timeout: 0 # set >0 to expire inactive browser sessions
user_header: X-Forwarded-User
login_path: /_auth/login
logout_path: /_auth/logout
app_name: MyApp
mask_usernames_in_logs: true # set false to log full usernames
secure_cookies: false # set true when behind HTTPS
All settings can also be provided via environment variables using __ as a separator — e.g. LDAP__URL, PROXY__SECRET_KEY.
Corporate / Active Directory setup
For corp environments with internal CAs where cert validation isn't feasible:
ldap:
url: ldaps://dc.example.com:636
tls_validate: NONE
# ... other settings ...
⚠️ Warning:
tls_validate: NONEdisables all TLS certificate validation and makes you vulnerable to man-in-the-middle attacks. Whenever possible, usetls_ca_cert_fileto trust your internal CA instead.
For plain LDAP with STARTTLS:
ldap:
url: ldap://dc.example.com:389
use_starttls: true
Mode 1 — Standalone Reverse Proxy
Run ldapgate as a standalone process in front of any app.
Browser / WebDAV client → ldapgate :9000 → backend app :8080
ldapgate serve --config ldapgate.yaml
All traffic is intercepted by ldapgate before reaching the backend. Authenticated requests are forwarded with the X-Forwarded-User header set to the verified username. Apps can point their logout link at the configured logout_path (default /_auth/logout) to clear the session.
WebDAV clients receive a 401 WWW-Authenticate: Basic challenge automatically and authenticate per-request via HTTP Basic auth — no session cookie needed.
Mode 2 — FastAPI Middleware
Drop ldapgate auth directly into an existing FastAPI app — no separate process.
from fastapi import FastAPI
from ldapgate.config import load_config
from ldapgate.middleware import add_ldap_auth
app = FastAPI()
config = load_config("ldapgate.yaml")
add_ldap_auth(app, config)
@app.get("/api/data")
async def data(request):
return {"user": request.state.user} # authenticated username
add_ldap_auth registers the login/logout routes and attaches the middleware in one call. The authenticated username is available as request.state.user and is also injected as the configured user_header into the request headers.
WebDAV with middleware
The middleware handles both browser and WebDAV clients on the same app instance:
| Client | Auth flow |
|---|---|
| Browser | Redirected to login form → session cookie |
| WebDAV (Windows, macOS Finder, curl) | 401 WWW-Authenticate: Basic challenge → Basic auth per-request |
No extra routes or config needed — if a request arrives without a session cookie and without Accept: text/html, the middleware issues a 401 with a WWW-Authenticate: Basic header. The client sends credentials, the middleware validates against LDAP, and the request proceeds.
Successful Basic auth checks are cached briefly in memory by default (proxy.basic_auth_cache_ttl: 60) so chatty WebDAV clients like macOS Finder do not force a fresh LDAP bind for every metadata request. Set it to 0 to disable the cache.
allowed_users and group_dn apply to both flows — a user blocked by those settings is rejected regardless of whether they authenticated via cookie or Basic auth.
Example — xwing file server with WebDAV:
from fastapi import FastAPI
from ldapgate.config import load_config
from ldapgate.middleware import add_ldap_auth
from xwing.app import create_app
from xwing.config import Settings
xwing_settings = Settings(root_dir="/srv/files", users_config="users.yaml")
app = create_app(xwing_settings)
ldap_config = load_config("ldapgate.yaml")
add_ldap_auth(app, ldap_config)
Windows users can now map http://your-server:8989/ as a network drive:
net use Z: http://your-server:8989/ /user:alice /persistent:yes
macOS Finder: Go → Connect to Server (⌘K) → http://your-server:8989/
CLI reference
ldapgate serve [OPTIONS]
--config PATH Path to ldapgate.yaml (reads env vars if omitted)
--host TEXT Override listen host
--port INTEGER Override listen port
--backend TEXT Override backend URL
--reload Enable auto-reload (dev only)
Development
Requires uv.
git clone https://github.com/anudeepd/ldapgate
cd ldapgate
uv sync
uv run pytest
License
MIT
Metadata
Release files for ldapgate 0.1.28
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ldapgate-0.1.28.tar.gz | 156.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ldapgate-0.1.28-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 300.6 kB
Release files / ldapgate-0.1.28.tar.gz
| Download URL | ldapgate-0.1.28.tar.gz |
|---|---|
| Size | 156.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5c0e907ee2284c80303f91a8db3126f0e55bd16d1c362ba22efeaee83ac5ecd2
|
|
BLAKE2b-256 checksum How to use checksums |
732da39a46d7cc857b27ff6f518c43c94b7bc520261871cbfbf84dcc1b879fdf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / ldapgate-0.1.28-py3-none-any.whl
| Download URL | ldapgate-0.1.28-py3-none-any.whl |
|---|---|
| Size | 143.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b0b824be40b9da208359774d883a48e4538fc04a64bd2a4d3ad87d3dfe6b124
|
|
BLAKE2b-256 checksum How to use checksums |
1203306a418c3f4b3de5b8aaba1a6b229fb5042bf000d18ad4d1ae2e8ea0f702
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|