ldap-auth-advanced
ldap-auth-advanced 插件对接 OpenLDAP、Active Directory 等 LDAP 目录完成客户端认证,并可把认证到的目录用户映射为消费者(Consumer)。
插件采用「先检索、后绑定」的方式解析用户:先在 base_dn 下检索 attribute 与客户端提供的用户名匹配的条目,再用客户端提供的密码以该条目执行绑定。由于用户的可分辨名称(DN)是检索出来的而非拼接出来的,目录结构无需写进网关配置,用户也可以位于目录树的不同层级。
当 consumer_required 启用时(默认启用),插件会继续查找凭据中记录了该 DN 的消费者。匹配成功后,网关会在把请求转发到上游之前加上 X-Consumer-Username、X-Credential-Identifier 等请求头,因此消费者级插件、限流和用量统计对 LDAP 认证的流量与其它身份一视同仁。把 consumer_required 设为 false 则只对接目录完成认证,不要求存在对应的消费者。
自 API7 企业版 3.10.5 起可用。
插件从 Authorization 请求头读取凭据;当 Proxy-Authorization 携带可用凭据时优先使用后者。方案名由 header_type 决定:默认值 ldap 对应 Authorization: ldap <base64(username:password)>;取值 basic 时使用标准的 Authorization: Basic <base64(username:password)>,因此已有的 HTTP Basic 客户端无需改动即可认证。
示例
以下示例演示了如何在不同场景下使用 ldap-auth-advanced 插件。
前置条件
以下示例假设有一个可通过 192.168.1.10:389 访问的 LDAP 目录,用户条目位于 ou=users,dc=example,dc=org 下,其中一个用户的 uid 为 johndoe、密码为 john-secret,检索时以 cn=admin,dc=example,dc=org 身份绑定。请根据你自己的目录调整这些取值。
对接 LDAP 目录完成认证
以下示例演示了如何把 consumer_required 设为 false,只对接 LDAP 目录完成客户端认证,而不映射到消费者。
- Admin API
- ADC
- Ingress Controller
创建一个配置了 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
}
}
}'
创建一个配置了 ldap-auth-advanced 插件的路由:
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
把配置同步到网关:
adc sync -f adc.yaml
创建一个配置了 ldap-auth-advanced 插件的路由:
- 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
把配置应用到集群:
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
把配置应用到集群:
kubectl apply -f ldap-auth-ic.yaml
使用正确的凭据验证
用目录用户的凭据向该路由发送请求,凭据经 base64 编码后以 ldap 方案携带:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:john-secret' | base64)"
应当收到 HTTP/1.1 200 OK 响应。
使用错误的凭据验证
用错误的密码向该路由发送请求:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:wrong-password' | base64)"
应当收到 HTTP/1.1 401 Unauthorized 响应:
WWW-Authenticate: ldap realm="ldap"
{"message":"Authorization required"}
不携带凭据验证
不携带任何凭据向该路由发送请求:
curl -i "http://127.0.0.1:9080/anything"
应当收到 HTTP/1.1 401 Unauthorized 响应。
把 LDAP 用户映射为消费者
以下示例演示了如何把目录用户映射为消费者,从而让消费者级的配置对其流量生效。消费者的凭据中记录用户的完整 DN,也就是插件通过目录检索解析出来的那个值。
ldap-auth-advanced 类型的消费者凭据通过 Admin API 或 Dashboard 创建。ADC 与 Ingress Controller 目前只支持 key-auth、basic-auth、jwt-auth 和 hmac-auth 类型的凭据。
创建消费者 johndoe:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "johndoe"
}'
为该消费者创建 ldap-auth-advanced 凭据,记录目录条目的 DN:
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"
}
}
}'
创建一个配置了 ldap-auth-advanced 的路由,consumer_required 保持默认值 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
}
}
}'
用目录用户的凭据向该路由发送请求:
curl -i "http://127.0.0.1:9080/anything" \
-H "Authorization: ldap $(printf '%s' 'johndoe:john-secret' | base64)"
应当收到 HTTP/1.1 200 OK 响应,上游会看到消费者相关的请求头:
{
"headers": {
"X-Consumer-Username": "johndoe",
"X-Credential-Identifier": "cred-john-ldap-auth",
...
},
...
}
如果目录用户认证成功,但其 DN 没有记录在任何消费者凭据中,则会收到 HTTP/1.1 401 Unauthorized 响应。
接受 HTTP Basic 认证方案
以下示例演示了如何接受标准 HTTP Basic 方案而非 ldap 方案的凭据,从而让已有的 Basic 客户端无需改动即可使用。
在路由上把 header_type 设为 basic:
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
}
}
}'
用普通的 Basic 凭据发送请求:
curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-secret
应当收到 HTTP/1.1 200 OK 响应。此时未认证请求收到的挑战头也变为 Basic 方案:
WWW-Authenticate: Basic realm="ldap"
通过 TLS 连接目录
以下示例演示了如何通过 LDAPS 访问目录。设置 use_ldaps 并把 ldap_uri 指向 LDAPS 端口;省略端口时,启用 LDAPS 使用 636,否则使用 389。若要在 389 端口上把明文连接升级为 TLS,改用 use_starttls。这两个选项互斥。
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
}
}
}'
证书校验由 ssl_verify 控制,默认开启。建议保持开启,并确保网关信任签发目录证书的 CA。