博客 | NGINX

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

阿卡什·阿南塔纳拉亚南 缩略图
阿卡什·阿南塔纳拉亚南
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文件:

# This is file frontend.conf 
# This is the backend application we are protecting with SAML SSO 
upstream my_backend { 
    zone my_backend 64k; 
    server dev.sports.com; 
} 

# Custom log format to include the 'NameID' subject in the REMOTE_USER field 
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"'; 

# The frontend server - reverse proxy with SAML SSO authentication 
# 
server { 
    # Functional locations implementing SAML SSO support 
    include conf.d/saml_sp.server_conf; 
 

    # Reduce severity level as required 
    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 / { 
        # When a user is not authenticated (i.e., the "saml_access_granted." 
        # variable is not set to "1"), an HTTP 401 Unauthorized error is 
        # returned, which is handled by the @do_samlsp_flow named location. 
        error_page 401 = @do_samlsp_flow; 

        if ($saml_access_granted != "1") { 
            return 401; 
        } 

        # Successfully authenticated users are proxied to the backend, 
        # with the NameID attribute passed as an HTTP header        
        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         

为了使属性saml_attrib_mailsaml_attrib_ objectid反映在 NGINX 配置中,请按如下方式更新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_签名密钥用于签署从 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 configuration 

map $host $saml_sp_entity_id { 
    # Unique identifier that identifies the SP to the IdP. 
    # Must be URL or URN. 
    default "https://dev.sports.com"; 
} 

map $host $saml_sp_acs_url { 
    # The ACS URL, an endpoint on the SP where the IdP  
    # will redirect to with its authentication response. 
    # Must match the ACS location defined in the "saml_sp.serer_conf" file. 
    default "https://dev.sports.com/saml/acs"; 
} 

map $host $saml_sp_request_binding { 
    # Refers to the method by which an authentication request is sent from 
    # the SP to an IdP during the Single Sign-On (SSO) process. 
    # Only HTTP-POST or HTTP-Redirect methods are allowed. 
    default 'HTTP-POST'; 
} 

map $host $saml_sp_sign_authn { 
    # Whether the SP should sign the AuthnRequest sent to the IdP. 
    default "false"; 
} 

map $host $saml_sp_decryption_key { 
    # Specifies the private key that the SP uses to decrypt encrypted assertion 
    # or NameID from the IdP. 
    default ""; 
} 

map $host $saml_sp_force_authn { 
    # Whether the SP should force re-authentication of the user by the IdP. 
    default "false"; 
} 

map $host $saml_sp_nameid_format { 
    # Indicates the desired format of the name identifier in the SAML assertion 
    # generated by the IdP. Check section 8.3 of the SAML 2.0 Core specification 
    # (http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) 
    # for the list of allowed NameID Formats. 
    default "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"; 
} 

map $host $saml_sp_relay_state { 
    # Relative or absolute URL the SP should redirect to 
    # after successful sign on. 
    default ""; 
} 

map $host $saml_sp_want_signed_response { 
    # Whether the SP wants the SAML Response from the IdP 
    # to be digitally signed. 
    default "false"; 
} 

map $host $saml_sp_want_signed_assertion { 
    # Whether the SP wants the SAML Assertion from the IdP 
    # to be digitally signed. 
    default "true"; 
} 

map $host $saml_sp_want_encrypted_assertion { 
    # Whether the SP wants the SAML Assertion from the IdP 
    # to be encrypted. 
    default "false"; 
} 

map $host $saml_idp_entity_id { 
    # Unique identifier that identifies the IdP to the SP. 
    # Must be URL or URN. 
    default "https://sts.windows.net/8807dced-9637-4205-a520-423077750c60/"; 
} 

map $host $saml_idp_sso_url { 
    # IdP endpoint that the SP will send the SAML AuthnRequest to initiate 
    # an authentication process. 
    default "https://login.microsoftonline.com/8807dced-9637-4205-a520-423077750c60/saml2"; 
} 

map $host $saml_idp_verification_certificate { 
    # Certificate file that will be used to verify the digital signature 
    # on the SAML Response, LogoutRequest or LogoutResponse received from IdP. 
    # Must be public key in PKCS#1 format. See documentation on how to convert 
    # X.509 PEM to DER format. 
    default "/etc/nginx/conf.d/demonginx.spki"; 
} 

######### Single Logout (SLO) ######### 

map $host $saml_sp_slo_url { 
    # SP endpoint that the IdP will send the SAML LogoutRequest to initiate 
    # a logout process or LogoutResponse to confirm the logout. 
    default "https://dev.sports.com/saml/sls"; 
} 

map $host $saml_sp_slo_binding { 
    # Refers to the method by which a LogoutRequest or LogoutResponse 
    # is sent from the SP to an IdP during the Single Logout (SLO) process. 
    # Only HTTP-POST or HTTP-Redirect methods are allowed. 
    default 'HTTP-POST'; 
} 

map $host $saml_sp_sign_slo { 
    # Whether the SP must sign the LogoutRequest or LogoutResponse 
    # sent to the IdP. 
    default "false"; 
} 

map $host $saml_idp_slo_url { 
    # IdP endpoint that the SP will send the LogoutRequest to initiate 
    # a logout process or LogoutResponse to confirm the logout. 
    # If not set, the SAML Single Logout (SLO) feature is DISABLED and 
    # requests to the 'logout' location will result in the termination 
    # of the user session and a redirect to the logout landing page. 
    default "https://login.microsoftonline.com/8807dced-9637-4205-a520-423077750c60/saml2"; 
} 

map $host $saml_sp_want_signed_slo { 
    # Whether the SP wants the SAML LogoutRequest or LogoutResponse from the IdP 
    # to be digitally signed. 
    default "true"; 
} 

map $host $saml_logout_landing_page { 
    # Where to redirect user after requesting /logout location. This can be 
    # replaced with a custom logout page, or complete 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; 
} 
# ADVANCED CONFIGURATION BELOW THIS LINE 
# Additional advanced configuration (server context) in saml_sp.server_conf 

######### Shared memory zones that keep the SAML-related key-value databases 

# Zone for storing AuthnRequest and LogoutRequest message identifiers (ID) 
# to prevent replay attacks. (REQUIRED) 
# Timeout determines how long the SP waits for a response from the IDP, 
# i.e. how long the user authentication process can take. 
keyval_zone zone=saml_request_id:1M                 state=/var/lib/nginx/state/saml_request_id.json                  timeout=5m; 

# Zone for storing SAML Response message identifiers (ID) to prevent replay attacks. (REQUIRED) 
# Timeout determines how long the SP keeps IDs to prevent reuse. 
keyval_zone zone=saml_response_id:1M                state=/var/lib/nginx/state/saml_response_id.json                 timeout=1h; 

# Zone for storing SAML session access information. (REQUIRED) 
# Timeout determines how long the SP keeps session access decision (the session lifetime). 
keyval_zone zone=saml_session_access:1M             state=/var/lib/nginx/state/saml_session_access.json              timeout=1h; 

# Zone for storing SAML NameID values. (REQUIRED) 
# Timeout determines how long the SP keeps NameID values. Must be equal to session lifetime. 
keyval_zone zone=saml_name_id:1M                    state=/var/lib/nginx/state/saml_name_id.json                     timeout=1h; 

# Zone for storing SAML NameID format values. (REQUIRED) 
# Timeout determines how long the SP keeps NameID format values. Must be equal to session lifetime. 
keyval_zone zone=saml_name_id_format:1M             state=/var/lib/nginx/state/saml_name_id_format.json              timeout=1h; 

# Zone for storing SAML SessionIndex values. (REQUIRED) 
# Timeout determines how long the SP keeps SessionIndex values. Must be equal to session lifetime. 
keyval_zone zone=saml_session_index:1M              state=/var/lib/nginx/state/saml_session_index.json               timeout=1h; 
  
# Zone for storing SAML AuthnContextClassRef values. (REQUIRED) 
# Timeout determines how long the SP keeps AuthnContextClassRef values. Must be equal to session lifetime. 
keyval_zone zone=saml_authn_context_class_ref:1M    state=/var/lib/nginx/state/saml_authn_context_class_ref.json     timeout=1h; 

# Zones for storing SAML attributes values. (OPTIONAL) 
# Timeout determines how long the SP keeps attributes values. Must be equal to session lifetime. 
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-related variables whose value is looked up by the key (session cookie) in the key-value database. 

# Required: 
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 

# Optional: 
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; 
  

######### Imports a module that implements SAML SSO and SLO functionality 
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, 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 

测试 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 内容。”