Skip to main content

LDAPGate

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 Directoryuid= and sAMAccountName= out of the box
  • Optional group gating — restrict access to members of a specific LDAP group
  • Header injection — injects X-Forwarded-User for 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
  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: NONE disables all TLS certificate validation and makes you vulnerable to man-in-the-middle attacks. Whenever possible, use tls_ca_cert_file to 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ldapgate-0.1.17.tar.gz (219.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ldapgate-0.1.17-py3-none-any.whl (139.9 kB view details)

Uploaded Python 3

File details

Details for the file ldapgate-0.1.17.tar.gz.

File metadata

  • Download URL: ldapgate-0.1.17.tar.gz
  • Upload date:
  • Size: 219.1 kB
  • Tags: Source
  • Uploaded using 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":null}

File hashes

Hashes for ldapgate-0.1.17.tar.gz
Algorithm Hash digest
SHA256 ac16d118ed4a62a22bb8100d66cf20d4d178004297ee1bf0edb5c62ee6e9ff03
MD5 07c7c81de6afa8d638fcc7006b3b35e4
BLAKE2b-256 11b66fc09359e5cca03582348654ed95f2f23019b21987086870ddcbda468a11

See more details on using hashes here.

File details

Details for the file ldapgate-0.1.17-py3-none-any.whl.

File metadata

  • Download URL: ldapgate-0.1.17-py3-none-any.whl
  • Upload date:
  • Size: 139.9 kB
  • Tags: Python 3
  • Uploaded using 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":null}

File hashes

Hashes for ldapgate-0.1.17-py3-none-any.whl
Algorithm Hash digest
SHA256 7f5c02036860b29dabc4efa81463acb7c289d312191872716cc0423da0b569c9
MD5 b098f777f42b01f48b4cf4dd29c0803e
BLAKE2b-256 9a2c7c0f7dde5035dcc4c959e6af41944c5f2797744829dd2f2b7a3e365f9a3c

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.21

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

This release

0.1.17 This release

2 files

0.1.16

2 files

0.1.15

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page