Skip to content

LDAP & Active Directory Guard (ldap) ​

The LDAP Guard plugin inspects Lightweight Directory Access Protocol version 3 (RFC 4511) connections. It decodes ASN.1 BER message envelopes, tracks BindRequest credentials, detects failed bind operations (resultCode: 49 invalidCredentials), and blocks unauthorized bind DN patterns to stop directory enumeration and credential stuffing against Active Directory and OpenLDAP servers.


Capabilities & Threat Defense ​

Threat / Attack VectorDefense MechanismAction Taken
Credential Stuffing & Password SprayingIntercepts BindResponse with resultCode: 49; enforces connection close when cap is reached (since v3.4.0)IP banned after max_auth_failures threshold; connection terminated immediately
Directory EnumerationEnforces rate limits per minute and burst capsConnection throttled before overloading directory
Protected DN ProbingFilters Bind DN against blocked_bind_dn_prefixesBlocked at the proxy layer
DN Suffix SpoofingValidates BindDN against allowed_bind_dn_suffixes using case-folded literal suffix matching (since v3.4.0)Prevents Unicode homoglyph and attribute-reordering evasion attacks

Installation ​

Install the plugin via CLI or declaratively:

# Install using short nametcp-warden plugins install ldap# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/ldap

Configuration Reference ​

Add an LDAP guard service to tcp-warden.yaml:

services:  ldap_bastion:    listen: ":1390"    upstream: "127.0.0.1:389"    protocol: "ldap"    rate_limit:      connections_per_minute: 60      burst: 10    max_auth_failures: 3    ban_after_failures: 3    ban_duration: "2h"    plugin_config:      max_auth_failures: 3      blocked_bind_dn_prefixes:        - "cn=Administrator"        - "cn=root"

Configuration Options ​

FieldTypeDefaultDescription
listenstring":1390"Local proxy listen address and port.
upstreamstring"127.0.0.1:389"Target LDAP directory server address and port.
protocolstring"ldap"Must be set to "ldap".
max_auth_failuresint3Failed bind attempts before banning. Since v3.4.0 the connection is actively closed with an LDAP BindResponse resultCode: 52 (Unavailable) when the cap is reached.
ban_durationstring"2h"Duration of the automated IP ban ("30m", "2h", "24h").
plugin_config.blocked_bind_dn_prefixes[]string[]List of sensitive DN prefixes forbidden from authenticating via this proxy.
plugin_config.allowed_bind_dn_suffixes[]string[]If non-empty, only BindDN values ending with one of these suffixes are accepted. Matching is case-folded and uses literal suffix comparison to prevent Unicode homoglyph evasion (since v3.4.0).

Network & Deployment Architecture ​

Option A: Kernel Firewall Redirection (nftables / iptables) ​

Redirect external traffic destined for port 389 to RouteWarden port 1390:

# Redirect external traffic destined for port 389 to RouteWarden port 1390sudo nft add rule ip routewarden_nat prerouting iifname "eth0" tcp dport 389 redirect to :1390

Option B: LDAPS (Port 636) Protection ​

For secure LDAPS (LDAP over TLS), configure RouteWarden in front of the LDAPS listener:

# LDAPS (port 636 over TLS)services:  ldaps_guard:    listen: ":1636"    upstream: "127.0.0.1:636"    protocol: "generic"    rate_limit:      connections_per_minute: 60      burst: 10

Option C: Docker Compose Network Isolation ​

Run the LDAP directory server inside an isolated container network, exposing both LDAP (389) and LDAPS (636) services only through RouteWarden:

services:  routewarden:    image: ghcr.io/routewarden/tcp-warden:latest    ports:      - "389:1390"  # Map host LDAP port 389 to RouteWarden      - "636:1636"  # Map host LDAPS port 636 to RouteWarden      - "9091:9091" # Management API    volumes:      - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro    depends_on:      - openldap  openldap:    image: osixia/openldap:latest    environment:      - LDAP_ORGANISATION=Example Inc      - LDAP_ADMIN_PASSWORD=adminpassword    # Do NOT publish host ports (389/636); keep internal to bridge network

Testing & Verification ​

Verify queries and monitor failed bind responses:

# Verify valid search queryldapsearch -x -H ldap://127.0.0.1:1390 -b "dc=example,dc=org"

Released under the MIT License.