Skip to main content

[!NOTE] The code in this repository is written with the help of AI (specifically, ChatGPT 5), including this README (lazy, I know). Although I tried my best to streamline and validate most of it, there may still be some intricacies that need to be sorted out.

Airflow LDAP Auth Manager

A drop-in Auth Manager for Apache Airflow 3.x that authenticates users against LDAP/Active Directory and maps LDAP groups to Airflow roles (admin / editor / viewer). It supports redundant LDAP servers, secure transport (LDAPS or StartTLS), and secret indirection for bind credentials via Airflow’s Secrets Backend (Variables).

Features

  • LDAP/AD authentication using ldap3
  • Group → role mapping (admin > editor > viewer)
  • Redundancy & failover via ldap3.ServerPool (ROUND_ROBIN by default)
  • TLS: LDAPS or StartTLS (with certificate verification)
  • Secrets-friendly config: bind_dn_secret and bind_password_secret read from Airflow Variables (resolved through your configured secrets backend)
  • Clean Airflow 3 API: uses the Airflow SDK (airflow.sdk.*)
  • Simple login UI with configurable instance name and login tip
  • Helpful logging: logs which LDAP server the connection bound to

To-do

  • Extend the user & group search base config items to allow multiple entries (or LDAP OR syntax)
  • Package this and upload to pypi

Requirements

  • Python: 3.12+
  • Airflow: 3.1+ (Auth Manager interface & SDK)
  • Libraries: ldap3, fastapi, jinja2 (fastapi & jinja2 is likely installed already as Airflow dependencies)
  • Optional (recommended): a Secrets Backend (Vault, AWS Secrets Manager, GCP SM, Azure KV) configured for Airflow Variables

Installation

Install from PyPi into the environment where Airflow is installed:

pip install airflow-ldap-auth-manager

Or install after cloning the project:

git clone <repo url>
cd airflow-ldap-auth-manager
pip install .

Configure Airflow

Changes needed in airflow.cfg:

Enable the Auth Manager

[core]
# Fully qualified path to the auth manager class in this repo
auth_manager = airflow_ldap_auth_manager.LdapAuthManager

Make sure JWT settings are configured correctly

[api_auth]
jwt_secret = # Needs to be set
jwt_algorithm = # Either leave empty, or make sure consistent across all machines
jwt_audience = # Either leave empty, or make sure consistent across all machines
jwt_issuer = # Either leave empty, or make sure consistent across all machines

LDAP settings

Add a section for the LDAP auth manager (adjust to your environment):

[ldap_auth_manager]
server_uri = ldaps://ldap1.example.com:636,ldaps://ldap2.example.com:636
bind_dn =
bind_password =
bind_dn_secret = secret/path/to/airflow/variable/for/bind_dn
bind_password_secret = path/to/airflow/variable/for/bind_password
user_search_base = OU=Users,DC=company_name
user_search_filter = (|(uid={username})(sAMAccountName={username})(mail={username}))
group_search_base = OU=Groups,DC=company_name
group_member_attr = member
admin_groups = airflow-admins
editor_groups = airflow-editors
viewer_groups = airflow-viewers,airflow-auditors
username_attr = uid
email_attr = mail
start_tls = false
verify_ssl = true
post_login_redirect = /
logout_redirect = /
debug_logging = false

