feat: add ldap-auth-advanced plugin (core authentication) - #13762
Open
janiussyafiq wants to merge 5 commits into
Open
feat: add ldap-auth-advanced plugin (core authentication)#13762janiussyafiq wants to merge 5 commits into
janiussyafiq wants to merge 5 commits into
Conversation
Add the ldap-auth-advanced plugin (priority 2541), Kong ldap-auth-advanced parity, core authentication: search-then-bind (AD sAMAccountName shape), service-account or anonymous search bind, user_dn Consumer association, LDAPS/StartTLS, LDAP filter-injection defense, strict 401-vs-500 auth/transport error split, multi-auth compatibility. Bump lua-resty-ldap 0.1.0 -> 0.3.0 (pure-Lua client/protocol/filter; the released client is lazy-connect, so the plugin binds first and classifies a (nil, err) return as a transport failure). Note: under 0.1.0 the ldap-auth plugin's tls_verify was a silent no-op (the library read verify_ldap_host); 0.3.0 fixes the field name, so tls_verify:true configs begin real certificate verification. Also regenerate t/certs/localhost_slapd_cert.pem: the old cert has CN=test.com but no subjectAltName, and nginx's hostname verification does not fall back to CN, so any real tls_verify handshake against it fails with "certificate host mismatch". The new cert (same key, same test CA) adds SAN DNS:test.com, DNS:localhost, IP:127.0.0.1. Group collection/authorization and credential caching/anonymous-consumer follow in separate PRs; docs follow up separately.
nic-6443
reviewed
Jul 30, 2026
nic-6443
reviewed
Jul 30, 2026
nic-6443
reviewed
Jul 30, 2026
nic-6443
reviewed
Jul 30, 2026
nic-6443
reviewed
Jul 30, 2026
nic-6443
reviewed
Jul 30, 2026
- strip client-supplied X-Consumer-Username/X-Consumer-Custom-ID/ X-Credential-Identifier (with X-Authenticated-Groups) before any auth work, so consumer_required=false cannot pass spoofed identity upstream - accept RFC 4512 numeric-OID attribute types and tighten ;option validation in the attribute schema pattern - fall back to Authorization when Proxy-Authorization is present but does not parse into credentials for header_type - classify directory failures by operation and result code: 401 only for invalidCredentials on the user bind plus the not-found/ambiguous cases (including sizeLimitExceeded, the >size_limit ambiguity); service-bind rejections and search operational failures are 500 - look up Consumers via consumer.find_consumer() so a secret-ref user_dn resolves (and unresolved references fail closed) - register ldap-auth-advanced user_dn in plugin_unique_key_attrs so the Admin API rejects duplicate user_dn values
| title = "work with route or service object", | ||
| properties = { | ||
| -- connection | ||
| ldap_uri = { type = "string" }, -- "host[:port]" |
Contributor
There was a problem hiding this comment.
can we add min and max lengths here? (and other relevant fields)?
There was a problem hiding this comment.
Pull request overview
This PR introduces a new core authentication plugin, ldap-auth-advanced, implementing search-then-bind LDAP authentication (including LDAPS/StartTLS, Consumer user_dn mapping, and filter-injection defenses) and updates the CI LDAP fixtures and plugin registration so it’s available and testable within APISIX.
Changes:
- Added
apisix/plugins/ldap-auth-advanced.luaplus a comprehensive LDAP integration test suite (t/plugin/ldap-auth-advanced.t). - Registered the plugin across admin/plugin listing and default config examples, and enforced Consumer unique-key duplication checks for
user_dn. - Updated the OpenLDAP CI container setup (bootstrap LDIF + ACL hook) and bumped
lua-resty-ldapto0.3.0.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
apisix/plugins/ldap-auth-advanced.lua |
New advanced LDAP auth plugin implementation (search-then-bind, TLS options, Consumer association). |
t/plugin/ldap-auth-advanced.t |
New end-to-end test coverage for the plugin, including injection defenses and multi-auth behavior. |
apisix/consumer.lua |
Adds ldap-auth-advanced to the auth-plugin unique key map for Consumer duplicate checks. |
apisix/cli/config.lua |
Registers ldap-auth-advanced in the built-in plugin list. |
t/admin/plugins.t |
Extends admin plugin list/priority assertions to include the new plugin. |
t/admin/consumers.t |
Adds Consumer duplicate user_dn coverage for ldap-auth-advanced. |
conf/config.yaml.example |
Documents the plugin in the example plugin list with correct priority ordering. |
apisix-master-0.rockspec |
Bumps lua-resty-ldap dependency to 0.3.0-0. |
ci/pod/docker-compose.plugin.yml |
Updates OpenLDAP service config and mounts new bootstrap fixtures/hooks. |
ci/pod/openldap/ad.ldif |
Provides a deterministic LDAP directory tree for CI (including advanced-plugin fixtures). |
ci/pod/openldap/enable-anon-bind.sh |
Configures OpenLDAP to allow unauthenticated bind and installs ACLs used by tests. |
t/certs/localhost_slapd_cert.pem |
Updates test cert material (notably SANs) to support hostname verification paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
nic-6443
reviewed
Jul 31, 2026
nic-6443
reviewed
Jul 31, 2026
This was referenced Aug 3, 2026
Reject empty usernames and passwords while parsing a credential header,
rather than after a header has already been chosen. In Lua "" is truthy,
so an unusable Proxy-Authorization such as "ldap OnBhc3M=" (":pass") or
"ldap dXNlcjo=" ("user:") satisfied the caller's truthiness check, won
proxy priority, and returned 401 without ever trying a valid
Authorization. Both headers now share the rule, which makes the later
checks in rewrite() redundant.
Also stop rejecting a trailing "~" as an unusable username. RFC 4515
UTF1SUBSET (%x5D-7F) includes "~", so "admin~" is a legal assertion
value and a legal directory user: it must reach the search and 401 as
not-found instead of being turned away up front. That depends on the
filter escaping fix in lua-resty-ldap, so TEST 46 fails until the
rockspec is bumped to the release carrying it.
0.3.1 escapes `~` as `\7e` when building a search filter. RFC 4515 UTF1SUBSET (%x5D-7F) includes `~`, but the bundled filter grammar rejects it raw at either end of a value, so a legal directory user such as `admin~` could not be searched for. This is what TEST 46 expects. 0.3.1 also relaxes its own pins from `lpeg = 1.0.2-1` to `lpeg >= 1.0.2` (and `lua_pack` likewise). The exact pin downgraded the lpeg that `graphql` and `jsonpath` resolve, so APISIX now keeps its own version.
nic-6443
approved these changes
Aug 3, 2026
shreemaan-abhishek
approved these changes
Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds the
ldap-auth-advancedplugin (priority 2541), bringing Kongldap-auth-advancedparity for core authentication: search-then-bind (ADsAMAccountNameshape), service-account or anonymous search bind,user_dnConsumer association, LDAPS/StartTLS, LDAP filter-injection defense, and a strict 401-vs-500 auth/transport error split. This is part 1 of 3 (core authentication); group collection/authorization and credential caching/anonymous-consumer follow in separate PRs. Prior discussion in #8958.This PR also bumps
lua-resty-ldap0.1.0 -> 0.3.0. Under 0.1.0, the existingldap-authplugin'stls_verifyoption was a silent no-op (the library read a different field name); 0.3.0 fixes this, sotls_verify: trueconfigs now perform real certificate verification. This also required regeneratingt/certs/localhost_slapd_cert.pem(same key, same test CA) to add asubjectAltName(DNS:test.com, DNS:localhost, IP:127.0.0.1) — the old cert hadCN=test.combut no SAN, and nginx's hostname verification does not fall back to CN, so the newly-realldap-authTLS-verify test started failing a certificate host-mismatch check that the 0.1.0 bug had been silently masking.Which issue(s) this PR fixes:
Related: #8958
Checklist
lua-resty-ldapbump activates realtls_verifycertificate verification, previously a silent no-op under 0.1.0 -- see description)