博客 | NGINX

使用 Microsoft Entra ID 配置 NGINX Plus 以实现 SAML SSO

NGINX-F5-horiz-black-type-RGB 的一部分
阿卡什·阿南塔纳拉亚南 缩略图
阿卡什·阿南塔纳拉亚南
2023 年 10 月 31 日发布

为了增强安全性并改善用户体验, 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 标头将它们传递给应用。

本教程包括三个步骤:

  1. 将 Microsoft Entra ID 配置为 IdP
  2. 配置 SAML 设置和 NGINX Plus 作为反向代理
  3. 测试配置

要完成本教程,您需要:

  • NGINX Plus (R29+),您可以免费试用 30 天
  • 免费或企业 Microsoft Entra ID 帐户
  • 在 NGINX Plus 服务器上安装有效的 SSL/TLS 证书(本教程使用dev.sports.com.crt 和 dev.sports.com.key
  • 验证 SAML 断言,可以通过从 IdP 下载公共证书demonginx.cer来完成

笔记: 本教程不适用于 NGINX 开源部署,因为键值存储是 NGINX Plus 独有的。

使用 NGINX Plus 作为 SAML 服务提供商

在此设置中,NGINX Plus 充当 SAML SP,并可以通过 SAML IdP 参与 SSO 实施,后者通过用户代理间接与 NGINX Plus 通信。

下图说明了 SSO 流程,其中包含 SP 启动和请求与响应的 POST 绑定。 需要再次注意的是,该通信渠道不是直接的,而是通过用户代理进行管理的。

图 1: SAML SP 发起的 SSO,具有针对 AuthnRequest 和 Response 的 POST 绑定

步骤 1: 将 Microsoft Entra ID 配置为身份提供者

要访问您的 Microsoft Entra ID 管理门户,请登录并导航到左侧面板。 选择Microsoft Entra ID ,然后单击需要 SSO 配置的目录标题。 选择后,选择企业应用


图 2: 在管理门户中选择企业应用

要创建应用,请单击门户顶部的“新建应用程序”按钮。 在这个例子中,我们创建了一个名为demonginx的应用。

图 3: 在 Microsoft Entra ID 中创建新应用

重定向到新创建的应用概述后,通过左侧菜单转到入门,然后单击管理下的单点登录。 然后,选择SAML作为单点登录方法

图4: 使用 SSO 部分启动 SAML 配置

要在企业应用中设置 SSO,您需要在 Microsoft Entra ID 中将 NGINX Plus 注册为 SP。为此,请单击基本 SAML 配置编辑旁边的铅笔图标,如图 5 所示。

添加以下值,然后单击“保存”

  • 标识符(实体 ID) – https://dev.sports.com
  • 回复 URL(断言消费者服务 URL) – https://dev.sports.com/saml/acs
  • 登录网址:https://dev.sports.com
  • 注销网址(可选) :https://dev.sports.com/saml/sls

验证证书的使用是可选的。 启用此设置时,必须解决 NGINX 中的两个配置选项:

  1. 要使用公钥验证签名,您需要将$saml_sp_sign_authn设置为true 。 这指示 SP 对发送给 IdP 的 AuthnRequest 进行签名。
  2. 通过配置$saml_sp_signing_key提供用于此签名的私钥路径。 确保将相应的公钥证书上传到Microsoft Entra ID进行签名验证。

笔记: 在此演示中,属性和声明已被修改,并且添加了新的 SAML 属性。 这些 SAML 属性由 IdP 发送。 确保您的 NGINX 配置设置为正确接收和处理这些属性。 您可以在NGINX GitHub repo中检查和调整相关设置。

从 Microsoft Entra ID 下载 IdP证书(原始)并将其保存到您的 NGINX Plus 实例。

图5: 从 Microsoft Entra ID 下载 IdP 证书(原始)

图6: 添加新用户或组

在 Microsoft Entra ID 中,您可以通过添加或分配用户和组来授予对支持 SSO 的公司应用的访问权限。

在左侧菜单上,单击“用户和组” ,然后单击顶部按钮“添加用户/组”

第 2 步: 配置 SAML 设置并将 NGINX Plus 配置为反向代理

在 NGINX Plus SP 中配置文件之前,请确保您拥有必要的证书:

  • 用于终止 TLS 会话的证书( dev.sports.com.crt 和 dev.sports.com.key
  • 从 Microsoft Entra ID 下载的证书用于 IdP 签名验证( demonginx.cer

笔记: 证书需要采用 SPKI 格式。

要开始此步骤,请从 Microsoft Entra ID 下载 IdP 证书进行签名验证。 然后,将 PEM 转换为 DER 格式:

openssl x509 -in demonginx.cer -outform DER -out demonginx.der

如果您想验证 SAML SP 断言,建议使用与用于 TLS 终止的公钥/私钥不同的公钥/私钥。

提取SPKI格式的公钥证书:

openssl x509 -inform DER -in demonginx.der -pubkey -noout > demonginx.spki

编辑 frontend.conf 文件以更新以下项目:

  • ssl_certificate – 更新以包含 TLS 证书路径。
  • ssl_certificate_key – 更新以包含 TLS 私钥路径。

在生产部署中,您可以根据业务需求使用不同的后端目的地。 在此示例中,后端提供了定制的响应:

“欢迎来到application页面\n我的objectid是$http_objectid\n我的电子邮件是$http_mail\n”;

我们通过为用户的邮件objectid添加新的声明,修改了 Microsoft Entra ID 中的属性和声明。 这些更新使您能够为您的应用提供更加个性化和定制化的响应,从而改善用户体验。

图 7: Microsoft Entra ID 中修改的属性和声明

下一步是配置 NGINX,它将代理流量到后端应用。 在此演示中,后端 SAML应用在https://dev.sports.com上公开提供。

编辑你的frontend.conf文件:

# 这是文件 frontend.conf
# 这是我们使用 SAML SSO 保护的后端应用
上游 my_backend {
区域my_backend 64k;
服务器 dev.sports.com;
}

# 自定义日志格式,在 REMOTE_USER 字段中包含“NameID”主题
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 身份验证的反向代理
#
服务器 {
# 实现 SAML SSO 支持的功能位置
包括conf.d/saml_sp.server_conf;


# 按要求降低严重程度
错误日志 /var/log/nginx/error.log 调试;
听443 ssl;
ssl_certificate /home/ubuntu/dev.sports.com.crt;
ssl_certificate_key /home/ubuntu/dev.sports.com.key;
ssl_session_cache共享:SSL:5米;


地点 / {
# 当用户未通过身份验证时(即“saml_access_granted”。
# 变量未设置为“1”),则会出现 HTTP 401 未授权错误
# 返回,由@do_samlsp_flow 命名位置处理。
错误页面 401 = @do_samlsp_flow;

如果 ($saml_access_granted != "1") {
返回401;
}

# 成功验证的用户被代理到后端,
# 将 NameID 属性作为 HTTP 标头传递
proxy_set_header mail $saml_attrib_mail; # Microsoft Entra ID 的用户邮件
proxy_set_header objectid $saml_attrib_objectid; # Microsoft Entra ID 的对象 ID
访问日志 /var/log/nginx/access.log saml_sso;
代理密码<a href="http://my_backend;">http://my_backend;</a>
proxy_set_header 主机 dev.sports.com;
return 200 "欢迎来到应用页面\n我的objectid是$http_objectid\n我的电子邮件是$http_mail\n";
默认类型文本/纯文本;

}
}
# vim: 语法=nginx         

为了使属性saml_attrib_mailsaml_attrib_ objectid反映在 NGINX 配置中,请按如下方式更新saml_sp_configuration.conf的键值存储部分:

keyval_zone 区域=saml_attrib_mail:1M 状态=/var/lib/nginx/state/saml_attrib_email.json 超时=1h; 
keyval $cookie_auth_token $saml_attrib_mail 区域=saml_attrib_mail; 

keyval_zone 区域=saml_attrib_objectid:1M 状态=/var/lib/nginx/state/saml_attrib_objectid.json 超时=1h; 
keyval $cookie_auth_token $saml_attrib_objectid 区域=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_签名密钥用于签署从 SP 到 IdP 的 SAML 请求的签名密钥。 确保将相关证书上传到 IdP 以验证签名。
saml_idp_entity_id用于定义 IdP 的身份。
saml_idp_sso_urlSP 向其发送 SAML 断言请求以启动身份验证请求的 IdP 端点。
saml_idp_verification_certificate用于验证从 IdP 收到的签名 SAML 断言的认证。 该证书由IdP提供,需要采用SPKI格式。
网址IdP 向其发送 SAML LogoutRequest(启动注销过程时)或 LogoutResponse(确认注销时)的 SP 端点。
签名指定注销 SAML 是否要由 SP 签名。
示例:saml_idp_slo_urlSP 向其发送 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,IdP 将使用其身份验证响应重定向到的 SP 上的端点。
# 必须与“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 Core 规范的第 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 响应 
# 进行数字签名。 
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 以启动 
# 注销过程的 SP 端点或用于确认注销的 LogoutResponse。
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。

默认“false”; 
} 

map $host $saml_idp_slo_url { 
# SP 将发送 LogoutRequest 以启动注销过程的 IdP 端点或发送 LogoutResponse 以确认注销。
# 如果未设置,则 SAML 单点注销 (SLO) 功能将被禁用,并且对“注销”位置的请求将导致用户会话终止并重定向到注销登录页面。

默认“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 位置后将用户重定向到何处。 这可以 
# 用自定义注销页面或完整 URL 替换。 
default "/_logout"; # 内置的简单注销页面 
} 

map $proto $saml_cookie_flags { 
http "Path=/; SameSite=lax;"; # 用于 HTTP/纯文本测试 
https "Path=/; SameSite=lax; HttpOnly; Secure;"; # 生产建议 
} 

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) 的区域 
# 以防止重放攻击。 (必需)
# 超时决定 SP 等待 IDP 响应的时间,
# 即用户身份验证过程需要多长时间。
keyval_zone zone=saml_request_id:1M state=/var/lib/nginx/state/saml_request_id.json timeout=5m;

# 用于存储 SAML 响应消息标识符 (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 请求 ID 
keyval $saml_response_id $saml_response_redeemed zone=saml_response_id; # SAML 响应 ID 
keyval $cookie_auth_token $saml_access_granted zone=saml_session_access; # SAML 访问决策 
keyval $cookie_auth_token $saml_name_id zone=saml_name_id; # SAML 名称 ID 
keyval $cookie_auth_token $saml_name_id_format zone=saml_name_id_format; # SAML 名称 ID 格式 
keyval $cookie_auth_token $saml_session_index zone=saml_session_index; # SAML 会话索引 
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; 

步骤3: 测试配置

测试配置需要两部分:

  1. 验证 SAML 流程
  2. 测试 SP 发起的注销功能

验证 SAML 流程

使用 NGINX Plus 配置 SAML SP 并使用 Microsoft Entra ID 配置 IdP 后,验证 SAML 流至关重要。 此验证过程可确保通过 IdP 的用户身份验证成功,并且授予对 SP 保护的资源的访问权限。

要验证 SP 发起的 SAML 流,请打开您喜欢的浏览器并在地址栏中输入https://dev.sports.com 。 这将把您引导至 IdP 登录页面。

图 8: IdP 登录页面

输入在 IdP 登录页面中配置的用户的凭据。 在用户提交后,IdP 将会对用户进行身份验证。

图 9: 输入已配置的用户凭证

成功建立会话后,用户将被授予对先前请求的受保护资源的访问权限。 随后,该资源将显示在用户的浏览器中。

图 10: 成功加载的应用页面

通过检查 SP 和 IdP 日志可以获得有关 SAML 流的有价值的信息。 在 SP 端(NGINX Plus),确保 auth_token cookie 设置正确。 在 IdP 端(Microsoft Entra ID),确保身份验证过程顺利完成,并且 SAML 断言已发送给 SP。

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,如 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,如 Gecko)版本/16.1 Safari/605.1.15" "- 

