ldap-auth-advanced
The ldap-auth-advanced plugin authenticates clients against an LDAP directory, such as OpenLDAP or Active Directory, and optionally maps the authenticated directory user onto a consumer.
The plugin resolves users with a search-then-bind flow. It first searches base_dn for an entry whose attribute matches the user name supplied by the client, and then binds as the entry it found, using the password supplied by the client. Because the user's distinguished name (DN) is discovered rather than constructed, the directory layout does not have to be encoded in the gateway configuration, and users can live at different depths of the tree.
When consumer_required is enabled, which is the default, the plugin then looks for a consumer whose credential records the DN that was resolved. On a match, the gateway adds headers such as X-Consumer-Username and X-Credential-Identifier to the request before proxying it upstream, so per-consumer plugins, rate limits, and analytics apply to LDAP-authenticated traffic like any other identity. Setting consumer_required to false authenticates against the directory without requiring a consumer.
Available in API7 Enterprise from version 3.10.5.
The plugin reads credentials from the Authorization header, or from Proxy-Authorization when that header carries usable credentials. The scheme word is determined by header_type: with the default ldap, clients send Authorization: ldap <base64(username:password)>; with basic, they send an ordinary Authorization: Basic <base64(username:password)> header, which lets existing HTTP Basic clients authenticate unchanged.
Examples
The examples below demonstrate how you can work with the ldap-auth-advanced plugin for different scenarios.
Prerequisites
The examples assume an LDAP directory reachable at 192.168.1.10:389 that holds user entries under ou=users,dc=example,dc=org, with a user whose uid is johndoe and whose password is john-secret. Searches are performed as cn=admin,dc=example,dc=org. Adjust these values for your own directory.
Authenticate Against an LDAP Directory
The following example demonstrates how to authenticate clients against an LDAP directory without mapping them onto consumers, by setting consumer_required to false.
- Admin API
- ADC
- Ingress Controller
Create a route with ldap-auth-advanced:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "ldap-auth-route",
"uri": "/anything",
"plugins": {
"ldap-auth-advanced": {
"ldap_uri": "192.168.1.10:389",
"base_dn": "ou=users,dc=example,dc=org",
"attribute": "uid",
"bind_dn": "cn=admin,dc=example,dc=org",
"ldap_password": "admin-secret",
"consumer_required": false
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Create a route with the ldap-auth-advanced plugin configured:
services:
- name: ldap-auth-service
routes:
- name: ldap-auth-route
uris:
- /anything
plugins:
ldap-auth-advanced:
ldap_uri: 192.168.1.10:389
base_dn: ou=users,dc=example,dc=org
attribute: uid
bind_dn: cn=admin,dc=example,dc=org
ldap_password: admin-secret
consumer_required: false
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
Create a route with the ldap-auth-advanced plugin configured:
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: ldap-auth-plugin-config
spec:
plugins:
- name: ldap-auth-advanced
config:
ldap_uri: 192.168.1.10:389
base_dn: ou=users,dc=example,dc=org
attribute: uid
bind_dn: cn=admin,dc=example,dc=org
ldap_password: admin-secret
consumer_required: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: ldap-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: ldap-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
Apply the configuration to your cluster:
kubectl apply -f ldap-auth-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: ldap-auth-route
spec:
ingressClassName: apisix
http:
- name: ldap-auth-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: ldap-auth-advanced
enable: true
config:
ldap_uri: 192.168.1.10:389
base_dn: ou=users,dc=example,dc=org
attribute: uid
bind_dn: cn=admin,dc=example,dc=org
ldap_password: admin-secret
consumer_required: false
Apply the configuration to your cluster:
kubectl apply -f ldap-auth-ic.yaml
Verify with Valid Credentials
Send a request to the route with the directory user's credentials, base64 encoded and presented in the ldap scheme:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:john-secret' | base64)"
You should receive an HTTP/1.1 200 OK response.
Verify with Invalid Credentials
Send a request to the route with an incorrect password:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:wrong-password' | base64)"
You should receive an HTTP/1.1 401 Unauthorized response:
WWW-Authenticate: ldap realm="ldap"
{"message":"Authorization required"}
Verify without Credentials
Send a request to the route without any credentials:
curl -i "http://127.0.0.1:9080/anything"
You should receive an HTTP/1.1 401 Unauthorized response.
Map LDAP Users to Consumers
The following example demonstrates how to map a directory user onto a consumer, so that consumer-scoped configurations apply to their traffic. The consumer's credential records the user's full DN, which is the value the plugin resolves through its directory search.
Consumer credentials of type ldap-auth-advanced are created through the Admin API or the Dashboard. ADC and the Ingress Controller currently support only key-auth, basic-auth, jwt-auth, and hmac-auth credentials.
Create a consumer johndoe:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "johndoe"
}'
Create an ldap-auth-advanced credential for the consumer, recording the DN of the directory entry:
curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-john-ldap-auth",
"plugins": {
"ldap-auth-advanced": {
"user_dn": "uid=johndoe,ou=users,dc=example,dc=org"
}
}
}'
Create a route with ldap-auth-advanced, leaving consumer_required at its default of true:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "ldap-auth-consumer-route",
"uri": "/anything",
"plugins": {
"ldap-auth-advanced": {
"ldap_uri": "192.168.1.10:389",
"base_dn": "ou=users,dc=example,dc=org",
"attribute": "uid",
"bind_dn": "cn=admin,dc=example,dc=org",
"ldap_password": "admin-secret"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request to the route with the directory user's credentials:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:john-secret' | base64)"
You should receive an HTTP/1.1 200 OK response, and the upstream should see the consumer headers:
{
"headers": {
"X-Consumer-Username": "johndoe",
"X-Credential-Identifier": "cred-john-ldap-auth",
...
},
...
}
A directory user who authenticates successfully but whose DN is not recorded on any consumer credential receives HTTP/1.1 401 Unauthorized.
Accept the HTTP Basic Authentication Scheme
The following example demonstrates how to accept credentials in the standard HTTP Basic scheme instead of the ldap scheme, so that existing Basic clients work unchanged.
Set header_type to basic on the route:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "ldap-auth-basic-route",
"uri": "/anything",
"plugins": {
"ldap-auth-advanced": {
"ldap_uri": "192.168.1.10:389",
"base_dn": "ou=users,dc=example,dc=org",
"attribute": "uid",
"bind_dn": "cn=admin,dc=example,dc=org",
"ldap_password": "admin-secret",
"header_type": "basic",
"consumer_required": false
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Send a request using an ordinary Basic credential:
curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-secret
You should receive an HTTP/1.1 200 OK response. An unauthenticated request is now challenged with the Basic scheme:
WWW-Authenticate: Basic realm="ldap"
Connect to the Directory Over TLS
The following example demonstrates how to reach the directory over LDAPS. Set use_ldaps and point ldap_uri at the LDAPS port; when the port is omitted, 636 is used under LDAPS and 389 otherwise. Use use_starttls instead to upgrade a plaintext connection on port 389. The two options are mutually exclusive.
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "ldaps-auth-route",
"uri": "/anything",
"plugins": {
"ldap-auth-advanced": {
"ldap_uri": "192.168.1.10:636",
"use_ldaps": true,
"base_dn": "ou=users,dc=example,dc=org",
"attribute": "uid",
"bind_dn": "cn=admin,dc=example,dc=org",
"ldap_password": "admin-secret",
"consumer_required": false
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Certificate verification is controlled by ssl_verify, which is enabled by default. Leave it enabled and make sure the directory's issuing CA is trusted by the gateway.