Apache APISIX ldap-auth 插件实战指南:基于 LDAP 的 Basic 认证接入与源码级原理
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
导读
ldap-auth是 Apache APISIX 内置的认证类插件,用于将路由或服务接入企业 LDAP 目录服务,让 API 调用方凭借 HTTP Basic 认证(用户名/密码)完成身份校验,并关联到 APISIX 的 Consumer 对象实现后续的鉴权与限流。读完本文,你将掌握ldap-auth的完整属性含义、Consumer 与 Route 的双层配置方法、TLS 加密与证书校验、Secret 托管user_dn的进阶用法,以及认证失败的各类错误返回与排查思路。
插件简介
ldap-auth可以为 Route 或 Service 添加 LDAP 认证能力。它结合 Consumer 对象使用:API 调用方通过 HTTP Basic 认证(Authorization: Basic base64(user:password))携带凭据,插件使用 lua-resty-ldap 与 LDAP 服务器建立连接并完成绑定(bind)认证。
从源码(apisix/plugins/ldap-auth.lua)可以看到该插件的元信息:
local _M = { version = 0.1, priority = 2540, type = 'auth', name = plugin_name, schema = schema, consumer_schema = consumer_schema }type = 'auth':属于认证类插件,在插件执行链中位于鉴权、限流类插件之前;priority = 2540:优先级较高(对比同为认证类的basic-auth为 2520),保证在网关流程早期完成身份识别;- 同时声明了面向 Route/Service 的
schema与面向 Consumer 的consumer_schema两套配置结构。
工作原理
ldap-auth在 OpenResty 的rewrite阶段完成全部认证逻辑(apisix/plugins/ldap-auth.lua),核心流程分三步:
- 解析 Authorization 头:读取请求头
Authorization,用正则Basic\s(.+)提取 Base64 密文并解码,按:拆出用户名与密码; - LDAP 绑定认证:调用
ldap.ldap_authenticate(username, password, ldapconf)向 LDAP 服务器发起绑定请求,凭据无效直接返回 401; - 关联 Consumer:按
uid 属性 + 用户名 + base_dn拼接出完整 DN(如cn=user01,ou=users,dc=example,dc=org),在已配置ldap-auth的 Consumer 中查找匹配项,命中后通过consumer_mod.attach_consumer把 Consumer 挂到请求上下文,供后续插件使用。
客户端请求 ──► rewrite 阶段 ├─ 提取 Authorization 头(缺失 → 401 Missing authorization) ├─ 解析 Basic 凭据(格式错误 → 401 Invalid authorization) ├─ ldap_authenticate 绑定认证(失败 → 401 Invalid user authorization) └─ 按 user_dn 关联 Consumer(未匹配 → 401 Invalid user authorization)其中 LDAP 连接参数在源码中有明确默认值(apisix/plugins/ldap-auth.lua):
local ldapconf = { timeout = 10000, -- 连接超时,单位毫秒 start_tls = false, -- 默认不启用 STARTTLS 升级 ldap_host = ldap_host, ldap_port = ldap_port or 389, -- 未指定端口时默认 389 ldaps = conf.use_tls, -- 是否使用 LDAPS(即 TLS) tls_verify = conf.tls_verify, base_dn = conf.base_dn, attribute = conf.uid, keepalive = 60000, -- 连接池 keepalive 时长,单位毫秒 }ldap_uri由核心工具函数core.utils.parse_addr解析(apisix/core/utils.lua),支持host:port、IPv4、带方括号的 IPv6(如[::1]:1389)等格式;省略端口时自动回退到 389。
属性(Attributes)详解
Consumer 侧配置
| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
| user_dn | string | True | LDAP 客户端的用户 DN,例如cn=user01,ou=users,dc=example,dc=org。该字段支持通过 APISIX Secret 资源保存为密文引用 |
对应的consumer_schema校验要求user_dn必填(apisix/plugins/ldap-auth.lua)。
Route / Service 侧配置
| 名称 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
| base_dn | string | True | — | LDAP 服务器的 Base DN,例如ou=users,dc=example,dc=org |
| ldap_uri | string | True | — | LDAP 服务器地址,例如localhost:1389 |
| use_tls | boolean | False | false | 设为true时使用 TLS(即 LDAPS)连接 |
| tls_verify | boolean | False | false | 在启用use_tls后是否校验服务器证书;设为true时必须在config.yaml中配置ssl_trusted_certificate,并确保ldap_uri的主机名与服务器证书中的主机名一致 |
| uid | string | False | cn | 用于匹配的 uid 属性名,如cn、uid等 |
Route 侧 schema 只强制要求base_dn与ldap_uri两个字段(apisix/plugins/ldap-auth.lua)。
两点值得注意:
- uid 参与 DN 拼接:Consumer 的
user_dn必须与uid=用户名,base_dn拼接结果一致才能命中。例如uid设为cn、用户名为user01、base_dn为ou=users,dc=example,dc=org时,拼接出的 DN 即为cn=user01,ou=users,dc=example,dc=org。若 LDAP 目录使用uid属性作为 RDN,把uid配置为uid即可。 - tls_verify 依赖全局证书配置:启用证书校验时,需要在
config.yaml的deployment.ssl段配置 CA 证书路径(conf/config.yaml.example):
deployment: ssl: # ssl_trusted_certificate: /path/to/ca-cert # 用于校验服务器证书的 PEM 格式 CA 证书启用插件
第一步:创建 Consumer
先通过 Admin API 创建 Consumer 并启用ldap-auth插件。admin_key可从conf/config.yaml中提取并写入环境变量:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')curl http://127.0.0.1:9180/apisix/admin/consumers -H "X-API-KEY: $admin_key" -X PUT -d ' { "username": "foo", "plugins": { "ldap-auth": { "user_dn": "cn=user01,ou=users,dc=example,dc=org" } } }'第二步:在 Route 或 Service 上启用
在目标 Route 上启用插件并配置 LDAP 服务器信息:
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "methods": ["GET"], "uri": "/hello", "plugins": { "ldap-auth": { "base_dn": "ou=users,dc=example,dc=org", "ldap_uri": "localhost:1389", "uid": "cn" } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'同样的配置也可以挂到 Service 上(/apisix/admin/services),让该 Service 下所有 Route 统一继承认证能力。
使用示例
完成上述配置后,客户端携带 Basic 凭据访问即可通过认证:
curl -i -uuser01:password1 http://127.0.0.1:9080/helloHTTP/1.1 200 OK ... hello, world各类认证失败场景
1. 缺少 Authorization 头:
curl -i http://127.0.0.1:9080/helloHTTP/1.1 401 Unauthorized ... {"message":"Missing authorization in request"}此时响应还会附带WWW-Authenticate: Basic realm='.'头,提示客户端应使用 Basic 认证(apisix/plugins/ldap-auth.lua)。
2. 用户名不存在(LDAP 绑定失败):
curl -i -uuser:password1 http://127.0.0.1:9080/helloHTTP/1.1 401 Unauthorized ... {"message":"Invalid user authorization"}3. 密码错误:
curl -i -uuser01:passwordfalse http://127.0.0.1:9080/helloHTTP/1.1 401 Unauthorized ... {"message":"Invalid user authorization"}错误信息速查表
| 场景 | 返回消息 |
|---|---|
| 缺少 Authorization 头 | Missing authorization in request |
| Authorization 头格式非法(非 Basic、Base64 解码失败、无密码) | Invalid authorization in request |
| LDAP 绑定失败 / Consumer 未匹配 | Invalid user authorization |
| 未配置任何 ldap-auth Consumer | Missing related consumer |
源码级认证链路解析
凭据解析
extract_auth_header函数(apisix/plugins/ldap-auth.lua)负责解析 Basic 头:
- 用
Basic\s(.+)提取密文,非 Basic 格式返回Invalid authorization header format; ngx.decode_base64解码失败返回Failed to decode authentication header;- 按
:拆分,拆分结果少于 2 段(如只有用户名没有密码)返回拆分错误; - 用户名与密码两侧的空白字符会被剔除。
认证与 Consumer 关联
绑定成功后才进入 Consumer 关联阶段(apisix/plugins/ldap-auth.lua):
local user_dn = conf.uid .. "=" .. user.username .. "," .. conf.base_dn local consumer_conf = consumer_mod.plugin(plugin_name) if not consumer_conf then return 401, { message = "Missing related consumer" } end local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf, "user_dn") local consumer = consumers[user_dn] if not consumer then return 401, {message = "Invalid user authorization"} end consumer_mod.attach_consumer(ctx, consumer, consumer_conf)这里体现了ldap-auth的一个设计特点:LDAP 只负责校验密码是否有效,具体哪个 DN 属于哪个 Consumer 由 APISIX 侧的 Consumer 配置决定。即使 LDAP 目录中存在某个用户,只要该 DN 没有预先登记为 Consumer,请求依然会被拒绝。这种「LDAP 校验 + 本地 Consumer 授权」的两段式模型,让认证与授权解耦,便于与consumer-restriction、limit-count等插件组合使用。
进阶:TLS 加密连接
当 LDAP 服务器需要加密传输时,在 Route 配置中开启use_tls:
{ "plugins": { "ldap-auth": { "base_dn": "ou=users,dc=example,dc=org", "ldap_uri": "test.com:1636", "uid": "cn", "use_tls": true } } }若还要校验服务器证书,追加tls_verify: true,并在config.yaml中配置 CA 证书路径(见上文)。注意:
ldap_uri中的主机名必须与服务器证书的 CN/SAN 匹配,否则校验证书会失败;use_tls走的是 LDAPS 端口(默认 636 类端口),与start_tls(明文端口上的 STARTTLS 升级)不同,插件源码中start_tls固定为false。
进阶:用 Secret 托管 user_dn
Consumer 的user_dn支持 APISIX Secret 引用,避免在配置中明文暴露 DN 信息。测试用例(t/plugin/ldap-auth.t)展示了完整的 Vault 集成流程:
- 创建 Vault Secret 资源:
curl http://127.0.0.1:9180/apisix/admin/secrets/vault/test1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "uri": "http://127.0.0.1:8200", "prefix": "kv/apisix", "token": "root" }'- 向 Vault 写入密钥:
VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put kv/apisix/user01 user_dn="cn=user01,ou=users,dc=example,dc=org"- Consumer 的
user_dn使用密文引用:
{ "username": "user01", "plugins": { "ldap-auth": { "user_dn": "$secret://vault/test1/user01/user_dn" } } }测试用例还覆盖了 token 走环境变量的方式("token": "$ENV://VAULT_TOKEN"),进一步避免敏感信息落盘(t/plugin/ldap-auth.t)。
删除插件
移除插件只需清空 Route 配置中的plugins字段。APISIX 会热加载新配置,无需重启即可生效:
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "methods": ["GET"], "uri": "/hello", "plugins": {}, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'测试验证
仓库自带的测试套件 t/plugin/ldap-auth.t 共 25 个用例,覆盖了插件的主要行为路径,可作为功能参考与回归验证:
- schema 校验:Consumer 侧缺
user_dn、Route 侧base_dn类型错误(传入数字)均会被拒绝; - 认证失败分支:缺 Authorization 头(401)、格式非法的 Basic 头(401 + 日志
Invalid authorization header format)、Base64 无效(日志Failed to decode authentication header)、只有用户名无密码(日志Split authorization err); - 认证成功分支:合法凭据返回
hello world,日志中出现find consumer user01; - TLS 分支:
use_tls、use_tls + tls_verify两种组合均有用例; - Secret 分支:Vault 密文引用与环境变量 token 两种方式均通过验证。
这些测试同时印证了本文描述的返回消息与日志关键字,排查线上问题时可直接对照。
小结
ldap-auth用一份简洁的配置把 LDAP 目录认证无缝接入 APISIX 网关:Route 侧声明 LDAP 服务器连接参数,Consumer 侧登记用户 DN,客户端只需携带标准 Basic 凭据即可访问受保护 API。结合 TLS 加密、证书校验与 Secret 托管能力,它能够满足企业级目录认证场景对安全性与可维护性的要求;两段式「LDAP 验证 + Consumer 授权」的模型也使其能自然融入 APISIX 的消费者管理生态,与限流、黑白名单等插件协同工作。
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考