使用NGINX Plus和NGINX通过LDAP对应用用户进行身份验证

NGINX | 2015年6月22日

客户经常询问,如何使用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。

用于LDAP身份验证的NGINX Plus参考实现包括ldap-auth守护进程和一个示例后端守护进程

下面逐步介绍参考实现中的身份验证流程。具体细节取决于nginx-ldap-auth.conf配置文件中的设置;请参阅下文的“配置参考实现”。步骤下方的流程图概述了这一流程。

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

在用于LDAP身份验证的NGINX Plus参考实现中,ldap-auth守护进程充当NGINX Plus与LDAP服务器之间的中介

安装组件

参考实现随附的NGINX Plus配置文件nginx-ldap-auth.conf将LDAP服务器以外的所有组件(即NGINX Plus、客户端、ldap‑auth守护进程和后端守护进程)配置为在同一主机上运行,足以满足测试需求。测试期间,LDAP服务器也可以在该主机上运行。

在实际部署中,后端应用和身份验证服务器通常分别在不同的主机上运行,NGINX Plus则在第三台主机上运行。大多数情况下,ldap-auth守护进程占用的资源不多,因此可以在NGINX Plus主机或您选择的其他主机上运行。

  1. 克隆GitHub存储库。
  2. 如果NGINX Plus尚未运行,请按照适用于所用操作系统的说明进行安装。
  3. 如果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配置文件中。
  4. 在将要运行 ldap‑auth 守护进程的主机上,安装以下附加软件。建议使用操作系统随附的版本,而不要从开源代码仓库下载这些软件。
    • Python 2 版本。不支持版本 3。
    • Python LDAP模块python‑ldap(由python-ldap.org开源项目创建)。
  5. 将以下文件从克隆的存储库复制到指定主机:
    • 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代码,在测试期间充当后端应用服务器。可安装在任意主机上。
  6. 按照下文“配置参考实现”中的说明修改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
  1. 启动NGINX Plus。如果NGINX Plus已在运行,请运行以下命令以重新加载配置文件:
root# nginx -s reload
  1. 在相应的主机上运行以下命令,以启动ldap‑auth守护进程和后端守护进程。
root# nginx-ldap-auth-daemon-ctl.sh start
root# python backend-sample-app.py
  1. 使用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 | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。