如果您最近参加了网络安全展会,想必已经注意到几乎每个展台都在宣传“零信任 (ZT)”概念。大多数安全防护公司似乎都秉持着相同的价值主张,即“采用零信任模式保护应用安全”。零信任是一个宽泛的术语,涵盖众多用例。
零信任不是一项特性或功能,而是 IT 安全防护领导者所奉行的一种理念。零信任假定是进出系统的所有流量均不可信,必须经过仔细检查才可放行。由于网络攻击的复杂性不断加剧,因此各企业纷纷转而采用零信任理念。基于边界的防火墙已经无法再确保数字资源的安全。
在本系列文章的第一篇,我们将 NGINX Plus 配置为外部负载均衡器,以便将 TLS 流量路由和卸载到集群节点。在本文中,我们将利用同一 NGINX Plus 部署来实现零信任用例,从而改善混合应用的安全防护。
注:如欲实现我们示例中的零信任用例,请先学习本系列文章的第一篇。请确保先完成第一篇的学习,再开始阅读第二篇。
零信任用例 1:OIDC 身份验证
OIDC (OpenID Connect) 是 OAuth 2.0 框架之上的身份验证层。许多企业会选择使用 OIDC 来验证数字身份,并为消费者应用启用 SSO(单点登录)。借助单点登录技术,用户可通过 IdP(身份验证供应商)验证其身份,从而使用一套用户凭证访问多个应用。
除了在第一篇中配置的基本反向代理负载均衡以外,我还可以配置 NGINX Plus 作为 OIDC 中继方,与 IdP 交换 ID 令牌并对其进行验证。
我将使用 IdP 来扩展第一篇中所述的架构,并将 NGINX Plus 配置为身份感知代理。
NGINX Plus 的必备组件
在将 NGINX Plus 配置为 OIDC 身份感知代理之前:
1. 需要安装 njs。
$ sudo apt-get install nginx-plus-module-njs
2. 在 nginx.conf 文件顶部添加下列一行,将 njs 模块加载到 NGINX 配置中。
load_module modules/ngx_http_js_module.so;
3. 将 OIDC GitHub 仓库复制到选定目录下
cd /home/ubuntu && git clone --branch R28 https://github.com/nginxinc/nginx-openid-connect.git
设置 IdP
IdP 将管理和存储数字身份,以防止攻击者冒充用户窃取敏感信息。现有许多 IdP 可供选择:Okta、Ping Identity、Azure AD。在本例中,我们选择了 Okta 作为 IdP。
如果您无法访问 IdP,则可使用 Okta 命令行接口 (CLI) 快速配置一个,并运行 okta register 命令注册一个新账户。
在成功创建账户后,我们将使用 Okta CLI 将 Okta 预配置为 IdP,创建 Okta 中所说的应用集成。其他 IdP 在定义应用集成时会用到不同的术语。例如,Azure AD 将其称为应用注册。 如果您使用的不是 Okta,则可按照所用 IdP 的文档进行操作,然后跳到下一节(将 NGINX 配置为 OpenID Connect 中继方)。
1. 运行 okta login 命令,验证 Okta 开发人员账户的 Okta CLI。根据提示输入 Okta 域名和 API 令牌
$ okta login
Okta Org URL: https://your-okta-domain
Okta API token: your-api-token
2. 创建应用集成
$ okta apps create --app-name=mywebapp --redirect-uri=https://<nginx-plus-hostname>:443/_codexch
其中
- –app-name 定义应用名称(此处为 mywebapp)
- –redirect-uri 定义将登录重定向到 NGINX Plus 的 URL。 应解析为在第一篇文章中配置的 NGINX Plus 的外部 IP。我们使用 443 端口,因为在第一篇文章中,NGINX Plus 上配置了 TLS 终结。回想一下,我们在 NGINX Plus 上配置 TLS 时使用了自签名证书和密钥。在生产环境中,建议使用由 Let’s Encrypt 等受信任证书颁发机构颁发的证书/密钥。
第 2 步中的命令完成后,便可在 ${HOME}/.okta.env 中找到从应用集成生成的客户端 ID 和密钥。
将 NGINX 配置为 OpenID Connect 中继方
现在,我们已经完成 IdP 的设置,可以开始将 NGINX Plus 配置为 OpenID Connect 中继方了。登录到 NGINX Plus 实例后,只需从主目录运行配置脚本即可。
$ ./nginx-openid-connect/configure.sh -h <nginx-plus-hostname> -k request -i <YOURCLIENTID> -s <YOURCLIENTSECRET> –x https://dev-xxxxxxx.okta.com/.well-known/openid-configuration
其中
- -h 定义 NGINX Plus 的主机名
- -k 定义 NGINX 将如何检索 JWK 文件以验证 JWT 签名。JWK 文件可从发送至 IdP 的子请求中检索出来
- -i 定义从 IdP 生成的客户端 ID
- -s 定义从 IdP 生成的客户端密钥
- -x 定义 OpenID 配置端点的 URL。以 Okta 为例,URL 使用您的 Okta 组织域名开头,后跟路径 URI /.well-known/openid-configuration
配置脚本将为 NGINX Plus 生成 OIDC 配置文件。我们需要将生成的配置文件复制到第一篇文章中所述的 /etc/nginx/conf.d 目录下。
$ sudo cp frontend.conf openid_connect.js openid_connect.server_conf openid_connect_configuration.conf /etc/nginx/conf.d/
默认情况下,frontend.conf 会以明文 http 方式监听 8010 端口。我们需要将 kube_lb.conf 合并到 frontend.conf 中,以实现第一篇文章和本文中介绍的两种用例。生成的 frontend.conf 应如下所示:https://gist.github.com/nginx-gists/af067326734063da6a4ff42146873262
最后,需要编辑 openid_connect_configuration.conf 文件,并将客户端密钥修改为 Okta IdP 生成的密钥。
重新加载 NGINX Plus,让新配置生效。
$ nginx -s reload
测试环境
现在,我们可以测试环境的运行情况了。简言之,我们设置了一个 IdP,并将 NGINX Plus 配置为身份感知代理,以便在用户访问 Kubernetes 集群之前对其 ID 令牌进行验证。
若要测试环境,我们需要打开浏览器,在地址栏中输入 NGINX Plus 的主机名。您将被重定向到 IdP 登录页面。
注:主机名应解析为 NGINX Plus 设备的公共 IP。
浏览器提示您进入 IdP 登录页面后,您就可以在通过用户凭证验证后访问 Kubernetes pod。用户凭证应由 IdP 进行定义。
登录到应用后,已通过身份验证的用户的 ID 令牌将被存储在 NGINX Plus 键值存储中。
通过 OIDC 启用 PKCE
在上一节中,我们学习了如何将 NGINX Plus 配置为 OIDC 中继方,以验证尝试连接到受保护 Kubernetes 应用的用户身份。但在少数情况下,攻击者可以拦截从 IdP 发出的代码交换请求,劫持您的 ID 令牌并入侵您的敏感应用。
PKCE 是 OIDC 授权代码流的扩展,可防止授权代码被截获和窃取。它增强了安全防护,攻击者若要从 IdP 处换取 ID 令牌,不仅需要提供授权代码,而且还需提供代码验证程序。
在我们当前的设置中,在将用户重定向到 IdP 登录页面时,NGINX Plus 会发送一个随机生成的 PKCE 代码验证程序作为查询参数。当使用授权代码换取 ID 令牌时,IdP 将使用此 PKCE 代码验证程序进行额外验证。
NGINX Plus 和 IdP 均需启用 PKCE。要在 NGINX Plus 上启用 PKCE 验证,编辑 openid_connect_configuration.conf 文件,并将 $oidc_pkce_enable 修改为 1,然后重新加载 NGINX Plus。
根据所用的 IdP,选择复选框以启用 PKCE。
测试环境
为了测试 PKCE 是否正常运行,我们将打开浏览器,再次输入 NGINX Plus 主机名。您会被重定向到登录页面,但这次 URL 略有变化,增加了查询参数:
- code_challenge_method —— 用于对明码验证程序进行哈希处理的方法(很可能是 SHA256)
- code_challenge —— 明码验证程序的哈希值
NGINX Plus 将提供此明码验证程序和授权代码以换取 ID 令牌,然后对其进行验证,并将它存储在缓存中。
利用第三方系统扩展 OIDC
客户可能需要将其 OIDC 工作流与已部署于生产环境的专有身份验证/授权系统相集成。例如,可能需要从外部 Redis 缓存或 JFrog Artifactory 中收集与用户相关的其他元数据。我们可以通过扩展上一节中的图表来填补此空白。
除了使用 NGINX Plus 进行令牌验证以外,我还从 JFrog Artifactory 拉取了响应数据,并在用户通过身份验证后将其传输给后端应用。
注:此处,我使用 JFrog Artifactory 作为第三方端点示例。从技术角度来说,我可以按需使用任何端点。
测试环境
为了测试环境的运行状况,我将对 NGINX OIDC 配置进行一些更新。您可从我们更新的 GitHub 仓库中拉取相关信息,作为更新 NGINX 配置的参考。
更新 1:添加 njs 源代码
第一个更新是使用 njs 扩展 NGINX Plus,并从我们的第三方系统中检索响应数据。将 KvOperations.js 源文件添加到 /etc/nginx/njs 目录中
更新 2:frontend.conf
将第 37-39 行添加到 frontend.conf,这样 NGINX Plus 就会在用户通过 OIDC 进行身份验证后向我们的第三方系统发起子请求。我们将此子请求的 URI 设置为 /kvtest。详情请参阅下一项更新。
更新 3:openid_connect.server_conf
我们将第 35-48 行添加到 openid_connect.server_conf,其中包含 NGINX 中的两个内部重定向:
- /kvtest;通过 URI /kvtest 的子请求进行内部重定向将在 KvOperations.js 中发挥作用
- /auth;对 URI /auth 的子请求的内部重定向将被代理到第三方端点。您可将第 47 行中的 <artifactory-url> 替换为自己的端点
更新 4:openid_connect_configuration.conf
此更新为可选项,适用于向第三方端点传递动态变量。您可通过向 NGINX Plus 键值存储发送 POST 请求来动态更新变量。
$ curl -iX POST -d '{"admin", "<input-value>"}' http://localhost:9000/api/7/http/<keyval-zone-name>
我们在第 102-104 行定义/实例化新的键值存储。更新完成后,便可测试优化后的 OIDC 环境,具体方法是故障排除/验证应用是否位于动态输入值的接收端。
结语
本文介绍了一些在混合架构中使用 NGINX 的零信任用例,主要围绕身份验证展开探讨。在本系列文章的下一篇,我将介绍更多零信任用例,包括:
- 其他身份验证方法 (SAML)
- 加密
- 授权和访问控制
- 监控/审计