客户经常询问,如何使用NGINX Plus和NGINX对请求访问受保护资源或应用的用户进行身份验证,从而保护这些资源或应用。今天,我们发布了此类身份验证系统的参考实现,并将其提供在NGINX, Inc.的GitHub代码仓库中。本文将介绍该实现的工作原理、安装方法,以及如何以此为范例构建自己的身份验证系统。
该解决方案利用NGINX Plus和NGINX中的ngx_http_auth_request_module模块,将身份验证请求转发到外部服务。在参考实现中,该服务是一个名为ldap‑auth的守护进程。它使用Python编写,并与轻量级目录访问协议(LDAP)身份验证服务器通信。默认使用OpenLDAP,但我们也针对Microsoft® Windows® Server Active Directory的默认配置测试了ldap‑auth守护进程(包括2003和2012版本)。
ldap‑auth守护进程可作为您自行开发“连接器”应用的参考模型。您可以使用其他语言编写该应用,也可以将其部署到不同的身份验证系统中,或同时采用这两种方式。NGINX 专业服务团队可协助完成此类适配。
注意事项:
- 该参考实现不适用于生产环境,仅供您自行实现时参考。
- 为便于阅读,本文其余部分均使用NGINX Plus,但该参考实现也适用于NGINX Open Source。必需的http_auth_request模块同时包含在NGINX Plus软件包和预构建的NGINX二进制文件中。
参考实现中的身份验证工作原理
为执行身份验证,http_auth_request 模块会向 ldap‑auth 守护进程发出 HTTP 子请求。该守护进程充当中介,为 LDAP 服务器解析此子请求;它使用 HTTP 与 NGINX Plus 通信,并使用相应的 API 与 LDAP 服务器通信。
我们假设,如果您对该参考实现感兴趣,应该已经有希望通过身份验证来保护的应用或其他资源。不过,为便于测试该参考实现,我们提供了一个同样使用Python编写的示例后端守护进程,用于侦听端口9000。测试期间,它可以代替实际的HTTP应用,提示用户输入凭据,并根据这些凭据创建Cookie。

下面逐步介绍参考实现中的身份验证流程。具体细节取决于nginx-ldap-auth.conf配置文件中的设置;请参阅下文的“配置参考实现”。步骤下方的流程图概述了这一流程。
- 客户端发送HTTP请求,以访问托管在由NGINX Plus充当反向代理的服务器上的受保护资源。
- NGINX Plus(具体而言,是 http_auth_request 模块)将请求转发给 ldap‑auth 守护进程。由于未提供凭据,该守护进程返回 HTTP 状态码
401。 - NGINX Plus将请求转发到http://backend/login,该地址对应后端守护进程。NGINX Plus会将原始请求URI写入所转发请求的
X-Target标头。 - 后端守护进程向客户端发送登录表单(该表单在守护进程的Python代码中定义)。根据
error_page指令的配置,NGINX将登录表单的HTTP状态码设置为200。 - 用户在表单中填写“用户名”和“密码”字段,然后单击“登录”按钮。根据表单中的代码,客户端生成一个HTTP
POST请求并将其发送至/login,NGINX Plus随后将该请求转发给后端守护进程。 - 后端守护进程构造一个采用以下格式的字符串:
username:password,对其进行Base64编码,生成一个名为nginxauth的Cookie,将其值设置为编码后的字符串,然后将该Cookie发送到客户端。后端守护进程还会设置httponly标志,以防止使用JavaScript读取或操纵该Cookie(防范跨站脚本[XSS]漏洞)。 - 客户端重新发送其原始请求(来自步骤 1),此次请求会在HTTP标头的
Cookie字段中包含该Cookie。NGINX Plus将请求转发到ldap-auth守护进程(与步骤 2 相同)。 - ldap‑auth守护进程对Cookie进行解码,并在身份验证请求中将用户名和密码发送到LDAP服务器。
- 后续操作取决于LDAP服务器是否成功对用户进行身份验证:
- 如果身份验证成功,ldap‑auth守护进程会发送HTTP状态码
200给NGINX Plus。NGINX Plus向后端守护进程请求资源。在参考实现中,后端守护进程返回以下文本:
- 如果身份验证成功,ldap‑auth守护进程会发送HTTP状态码
Hello, world! Requested URL: URL- The nginx-ldap-auth.conf文件包含用于缓存身份验证尝试结果的指令;要禁用缓存,请参阅下文的“缓存”。
- 如果身份验证失败,ldap‑auth守护进程会将HTTP代码
401发送到NGINX Plus。NGINX Plus会再次将请求转发到后端守护进程(与步骤3相同),并重复此过程。