而 NGINX debug.log如下所示:

2023/08/14 21:25:49 [信息] 27513#27513: *399 js: SAML SP 成功,创建会话 _d4db9b93c415ee7b4e057a4bb195df6cd0be7e4d 

测试 SP 发起的注销功能

SAML 单点注销 (SLO) 允许用户通过一个操作注销所有相关的 IdP 和 SP。 NGINX Plus 支持 SP 发起和 IdP 发起的注销场景,增强 SSO 环境中的安全性和用户体验。 在此示例中,我们使用 SP 发起的注销场景。

图 11: SAML SP 发起的 SLO,具有针对 LogoutRequest 和 LogoutResponse 的 POST/重定向绑定

验证您的会话后,通过访问 SP 中配置的注销 URL 注销。 例如,如果您已将https://dev.sports.com/logout设置为 NGINX Plus 中的注销 URL,请在浏览器的地址栏中输入该 URL。

图 12: 成功退出会话

为了确保安全注销,SP 必须发起 SAML 请求,然后由 IdP 进行验证和处理。 此操作有效地终止了用户的会话,然后 IdP 将发送 SAML 响应以将用户的浏览器重定向回 SP。

结论

恭喜! NGINX Plus 现在可以充当 SAML SP,为身份验证过程提供另一层安全性和便利性。 这一新功能是 NGINX Plus 向前迈出的重要一步,使其成为优先考虑安全性和效率的组织的更强大、多功能的解决方案。 

了解有关将 SAML 与 NGINX Plus 结合使用的更多信息

您现在就可以开始使用 SAML 和 NGINX Plus,只需启动NGINX Plus 的 30 天免费试用即可。 我们希望您发现它有用并欢迎您的反馈。

有关 NGINX Plus 与 SAML 的更多信息,请参阅以下资源。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”