在如今勒索软件和机器人攻击如此常见的大环境下,保护应用和 API 变得至关重要 —— 我们已多次撰文强调这一点。除了 Web 应用防火墙(WAF)等机制之外,验证用户身份和实施授权控制也是业务应用防护的重要方式。
在应用中进行身份验证和授权是最直接的办法。但这种方法只适用于用户群较小且不需要频繁更新的应用,反之就行不通了。首先,由于每个应用都有不同的账户名和密码,他们在登录时经常会遇到“用户名或密码不正确”的问题,为了好记,他们会采用不安全的解决方案,例如容易被猜到的 “abc123” 密码。此外,我们都见过显示器上贴着密码提示便利贴的情形!
单点登录 (SSO) 技术利用一组凭证消除了单独设置各种用户名和密码的需要,从而在一定程度上解决了这些问题。借助身份提供商 (IdP),用户只需登录一次即可访问许多应用。但开发人员仍必须在应用中写入代码以便与 SSO 系统交互,这项工作非常具有挑战性,特别是在应用变得日益复杂的情况下。
随着企业规模不断扩大,要想满足不断增长的用户群的要求,将那些非应用本身特定需要的功能(例如身份验证和授权)从应用层卸载下来就变得至关重要。在 Kubernetes 中,Ingress controller 是进行集中式身份验证和授权的理想位置,因为它可以检查进入 Kubernetes 集群的所有流量,并将其路由到相应的服务。开发人员不仅可以摆脱构建、维护和复制身份验证逻辑的麻烦,而且还可以使用原生 Kubernetes API 即可在入口层轻松使用 SSO 技术。
本文将展示如何使用基于 NGINX Plus 的 NGINX Ingress Controller 实现完整的 SSO 解决方案。其中 NGINX Ingress Controller 可以作为中继方运行,利用 Okta(作为预配置身份提供商 (IdP))为 OIDC 授权代码流提供支持。
注:基于 NGINX 开源版的 NGINX Ingress Controller 不提供此功能。
前提条件
本文假设您拥有 Kubernetes 环境操作经验。此外,您还需要:
- Kubernetes 环境 —— NGINX Ingress Controller 适用于 Vanilla Kubernetes 以及许多 Kubernetes 平台,包括 Amazon Elastic Kubernetes (EKS)、裸机、Google Kubernetes Engine (GKE)、Microsoft Azure Kubernetes Service (AKS)、Rancher Kubernetes Engine 及 Red Hat OpenShift。
- 基于 NGINX Plus 的 NGINX Ingress Controller —— 必须拥有基于 NGINX Plus 版的 NGINX Ingress Controller 的有效许可。您可以先申请 30 天免费试用版许可。更多相关信息,请参阅我们的文档。
- Okta 开发者账号 —— 要将 Okta 配置为 IdP,请先注册一个开发者账号。作为替代方案,您也可以下载 Okta 命令行接口(CLI)并运行
okta
register
命令,注册一个新账户。在撰写本文时,Okta CLI 尚处于测试阶段,因此不建议在生产环境中使用。
预配置 IdP
云服务必须清楚在哪里检索和验证用户身份,这就需要使用 IdP。IdP 能够安全地管理和存储数字身份,并确保攻击者无法窃取用户身份并冒充用户。
在本节中,我们使用 Okta CLI 将 Okta 预配置为 IdP,创建 Okta 中所说的 应用集成。
-
运行
okta
login
命令,验证 Okta 开发者账号的 Okta CLI。根据提示输入 Okta 域名和 API 令牌。$ okta login Okta Org URL: https://your-okta-domain Okta API token: your-api-token
-
创建应用集成:
$ okta apps create --app-name=mywebapp --redirect-uri=http[s]://ingress-controller-hostname/_codexch
其中,
--app-name
定义应用名称(此处为 mywebapp)--redirect-uri
定义将登录行为重定向到了哪个 URI(此处为 ingress-controller-hostname/_codexch)
-
指定响应提示的应用类型,首先用 1(表示一个 Web 应用),然后用 5(表示一个与列出的框架不同的框架)。
Type of Application (The Okta CLI only supports a subset of application types and properties): > 1: Web > 2: Single Page App > 3: Native App (mobile) > 4: Service (Machine-to-Machine) Enter your choice [Web]: 1 Type of Application > 1: Okta Spring Boot Starter > 2: Spring Boot > 3: JHipster > 4: Quarkus > 5: Other Enter your choice [Other]: 5 Configuring a new OIDC Application, almost done: Created OIDC application, client-id: 0oa1mi...OrfQAg5d7
配置 NGINX Ingress Controller
将基于 NGINX Plus 的 NGINX Ingress Controller 配置为验证用户身份的中继方。
定义客户端凭证密钥
出于安全原因,系统不支持对 OIDC 策略对象中的客户端密码进行硬编码。因而,我们创建了一个 Kubernetes Secret 对象,其中包含客户机密的 base64 编码值的数据。
apiVersion: v1
kind: Secret
metadata:
name: oidc-secret
type: nginx.org/oidc
data:
client-secret: base64-encoded-value-of-client-secret
然后应用包含 Secret 对象的 YAML 文件(此处为 client-secret.yaml):
$ kubectl apply –f client-secret.yaml
获取身份验证端点
使用 OAuth 2.0 和 OpenID Connect API 获取有关 Okta 在其授权服务器上公开的端点的信息。
在本地设备上运行以下命令,以输出有关 Okta 端点的信息。注意输出示例中显示的 authorization_endpoint
、token_endpoint
和 jwks_uri
的值。您在下一节中需要使用这些值。
$ curl -i https://your-okta-domain/.well-known/openid-configuration
{
"authorization_endpoint": "https://your-okta-domain/oauth2/v1/authorize",
...
"jwks_uri": "https://your-okta-domain/oauth2/v1/keys",
...
"token_endpoint": "https://your-okta-domain/oauth2/v1/token",
...
}
定义 NGINX Ingress OIDC 策略
NGINX Ingress Controller 1.10.0 中添加了对基于 OIDC 的身份验证的支持。
NGINX Ingress Controller 实现的 OIDC 身份验证的使用的是一个 Policy
对象,它是一个 Kubernetes 自定义资源,在 NGINX Ingress Controller 中定义了 OIDC 策略。
-
将上一节中获得的信息插入
Policy
对象的authEndpoint
、tokenEndpoint
和jwksURI
字段。apiVersion: k8s.nginx.org/v1 kind: Policy metadata: name: oidc-policy spec: oidc: clientID: client-id clientSecret: oidc-secret authEndpoint: https://your-okta-domain/oauth2/v1/authorize tokenEndpoint: https://your-okta-domain/oauth2/v1/token jwksURI: https://your-okta-domain/oauth2/v1/keys
-
应用策略(此处在 oidc.yaml 中进行定义):
$ kubectl apply -f oidc.yaml
-
(可选)检查策略的有效性:
$ kubectl get policy NAME STATE AGE oidc-policy Valid 2m
定义 VirtualServer 对象
VirtualServer 和 VirtualServerRoute 是 NGINX Ingress 资源,它们制定了将入向流量路由到 Kubernetes 集群中后端应用的规则。必须在 VirtualServer 或 VirtualServerRoute 资源中引用 OIDC 策略才能使其生效。
-
在 / 路径前缀下引用 OIDC 策略,以便在将请求代理到
app-server-payload
服务之前,对请求匹配该前缀的路径的用户进行身份验证。apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: app-ingress spec: host: unit-demo.linkpc.net upstreams: - name: app-server-payload service: app-server-svc port: 80 routes: - path: / policies: - name: oidc-policy action: proxy: upstream: app-server-payload
-
应用 VirtualServer 资源(此处在 app-virtual-server.yaml 中进行定义):
$ kubectl apply -f app-virtual-server.yaml
-
(可选)验证资源的有效性:
$ kubectl get vs NAME STATE HOST IP PORTS AGE app-ingress Valid unit-demo.linkpc.net 2m
测试环境
要测试 OIDC Okta 集成能否正常工作,请在浏览器的地址栏中输入 NGINX Ingress Controller 的主机名。您将被重定向到 Okta 登录门户,您可在该门户上输入 Okta 开发者账号的凭证以访问后端应用。
一旦通过身份验证,即可访问 app-server-payload
上游服务。
将用户添加到应用
在大多数情况下,企业的多个用户都需要访问应用。在 Okta 管理员控制台目录(Directory)类别下的人员(People)页面上添加每个用户。
为 SSO 创建多个应用集成
我们使用 Okta(作为 IdP)和 NGINX Ingress Controller(作为中继方)配置 SSO,从而卸载一个应用的身份验证流程。在实践中,您可能希望用户能够使用一组凭证访问多个应用。您可能还希望能够灵活地改变用户可以访问的应用。
您可以通过 集成 Okta 与其他应用、定义其他 OIDC 策略并在 VirtualServer 资源中引用这些策略来实现这一点。在下图描述的示例中,有两个子域名:unit-demo.marketing.net 和 unit-demo.engineering.net,它们解析为 NGINX Ingress Controller 的外部 IP 地址。NGINX Ingress Controller 根据子域名将请求路由到 营销(Marketing) 应用或 工程(Engineering)应用。如要授予用户访问权限,请在 Okta GUI 的 分配(Assignments) 选项卡上,将用户与每个适当的应用相关联。然后,Okta 向通过身份验证的用户颁发一个短暂的会话 cookie,以支持他们访问这些应用。
结语
通过将 NGINX Ingress Controller 配置为中继方,将 Okta 配置为 IdP,您可以在 Kubernetes 中实现基于 OIDC 的 SSO,并消除开发人员的身份验证和授权负担,让他们专注于优化应用中的业务逻辑。如欲试用基于 NGINX Plus 的 Ingress Controller,请立即申请 30 天免费试用版或与我们联系以讨论您的用例。