安装组件
参考实现随附的NGINX Plus配置文件nginx-ldap-auth.conf将LDAP服务器以外的所有组件(即NGINX Plus、客户端、ldap‑auth守护进程和后端守护进程)配置为在同一主机上运行,足以满足测试需求。测试期间,LDAP服务器也可以在该主机上运行。
在实际部署中,后端应用和身份验证服务器通常分别在不同的主机上运行,NGINX Plus则在第三台主机上运行。大多数情况下,ldap-auth守护进程占用的资源不多,因此可以在NGINX Plus主机或您选择的其他主机上运行。
- 克隆GitHub存储库。
- 如果NGINX Plus尚未运行,请按照适用于所用操作系统的说明进行安装。
- 如果LDAP服务器尚未运行,请安装并配置一台。默认情况下,ldap‑auth守护进程与OpenLDAP通信,但也支持Microsoft Windows Active Directory 2003和2012。如果仅使用LDAP服务器测试参考实现,可以使用GitHub上提供的OpenLDAP服务器Docker镜像,也可以按照相关说明设置服务器,例如如何在Ubuntu 16.04上安装和配置OpenLDAP及phpLDAPadmin。请记下为Base DN、Bind DN和Bind密码设置的值。您需要按照配置参考实现中的说明,将这些值添加到NGINX配置文件中。
- 在将要运行 ldap‑auth 守护进程的主机上,安装以下附加软件。建议使用操作系统随附的版本,而不要从开源代码仓库下载这些软件。
- Python 2 版本。不支持版本 3。
- Python LDAP模块python‑ldap(由python-ldap.org开源项目创建)。
- 将以下文件从克隆的存储库复制到指定主机:
- nginx-ldap-auth.conf:NGINX Plus配置文件,其中包含测试参考实现所需的最少指令集。请将其安装在NGINX Plus主机上(如果使用常规配置方案,则安装在/etc/nginx/conf.d目录中)。为避免配置冲突,请务必移动或重命名随NGINX Plus安装的所有默认配置文件。
- nginx-ldap-auth-daemon.py:ldap‑auth守护进程的Python代码。请将其安装在所选主机上。
- nginx-ldap-auth-daemon-ctl.sh:用于启动和停止守护进程的示例Shell脚本。请将其安装在运行ldap‑auth守护进程的同一主机上。
- backend-sample-app.py:守护进程的Python代码,在测试期间充当后端应用服务器。可安装在任意主机上。
- 按照下文“配置参考实现”中的说明修改NGINX Plus配置文件。完成更改后,运行
nginx-t命令,验证文件语法是否有效。
root# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful- 启动NGINX Plus。如果NGINX Plus已在运行,请运行以下命令以重新加载配置文件:
root# nginx -s reload- 在相应的主机上运行以下命令,以启动ldap‑auth守护进程和后端守护进程。
root# nginx-ldap-auth-daemon-ctl.sh start
root# python backend-sample-app.py- 使用Web浏览器访问http://nginx-server-address:8081。确认浏览器显示身份验证表单。填写并提交表单后,确认服务器针对有效凭据返回预期响应。如上所述,后端守护进程会返回以下文本:
Hello, world! Requested URL: URL配置参考实现
在nginx-ldap-auth.conf文件中进行以下更改。如标注所示,部分更改为必需,部分为可选。
LDAP服务器设置
根据nginx-ldap-auth-daemon.py中的实现,ldap‑auth守护进程与OpenLDAP服务器通信,并通过传入参数指定要进行身份验证的用户账户。为避免修改Python代码,nginx-ldap-auth.conf文件包含proxy_set_header指令,用于在HTTP标头中设置值,随后使用这些值设置参数。下表列出了参数与标头的对应关系。
LDAP参数 HTTP标头basedn X-Ldap-BaseDNbinddn X-Ldap-BindDNbindpasswd X-Ldap-BindPasscookiename X-CookieNamerealm X-Ldap-Realmtemplate X-Ldap-Templateurl X-Ldap-URL
- (必需)在以下指令中,将粗体值替换为适用于LDAP服务器部署的正确值。请特别注意,nginx-ldap-auth.conf文件使用LDAPS的知名端口636。如果将端口更改为389(LDAP的知名端口)或其他LDAP端口,还需将协议名称从
ldaps更改为ldap。
# URL and port for connecting to the LDAP server
proxy_set_header X-Ldap-URL "ldaps://example.com:636";
# Base DN
proxy_set_header X-Ldap-BaseDN "cn=Users,dc=test,dc=local";
# Bind DN
proxy_set_header X-Ldap-BindDN "cn=root,dc=test,dc=local";
# Bind password
proxy_set_header X-Ldap-BindPass "secret";- (如果使用Active Directory而非OpenLDAP,则必须执行此操作)按如下所示取消注释以下指令:
proxy_set_header X-Ldap-Template "(SAMAccountName=%(username)s)";- (可选)参考实现使用基于Cookie的身份验证。如果改用HTTP基本身份验证,请按如下所示注释掉以下指令:
#proxy_set_header X-CookieName "nginxauth";
#proxy_set_header Cookie nginxauth=$cookie_nginxauth;- (可选)如果要更改ldap‑auth守护进程默认传递给OpenLDAP服务器的
template参数值,请按如下所示取消注释以下指令,然后更改该值:
proxy_set_header X-Ldap-Template "(cn=%(username)s)";- (可选)如果要将领域名称从默认值(Restricted)更改为其他值,请取消注释并修改以下指令:
proxy_set_header X-Ldap-Realm "Restricted";后端守护进程的IP地址
如果后端守护进程与NGINX Plus不在同一主机上运行,请在upstream配置块中更改其IP地址:
upstream backend { server 127.0.0.1:9000;
}ldap‑auth 守护进程的 IP 地址
如果ldap‑auth守护进程与NGINX Plus不在同一主机上,请更改此proxy_pass指令中的IP地址:
location = /auth-proxy { proxy_pass http://127.0.0.1:8888;
# ...
}NGINX 侦听的 IP 地址和端口
如果客户端与NGINX Plus不在同一主机上运行,请更改此listen指令中的IP地址(也可以完全删除该地址,以接受来自任何客户端的流量)。还可以根据需要将NGINX的监听端口从8081更改为其他端口:
server { listen 127.0.0.1:8081;
# ...
}缓存
通过 nginx-ldap-auth.conf 文件可缓存数据和凭据。您可以根据需要更改以下设置:
- The
proxy_cache_path指令在http配置块中创建名为cache的本地磁盘目录,并在共享内存中为名为auth_cache的区域分配10 MB空间,用于存储元数据。
proxy_cache_path cache/ keys_zone=auth_cache:10m;- 如果更改共享内存区域的名称,还必须在将流量定向到ldap‑auth守护进程的
proxy_cache指令(位于location块中)内同步更改该名称。
location = /auth-proxy {
proxy_cache auth_cache;
# ...
}- The
proxy_cache_valid指令(与location位于同一代码块中的proxy_cache)指定,标记为HTTP状态码200或403的缓存响应有效期为10分钟。
location = /auth-proxy { proxy_cache_valid 200 403 10m;
# ...
}要禁用缓存,请注释掉这3条指令以及“proxy_cache_key”指令。
自定义身份验证系统
如上所述,可以将ldap‑auth守护进程作为模型,开发自己的应用程序来接收http_auth_request模块发出的请求。如果使用Python编写应用程序,以便与其他类型(非LDAP)的身份验证服务器通信,请编写新的身份验证处理程序类,以替换LDAPAuthHandler,该类位于脚本nginx-ldap-auth-daemon.py中。
安全注意事项
后端守护进程使用Base64对Cookie中的用户名和密码进行编码。Base64只是一种非常弱的混淆方式,凭据很容易被提取和滥用。要让身份验证真正发挥作用,需要在后端应用中采用更复杂的加密方式。
关于作者

相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