Environment variable overrides are supported in the standard Airflow fashion, e.g.:`

AIRFLOW__LDAP_AUTH_MANAGER__BIND_DN_SECRET=api_server/ldap_auth_manager/bind_dn

Branding & login hint (optional)

[api]
# Human-friendly name for titles/headers on the login page
instance_name = Company Airflow

[ldap_auth_manager]
# Optional helper text shown under "Sign in"
login_tip = Using your Company credentials

Restart the api-server after changes.

Configuration reference

[ldap_auth_manager]
# LDAP authentication/authorization settings for LdapAuthManager.
# This section supports multiple redundant servers, secure transport (LDAPS or StartTLS),
# and secret indirection for bind credentials via Airflow’s Secrets Backend.

# Comma-separated list of LDAP server URIs.
# - Supports ldap:// and ldaps:// schemes.
# - When multiple URIs are provided, the manager builds an ldap3 ServerPool with ROUND_ROBIN
#   strategy for load distribution and failover.
# - For ldaps://, TLS is implicit from connect; for ldap:// + start_tls=true, StartTLS is
#   negotiated before bind.
# - Mixing ldaps:// with start_tls=true is not meaningful; prefer one approach.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__SERVER_URI
#
server_uri = ldaps://ldap1.example.com:636,ldaps://ldap2.example.com:636

# Name of the Airflow Variable that contains the LDAP bind DN (service account).
# The Variable is resolved through the configured Secrets Backend (e.g. Vault, AWS SM, etc.).
# If set, any plaintext `bind_dn` value is ignored. Leave empty to attempt anonymous bind.
# Remember: This needs to be set in the "Airflow/Variables" section in your secret manager,
# NOT "Airflow/Config"!
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__BIND_DN_SECRET
#
bind_dn_secret = secret/path/to/airflow/variable/for/bind_dn
bind_dn =

# Name of the Airflow Variable that contains the LDAP bind password (service account).
# The Variable is resolved through the configured Secrets Backend.
# If set, any plaintext `bind_password` value is ignored.
# Remember: This needs to be set in the "Airflow/Variables" section in your secret manager,
# NOT "Airflow/Config"!
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__BIND_PASSWORD_SECRET
#
bind_password_secret = path/to/airflow/variable/for/bind_password
bind_password =

# Base DN under which user entries are searched.
# Example (Active Directory): OU=Users,OU=Country,DC=example,DC=com
# Supports multiple base DNs, separated by newlines, semicolons, or as a JSON array.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__USER_SEARCH_BASE
#
user_search_base = OU=Users,DC=company_name

# LDAP filter template to locate the authenticating user.
# The literal "{username}" placeholder is replaced with the submitted login identifier.
# Must be a valid RFC 4515 filter. The username value is safely escaped before substitution.
# Example: (|(uid={username})(sAMAccountName={username})(mail={username}))
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__USER_SEARCH_FILTER
#
user_search_filter = (|(uid={username})(sAMAccountName={username})(mail={username}))

# Base DN under which group entries are searched for authorization.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__GROUP_SEARCH_BASE
#
group_search_base = OU=Groups,DC=company_name

# The group attribute that lists membership (DNs of user entries).
# Common values:
#   - Active Directory: member
#   - RFC2307/posix groups: memberUid (then matching is by username instead of DN)
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__GROUP_MEMBER_ATTR
#
group_member_attr = member

# Comma-separated list of groups that grant the Airflow "admin" role.
# Values can be group CNs or full DNs under group_search_base (matching is case-insensitive).
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__ADMIN_GROUPS
#
admin_groups = airflow-admins


# Comma-separated list of groups that grant the Airflow "editor" role.
# Leave empty to disable this mapping.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__EDITOR_GROUPS
#
editor_groups = airflow-editors

# Comma-separated list of groups that grant the Airflow "viewer" role.
# Users are mapped to the highest role matched (admin > editor > viewer).
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__VIEWER_GROUPS
#
viewer_groups = airflow-viewers,airflow-auditors

# Attribute on the user entry to use as the Airflow username.
# Typical values: uid, sAMAccountName, userPrincipalName
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__USERNAME_ATTR
#
username_attr = uid

# Attribute on the user entry that contains the email address.
# Typical values: mail, userPrincipalName
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__EMAIL_ATTR
#
email_attr = mail

# Whether to perform StartTLS on ldap:// connections before bind.
# - true  : Use StartTLS (only applies to ldap:// URIs).
# - false : Do not use StartTLS. For ldaps:// URIs, TLS is already implicit.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__START_TLS
#
start_tls = false

# Whether to verify the server certificate for TLS (ldaps or StartTLS).
# Set to true in production with a valid trust store. Set to false only for testing.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__VERIFY_SSL
#
verify_ssl = true

# Path (relative to the Airflow web root) to redirect a user after successful login.
# Example: "/" or "/home"
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__POST_LOGIN_REDIRECT
#
post_login_redirect = /

# Path (relative to the Airflow web root) to redirect a user after logout.
# Example: "/" or "/login"
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__LOGOUT_REDIRECT
#
logout_redirect = /

# Enable debug logging for LDAP operations.
# This can be useful to troubleshoot LDAP issues, but beware that it may log
# sensitive information such as usernames and group names.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__DEBUG_LOGGING
#
debug_logging = false

# Optional login hint shown under "Sign in" title. Leave empty to hide.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__LOGIN_TIP
#
login_tip = Using your Company credentials

# Optional override to the support hint shown under the "Sign in" button.
#
# Variable: AIRFLOW__LDAP_AUTH_MANAGER__SUPPORT_TIP
#
support_tip = Having issues? Raise a ticket to the helpdesk.

UI & templates

  • Login template: ldap_login.html
  • Static assets are served under /auth/static/ (e.g. /auth/static/style.css, /auth/static/airflow.svg)
  • Form posts to /auth/token

If you see 404 Not Found for /auth/static/..., ensure the StaticFiles mount path in your FastAPI router matches the URLs used in the template.

Logging & diagnostics

If debug_logging = true in config:

After successful bind, the manager logs which LDAP server was selected from the pool, e.g.:

LDAP bound to ldaps://ldap2.example.com:636 (pool_strategy=ROUND_ROBIN, start_tls=False)

Optionally, it can log the authenticated identity via the LDAP whoami extended operation.

Security notes

  • Choose one TLS mode: either ldaps://... or ldap:// with start_tls=true. Mixing them is discouraged.
  • Keep verify_ssl = true in production and ensure your trust store contains the issuing CA(s).
  • Bind credentials should preferrably be supplied via *_secret indirection (Variables → Secrets Backend), not plaintext.

Troubleshooting

  • Can’t bind / invalid credentials: test with ldapsearch using the same DN/password and base/filter.
  • User not found: verify user_search_base and user_search_filter (remember {username} substitution).
  • Group mapping not applied: confirm group_search_base, group_member_attr, and that your groups are in the configured bases.
  • TLS errors: verify certificates and CA chain; ensure the hostname matches the server certificate CN/SAN.
  • Static assets 404: check the FastAPI StaticFiles mount matches /auth/static.

Contributing

Issues and PRs are welcome. Please include:

  • A clear description of the problem or feature
  • Repro steps or tests when possible
  • Your Airflow, Python, and LDAP server versions

Download files

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

Source Distribution

airflow_ldap_auth_manager-0.1.1.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

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

airflow_ldap_auth_manager-0.1.1-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file airflow_ldap_auth_manager-0.1.1.tar.gz.

File metadata

File hashes

Hashes for airflow_ldap_auth_manager-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f24d2abfcc05bb4741ba207f21bfa20f1b8f6bc8934d264bafda284ea4230c8a
MD5 216fad828731ccc57e39361f3fe4f562
BLAKE2b-256 ae01dd13cdeec7910079241f3b2cee14dae283339a876a1029c1a556dd8f0807

See more details on using hashes here.

File details

Details for the file airflow_ldap_auth_manager-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for airflow_ldap_auth_manager-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cf71c0519c385fca8bcd8f354e1f2a4d4ae8ace020efeff8046aca204cee65c3
MD5 c15d33bca28d20fb743eb138c337fffb
BLAKE2b-256 02a0829d1ad047d053e1ec023422a3ea9b378d5b7bf14a48a72f1580a56a45d1

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

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