为了增强安全防护和提升用户体验,F5 NGINX Plus (R29+) 现在支持安全断言标记语言 (SAML)。SAML 是一种成熟的协议,可为 Web 应用提供单点登录 (SSO),支持身份验证供应商 (IdP) 对访问资源的用户进行身份验证,然后将这些信息传递给服务提供商 (SP) 进行授权。
本文将使用原生不支持 SAML 的 Web 应用,分步说明如何集成 NGINX 与 Microsoft Entra ID(原名为 Azure Active Directory (Azure AD)),并介绍如何实现面向应用的 SSO 并将其与 Microsoft Entra ID 生态系统相集成。通过学习本教程,您还将了解 NGINX 如何从 SAML 断言中提取声明(包括 UPN、名字、姓氏和组成员资格),然后通过 HTTP 请求头将其传递给应用。
本教程分为三步:
若要学习本教程,您需要:
注:本教程不适用于 NGINX 开源版部署,因为键值存储是 NGINX Plus 的独有功能。
在此设置中,NGINX Plus 可充当 SAML SP,并与 SAML IdP(通过用户代理与 NGINX Plus 间接通信)一同实现 SSO。
下图显示了 SSO 流程,包括 SP 发起及用于请求和响应的 POST 绑定。同样需要注意的是,这种通信信道并非直接信道,而是通过用户代理进行管理。
图 1:SAML SP 发起的 SSO,通过 POST binding(绑定)发送 AuthnRequest 和 Response
要访问 Microsoft Entra ID 管理门户,请登录并导航到左侧面板。选择 Microsoft Entra ID,然后点击需要 SSO 配置的目录标题。完成后,选择 Enterprise applications(企业应用)。
图 2:在管理门户中选择企业应用
要创建应用,点击门户页面顶部的 New application(新建应用)按钮。在本例中,我们创建了一个名为“demonginx”的应用。
图 3:在 Microsoft Entra ID 中新建一个应用
在重定向到新建应用 Overview(概览)后,通过左侧菜单转到 Getting Started(开始),并点击 Manage(管理)下的 Single sign-on(单点登录)。然后,选择 SAML 作为单点登录方法。
图 4:从 SSO 设置开始 SAML 配置
如需在企业应用中设置 SSO,需要在 Microsoft Entra ID 中将 NGINX Plus 注册为 SP。如图 5 所示,点击 Basic SAML Configuration(基本 SAML 配置)中 Edit(编辑)旁边的铅笔图标。
填写以下值,然后点击 Save(保存):
可选择是否使用证书验证。启用此设置时,必须配置 NGINX 中的两个配置选项:
注:在此演示中,修改了属性和声明,并添加了新的 SAML 属性。这些 SAML 属性由 IdP 发送。确保已将 NGINX 配置设置为正常接收和处理这些属性。您可以在 NGINX GitHub 仓库中查看并调整相关设置。
从 Microsoft Entra ID 下载 IdP Certificate (Raw)(证书(原始格式)),并将其保存到 NGINX Plus 实例中。
图 5:从 Microsoft Entra ID 下载 IdP 证书(原始格式)
图 6:添加新用户或组
在 Microsoft Entra ID 中,您可通过添加或分配用户和组来授予对支持 SSO 的公司应用的访问权限。
在左侧菜单中,点击 User and groups(用户和组),然后点击顶部按钮 Add user/group(添加用户/组)。
在 NGINX Plus SP 中配置文件之前,请确保您已获得必要的证书:
注:证书必须是 SPKI 格式。
首先,请从 Microsoft Entra ID 下载 IdP 证书进行签名验证。然后,将 PEM 转换为 DER 格式:
openssl x509 -in demonginx.cer -outform DER -out demonginx.der
如果您想验证 SAML 服务提供商的断言,建议使用与用于 TLS 终止的公钥/私钥不同的公钥/私钥。
取 SPKI 格式的公钥证书:
openssl x509 -inform DER -in demonginx.der -pubkey -noout > demonginx.spki
编辑 frontend.conf 文件,更新以下项目:
在生产部署中,您可以根据业务要求使用不同的后端目的地。在本例中,后端提供了一个定制响应:
“Welcome to Application page\n My objectid is $http_objectid\n My email is $http_mail\n”;
我们修改了 Microsoft Entra ID 中的属性和声明,为用户的 mail 和 objectid 添加了新的声明。通过这些更新,您能够为应用提供更加个性化的定制响应,从而提升用户体验。
图 7:修改了 Microsoft Entra ID 中的属性和声明
下一步是配置 NGINX,它会将流量代理到后端应用。在此演示中,后端 SAML 应用可从 https://dev.sports.com 公开访问。
编辑您的 frontend.conf 文件:
# 这是 frontend.conf 文件
# 这是我们使用 SAML SSO 保护的后端应用
upstream my_backend {
zone my_backend 64k;
server dev.sports.com;
}
# 自定义日志格式,将‘NameID’主题添加到 REMOTE_USER 字段中
log_format saml_sso '$remote_addr - $saml_name_id [$time_local] "$request" "$host" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 前端服务器 — 支持 SAML SSO 身份验证的反向代理
#
server {
# 实现 SAML SSO 支持的功能位置
include conf.d/saml_sp.server_conf;
# 按需降低严重性级别
error_log /var/log/nginx/error.log debug;
listen 443 ssl;
ssl_certificate /home/ubuntu/dev.sports.com.crt;
ssl_certificate_key /home/ubuntu/dev.sports.com.key;
ssl_session_cache shared:SSL:5m;
location / {
# 如果用户未通过身份验证(即 "saml_access_granted."
# 变量未设置为“1”),则返回 HTTP 401 Unauthorized 错误,
# 该错误由 @do_samlsp_flow 命名的 location 进行处理。
error_page 401 = @do_samlsp_flow;
if ($saml_access_granted != "1") {
return 401;
}
# 成功通过身份验证的用户会被代理到后端,
# 并将 NameID 属性作为 HTTP 请求头进行传递
proxy_set_header mail $saml_attrib_mail; # Microsoft Entra ID's user.mail
proxy_set_header objectid $saml_attrib_objectid; # Microsoft Entra ID's objectid
access_log /var/log/nginx/access.log saml_sso;
proxy_pass http://my_backend;
proxy_set_header Host dev.sports.com;
return 200 "Welcome to Application page\n My objectid is $http_objectid\n My email is $http_mail\n";
default_type text/plain;
}
}
# vim: syntax=nginx
要在 NGINX 配置中反映 saml_attrib_mail 和 saml_attrib_ objectid 属性,更新 saml_sp_configuration.conf 的键值存储部分,如下所示:
keyval_zone zone=saml_attrib_mail:1M state=/var/lib/nginx/state/saml_attrib_email.json timeout=1h;
keyval $cookie_auth_token $saml_attrib_mail zone=saml_attrib_mail;
keyval_zone zone=saml_attrib_objectid:1M state=/var/lib/nginx/state/saml_attrib_objectid.json timeout=1h;
keyval $cookie_auth_token $saml_attrib_objectid zone=saml_attrib_objectid;
接下来,配置 SAML SSO 配置文件。该文件包含 SP 和 IdP 的主要配置。若要根据特定 SP 和 IdP 设置对其进行自定义,需要调整文件中所含的多个 map{} 代码块。
下表描述了 saml_sp_configuration.conf 中的变量:
变量 | 描述 |
---|---|
saml_sp_entity_id | 用户访问应用时使用的 URL。 |
saml_sp_acs_url | 服务提供商用于接收和处理 SAML 响应、提取用户身份信息,然后根据所提供的信息允许或拒绝访问请求资源的 URL。 |
saml_sp_sign_authn | 指定是否对从 SP 发送到 IdP 的 SAML 请求进行签名。签名使用 SP 签名密钥完成,并需要将关联的证书上传到 IdP 以验证签名。 |
saml_sp_signing_key | 用于对从 SP 发送到 IdP 的 SAML 请求进行签名的签名密钥。确保将关联的证书上传到 IdP 以验证签名。 |
saml_idp_entity_id | 用于定义 IdP 的身份。 |
saml_idp_sso_url | SP 向其发送 SAML 断言请求以发起身份验证请求的 IdP 端点。 |
saml_idp_verification_certificate | 用于验证从 IdP 收到的已签名 SAML 断言的证书。证书由 IdP 提供,必须采用 SPKI 格式。 |
saml_sp_slo_url | IdP 发送 SAML LogoutRequest(启动注销流程时)或 LogoutResponse(确认注销时)的 SP 目标端点。 |
saml_sp_sign_slo | 指定注销 SAML 是否由 SP 签名。 |
saml_idp_slo_url | SP 发送 LogoutRequest(启动注销流程时)或 LogoutResponse(确认注销时)的 IdP 目标端点。 |
saml_sp_want_signed_slo | 指定 SAML SP 是否需要对来自 IdP 的 SAML 注销响应或请求进行签名。 |
下面的代码显示了 saml_sp_configuration.conf. 中仅针对此用例编辑的值。
注:确保配置文件的其余部分(如键值存储)仍显示在文件中,以及根据部署情况适当调整 saml_sp_configuration.conf 文件中的变量。
# SAML SSO 配置
map $host $saml_sp_entity_id {
# 向 IdP 标识 SP 的唯一标识符。
# 必须是 URL 或 URN。
default "https://dev.sports.com";
}
map $host $saml_sp_acs_url {
# ACS URL 是 SP 上的一个端点,
# IdP 将通过其身份验证响应重定向到该端点。
# 必须与“saml_sp.serer_conf”文件中定义的 ACS 位置相匹配。
default "https://dev.sports.com/saml/acs";
}
map $host $saml_sp_request_binding {
# 在单点登录 (SSO) 过程中
# SP 向 IdP 发送身份验证请求所用的方法。
# 只允许使用 HTTP-POST 或 HTTP-Redirect 方法。
default 'HTTP-POST';
}
map $host $saml_sp_sign_authn {
# SP 是否对发送给 IdP 的 AuthnRequest 进行签名。
default "false";
}
map $host $saml_sp_decryption_key {
# 指定 SP 用于对来自 IdP 的加密断言
# 或 NameID 进行解密的私钥。
default "";
}
map $host $saml_sp_force_authn {
# SP 是否通过 IdP 对用户重新进行身份验证。
default "false";
}
map $host $saml_sp_nameid_format {
# 表示 IdP 生成的 SAML 断言中
# 名称标识符的所需格式。查看 SAML 2.0 核心规范第 8.3 节,
# (http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf)
# 获取允许的 NameID 格式列表。
default "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified";
}
map $host $saml_sp_relay_state {
# 成功登录后,SP 应重定向到的
# 相对或绝对 URL。
default "";
}
map $host $saml_sp_want_signed_response {
# SP 是否需要对来自 IdP 的 SAML Response 进行
# 数字签名。
default "false";
}
map $host $saml_sp_want_signed_assertion {
# SP 是否需要对来自 IdP 的 SAML 断言
# 进行数字签名。
default "true";
}
map $host $saml_sp_want_encrypted_assertion {
# SP 是否需要对来自 IdP 的 SAML 断言
# 进行加密。
default "false";
}
map $host $saml_idp_entity_id {
# 向 SP 标识 IdP 的唯一标识符。
# 必须是 URL 或 URN。
default "https://sts.windows.net/8807dced-9637-4205-a520-423077750c60/";
}
map $host $saml_idp_sso_url {
# SP 将发送 SAML AuthnRequest 以启动
# 身份验证流程的 IdP 端点。
default "https://login.microsoftonline.com/8807dced-9637-4205-a520-423077750c60/saml2";
}
map $host $saml_idp_verification_certificate {
# 证书文件,
# 用于验证从 IdP 收到的 SAML Response、LogoutRequest 或 LogoutResponse 上的数字签名。
# 必须是 PKCS#1 格式的公钥。请参阅文档,了解如何
# 将 X.509 PEM 转换为 DER 格式。
default "/etc/nginx/conf.d/demonginx.spki";
}
######### 单点注销 (SLO) #########
map $host $saml_sp_slo_url {
# IdP 将发送 SAML LogoutRequest 以启动注销流程
# 或发送 LogoutResponse 以确认注销的 SP 端点。
default "https://dev.sports.com/saml/sls";
}
map $host $saml_sp_slo_binding {
# 在单点注销 (SLO) 过程中
# SP 向 IdP 发送 LogoutRequest 或 LogoutResponse 所用的方法。
# 只允许使用 HTTP-POST 或 HTTP-Redirect 方法。
default 'HTTP-POST';
}
map $host $saml_sp_sign_slo {
# SP 是否必须
# 对发送给 IdP 的 LogoutRequest 或 LogoutResponse 进行签名。
default "false";
}
map $host $saml_idp_slo_url {
# SP 将发送 LogoutRequest 以启动注销流程
# 或发送 LogoutResponse 以确认注销的 IdP 端点。
# 如果未设置,将禁用 SAML 单点注销 (SLO) 功能,
# 而且对“logout”location 的请求将导致
# 用户会话卸载并重定向到注销登陆页面。
default "https://login.microsoftonline.com/8807dced-9637-4205-a520-423077750c60/saml2";
}
map $host $saml_sp_want_signed_slo {
# SP 是否需要对来自 IdP 的 SAML LogoutRequest 或 LogoutResponse 进行
# 数字签名。
default "true";
}
map $host $saml_logout_landing_page {
# 请求 /logout location 后,将用户重定向到的目标位置。这可以
# 替换为自定义注销页面或完整 URL。
default "/_logout"; # Built-in, simple logout page
}
map $proto $saml_cookie_flags {
http "Path=/; SameSite=lax;"; # For HTTP/plaintext testing
https "Path=/; SameSite=lax; HttpOnly; Secure;"; # Production recommendation
}
map $http_x_forwarded_port $redirect_base {
"" $proto://$host:$server_port;
default $proto://$host:$http_x_forwarded_port;
}
map $http_x_forwarded_proto $proto {
"" $scheme;
default $http_x_forwarded_proto;
}
# 此行以下为高级配置
# saml_sp.server_conf 中的其他高级配置(服务器上下文)。
######### 保存 SAML 相关键值数据库的共享内存区
# 该区域用于存储 AuthnRequest 和 LogoutRequest 消息标识符 (ID)
# 以防止重放攻击。(必选项)
# 超时 (timeout) 决定了 SP 等待 IDP 响应的时长,
# 即用户身份验证流程所用时长。
keyval_zone zone=saml_request_id:1M state=/var/lib/nginx/state/saml_request_id.json timeout=5m;
# 该区域用于存储 SAML Response 消息标识符 (ID) 以防止重放攻击。(必选项)
# 超时决定了 SP 为防止重用而保存 ID 的时长。
keyval_zone zone=saml_response_id:1M state=/var/lib/nginx/state/saml_response_id.json timeout=1h;
# 该区域用于存储 SAML 会话访问信息。(必选项)
# 超时决定了 SP 保存会话访问决策的时长(会话有效期)。
keyval_zone zone=saml_session_access:1M state=/var/lib/nginx/state/saml_session_access.json timeout=1h;
# 该区域用于存储 SAML NameID 值。(必选项)
# 超时决定了 SP 保存 NameID 值的时长。必须等于会话有效期。
keyval_zone zone=saml_name_id:1M state=/var/lib/nginx/state/saml_name_id.json timeout=1h;
# 该区域用于存储 SAML NameID 格式值。(必选项)
# 超时决定了 SP 保存 NameID 格式值的时长。必须等于会话有效期。
keyval_zone zone=saml_name_id_format:1M state=/var/lib/nginx/state/saml_name_id_format.json timeout=1h;
# 该区域用于存储 SAML SessionIndex 值。(必选项)
# 超时决定了 SP 保存 SessionIndex 值的时长。必须等于会话有效期。
keyval_zone zone=saml_session_index:1M state=/var/lib/nginx/state/saml_session_index.json timeout=1h;
# 该区域用于存储 SAML AuthnContextClassRef 值。(必选项)
# 超时决定了 SP 保存 AuthnContextClassRef 值的时长。必须等于会话有效期。
keyval_zone zone=saml_authn_context_class_ref:1M state=/var/lib/nginx/state/saml_authn_context_class_ref.json timeout=1h;
# 该区域用于存储 SAML 属性值。(可选项)
# 超时决定了 SP 保存属性值的时长。必须等于会话有效期。
keyval_zone zone=saml_attrib_uid:1M state=/var/lib/nginx/state/saml_attrib_uid.json timeout=1h;
keyval_zone zone=saml_attrib_name:1M state=/var/lib/nginx/state/saml_attrib_name.json timeout=1h;
keyval_zone zone=saml_attrib_memberOf:1M state=/var/lib/nginx/state/saml_attrib_memberOf.json timeout=1h;
######### SAML 相关变量,其值可使用键值数据库中的键(会话 cookie)进行查询。
# 必选项:
keyval $saml_request_id $saml_request_redeemed zone=saml_request_id; # SAML Request ID
keyval $saml_response_id $saml_response_redeemed zone=saml_response_id; # SAML Response ID
keyval $cookie_auth_token $saml_access_granted zone=saml_session_access; # SAML Access decision
keyval $cookie_auth_token $saml_name_id zone=saml_name_id; # SAML NameID
keyval $cookie_auth_token $saml_name_id_format zone=saml_name_id_format; # SAML NameIDFormat
keyval $cookie_auth_token $saml_session_index zone=saml_session_index; # SAML SessionIndex
keyval $cookie_auth_token $saml_authn_context_class_ref zone=saml_authn_context_class_ref; # SAML AuthnContextClassRef
# 可选项:
keyval $cookie_auth_token $saml_attrib_uid zone=saml_attrib_uid;
keyval $cookie_auth_token $saml_attrib_name zone=saml_attrib_name;
keyval $cookie_auth_token $saml_attrib_memberOf zone=saml_attrib_memberOf;
keyval_zone zone=saml_attrib_mail:1M state=/var/lib/nginx/state/saml_attrib_mail.json timeout=1h;
keyval $cookie_auth_token $saml_attrib_mail zone=saml_attrib_mail;
keyval $cookie_auth_token $saml_attrib_objectid zone=saml_attrib_objectid;
keyval_zone zone=saml_attrib_objectid:1M state=/var/lib/nginx/state/saml_attrib_objectid.json timeout=1h;
######### 导入实现 SAML SSO 和 SLO 功能的模块
js_import samlsp from conf.d/saml_sp.js;
测试配置分为两步:
在使用 NGINX Plus 配置 SAML SP 和使用 Microsoft Entra ID 配置 IdP 后,必须验证 SAML 流。此验证流程可确保用户通过 IdP 成功进行身份验证,并获准访问受 SP 保护的资源。
要验证 SP 发起的 SAML 流,请打开您的常用浏览器,然后在地址栏中键入 https://dev.sports.com。这会将您定向到 IdP 登录页面。
图 8:IdP 登录页面
输入在 IdP 登录页面中配置的用户的凭证。提交后,IdP 将对用户进行身份验证。
图 9:输入配置的用户凭证
成功建立会话后,用户将获准访问先前请求的受保护资源。随后,该资源将显示在用户的浏览器中。
图 10:成功加载的应用页面
有关 SAML 流的详细信息可从 SP 和 IdP 日志中查看。在 SP 端 (NGINX Plus),确保正确设置 auth_token cookie。在 IdP 端 (Microsoft Entra Id),确保身份验证流程正确完成,并将 SAML 断言发送至 SP。
The NGINX access.log 应如下所示:
127.0.0.1 - - [14/Aug/2023:21:25:49 +0000] "GET / HTTP/1.0" 200 127 "https://login.microsoftonline.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15" "-"
99.187.244.63 - Akash Ananthanarayanan [14/Aug/2023:21:25:49 +0000] "GET / HTTP/1.1" "dev.sports.com" 200 127 "https://login.microsoftonline.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15" "-
NGINX debug.log 应为:
2023/08/14 21:25:49 [info] 27513#27513: *399 js: SAML SP success, creating session _d4db9b93c415ee7b4e057a4bb195df6cd0be7e4d
SAML 单点注销 (SLO) 允许用户只需一次操作即可注销所有相关的 IdP 和 SP。NGINX Plus 支持 SP 发起和 IdP 发起的注销方案,可增强 SSO 环境中的安全防护并提升用户体验。在本例中,我们使用 SP 发起的注销方案。
图 11:SAML SP 发起的 SLO,采用 POST/Redirect binding 发送 LogoutRequest 和 LogoutResponse
对会话进行身份验证后,访问 SP 中配置的注销 URL 便可注销。例如,如果您在 NGINX Plus 中将 https://dev.sports.com/logout 设置为注销 URL,在浏览器的地址栏中输入该 URL 即可。
图 12:成功注销会话
为了确保安全注销,SP 必须发起一个 SAML 请求,然后由 IdP 进行验证和处理。此操作可有效卸载用户的会话,然后 IdP 将发送一个 SAML 响应,将用户的浏览器重定向回 SP。
恭喜!现在 NGINX Plus可以充当 SAML SP,进一步提升身份验证流程的安全性和便利性了。对于重视安全防护和效率的企业而言,这一新功能使得 NGINX Plus 成为一个更为可靠和多功能的解决方案。
请开启 NGINX Plus 30 天免费试用,立即开始结合使用 SAML 和 NGINX Plus。希望它对您有所帮助,欢迎提出反馈意见。
有关支持 SAML 的 NGINX Plus 的更多信息,请参阅以下资源。
"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."