配置 Anthos Service Mesh 用户身份验证

Anthos Service Mesh 用户身份验证是一种集成式解决方案,用于基于浏览器的最终用户身份验证和对已部署工作负载的访问权限控制。该解决方案可让您与现有的身份提供商 (IDP) 集成以进行用户身份验证,并使用 Istio API 和授权政策管理访问权限。这是 Istio JSON Web 令牌 (JWT) 身份验证的易用替代方案。

典型使用场景是组织使用 Anthos Service Mesh 托管员工通过 Web 浏览器访问的 Web 应用时。此外,组织需要使用其现有身份提供商来管理用户身份。Anthos Service Mesh 用户身份验证可让用户使用标准基于 Web 的 OpenID Connect (OIDC) 登录和同意流程轻松地进行身份验证。用户进行身份验证时,Anthos Service Mesh 会强制执行 Istio 授权政策,成功授权后,它会以安全凭据格式将身份传输到工作负载。

运作方式

Anthos Service Mesh 用户身份验证引入了新的组件 authservice。此组件与基于 Envoy 的 Ingress 集成,作为外部授权服务,该服务可拦截身份验证的所有传入请求。authservice 实现 OIDC 协议的客户端,可让用户通过浏览器访问应用,其中用户完成了交互式身份验证和同意流程,从而构建短期会话。authservice 实现了业界标准协议,以与充当 OIDC 授权服务器的任何身份提供方集成。当用户通过身份验证时,主信息以 JWT 格式封装在 RCToken 中,并由 authservice 签名,后者将转发到 Ingress 中的 Istio 授权层。该模型为发送到网格的流量提供了边界访问控制。如果用户有权访问资源,则此 RCToken 也将转发到微服务,以获取主信息并强制执行精细的访问权限控制。

下图显示了网格中 authservice 的位置以及它与网格其他部分(例如 Ingress、工作负载、用户的浏览器和任何现有 IDP)之间的关系。

最终用户身份验证

管理员可以在安装 Anthos Service Mesh 时以插件形式安装 authservice。安装后,authservice 读取 OIDC 端点配置和 UserAuth 自定义资源中定义的其他相关设置。管理员可以使用 Anthos Service Mesh ExternalAuthorization API 将 auth_server 配置为 Ingress 的过滤器。

安装用户身份验证服务

以下步骤介绍了如何配置 authservice

前提条件

按照使用入门中的步骤执行以下操作:

通过以下步骤确保满足前提条件。

使用用户身份验证叠加层自定义安装

如需安装用户身份验证服务,您必须自定义 Anthos Service Mesh 安装以添加网格级层的外部授权提供商。

kpt v1+

获取用户身份验证叠加层示例,并在网格中进行任何自定义设置时更新该叠加层。建议的最佳做法是在源代码控制系统中维护此叠加文件。

curl https://raw.githubusercontent.com/GoogleCloudPlatform/asm-user-auth/v1.1.0/overlay/user-auth-overlay.yaml > user-auth-overlay.yaml

kpt v1-

获取用户身份验证叠加层示例,并在网格中进行任何自定义设置时更新该叠加层。建议的最佳做法是在源代码控制系统中维护此叠加文件。

curl https://raw.githubusercontent.com/GoogleCloudPlatform/asm-user-auth/v1.0.1/overlay/user-auth-overlay.yaml > user-auth-overlay.yaml
  1. 按照使用叠加层安装 Anthos Service Mesh 的说明,通过 Google 提供的脚本使用用户身份验证叠加层安装 Anthos Service Mesh。例如:

    ./asmcli install \
      --project_id "PROJECT_ID" \
      --cluster_name "CLUSTER_NAME" \
      --cluster_location "CLUSTER_LOCATION" \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --custom_overlay user-auth-overlay.yaml
    

    用户身份验证 kpt 软件包会创建一个 AuthorizationPolicy,用于引用 pkg/ext-authz.yaml 指定的外部授权提供商。

  2. 创建 asm-user-auth 命名空间并为其添加标签。

    kubectl create namespace asm-user-auth
    kubectl label namespace asm-user-auth istio.io/rev=REVISION --overwrite
    

    您可以通过检查 kubectl get pod -n istio-system -L istio.io/rev 以找到 REVISION 标签值

  3. asm-user-auth 命名空间中安装 Istio 网关

    kubectl apply -n asm-user-auth -f DIR_PATH/samples/gateways/istio-ingressgateway
    

准备 OIDC 客户端配置

按照以下步骤设置 OIDC 客户端配置。本指南使用 Google 作为 IDP,但您可以使用任何支持 OIDC 身份验证的 IDP。

  1. 在 Google Cloud 控制台中,转到 API 和服务 > 凭据

    转到“凭据”页面

  2. 转到创建凭据,然后选择 OAuth 客户端 ID。根据需要设置 OAuth 同意屏幕选项,然后配置以下选项:

    • 应用类型设置为 Web 应用
    • 已获授权的重定向 URI 设置为 https://<your-oidc-redirect-host><your-oidc-redirect-path>。例如,对于 localhost,您可以将其设置为 https://localhost:8443/_gcp_asm_authenticate

    然后,点击保存

  3. 此外,请保存您的 OIDC 客户端配置以备后续使用。

    export OIDC_CLIENT_ID='<your-client-id>'
    export OIDC_CLIENT_SECRET='<your-client-secret>'
    export OIDC_ISSUER_URI='https://accounts.google.com'
    export OIDC_REDIRECT_HOST='https://localhost:8443'
    export OIDC_REDIRECT_PATH='/_gcp_asm_authenticate'
    

获取 kpt 软件包

按照以下步骤从公共代码库安装推荐的 authservice 配置。这些命令将检索最新的 authservice 容器,并在 asm-user-auth 命名空间中以 Pod 的形式启动该容器。它还会将入站流量配置为拦截所有请求。

kpt v1+

获取 kpt 软件包:

kpt pkg get https://github.com/GoogleCloudPlatform/asm-user-auth.git/@v1.1.0 .
cd asm-user-auth/

kpt v1-

获取 kpt 软件包:

kpt pkg get https://github.com/GoogleCloudPlatform/asm-user-auth.git/@v1.0.1 .
cd asm-user-auth/

为 Ingress 网关设置重定向网址和 Secret

OAuth2 需要托管在 HTTPS 保护端点上的重定向网址。这些命令用于示例目的,并通过为 Istio Ingress Gateway 生成自签名证书来简化设置。

  1. 生成自签名证书:

    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
     -days 365 -nodes -subj '/CN=localhost'
    
  2. 为 Ingress 网关创建 Secret 来托管 HTTPS 流量:

    kubectl create -n asm-user-auth secret tls userauth-tls-cert --key=key.pem \
    --cert=cert.pem
    

应用加密和签名密钥

authservice 需要两组密钥才能成功运行。第一个是用于加密和解密的对称密钥。在将会话状态设置为 cookie 之前,此密钥用于加密会话状态。

第二组密钥是公钥/私钥对。该密钥用于将 JWT 格式的经过身份验证的用户信息签名为 RCToken。此密钥对的公钥发布在一个预定义端点上,辅助信息文件可将该端点用于验证 JWT。

用户身份验证 kpt 软件包包含两个用于快速设置的示例密钥。但是,您可以使用偏好的密钥管理系统生成这些密钥。

  1. 请使用以下格式准备会话加密密钥,或者使用 pkg 中的示例(可通过 cat ./samples/cookie_encryption_key.json 查看)。

    {
      "keys":[
         {
            "kty":"oct",
            "kid":"key-0",
            "K":"YOUR_KEY",
            "useAfter": 1612813735
         }
      ]
    }
    

    您可以使用以下命令生成测试 AES 密钥:

    openssl enc -aes-256-cbc -k mycustomkey -P -md sha1 | grep key
    
  2. 请遵循以下格式准备 RCToken 签名密钥,或使用 pkg 中的示例(可通过 cat ./samples/rctoken_signing_key.json 查看)。

    {
      "keys":[
         {
            "kty":"RSA",
            "kid":"rsa-signing-key",
            "K":"YOUR_KEY",  # k contains a Base64 encoded PEM format RSA signing key.
            "useAfter": 1612813735  # unix timestamp
         }
      ]
    }
    

    您可以使用以下命令生成 256 位测试 RSA 私钥:

    openssl genpkey -algorithm RSA -out rsa_private.pem -pkeyopt rsa_keygen_bits:256
    
  3. 创建 kubernetes Secret,authservice 会将其装载到自己的文件系统中。

    kubectl create secret generic secret-key  \
        --from-file="session_cookie.key"="./samples/cookie_encryption_key.json" \
        --from-file="rctoken.key"="./samples/rctoken_signing_key.json"  \
        --namespace=asm-user-auth
    

部署用户身份验证服务

以下命令会在 asm-user-auth 命名空间中创建用户身份验证服务和部署。

kpt v1+

设置用户身份验证配置的必要值。客户端 ID 和 Secret 均存储为 Kubernetes Secret,因此我们使用 Base64 对其进行编码。请访问公共代码库,以查看所有可用的 setter。

kpt fn eval pkg --image gcr.io/kpt-fn/apply-setters:v0.2 --truncate-output=false -- \
  client-id="$(echo -n ${OIDC_CLIENT_ID} | base64 -w0)" \
  client-secret="$(echo -n ${OIDC_CLIENT_SECRET} | base64 -w0)" \
  issuer-uri="${OIDC_ISSUER_URI}" \
  redirect-host="${OIDC_REDIRECT_HOST}" \
  redirect-path="${OIDC_REDIRECT_PATH}"

kpt v1-

设置用户身份验证配置的必要值。客户端 ID 和 Secret 均存储为 Kubernetes Secret,因此我们使用 Base64 对其进行编码。请访问 Kptfile,以查看所有可用的 setter。

kpt cfg set pkg anthos.servicemesh.user-auth.oidc.clientID $(echo -n ${OIDC_CLIENT_ID} | base64 -w0)
kpt cfg set pkg anthos.servicemesh.user-auth.oidc.clientSecret $(echo -n ${OIDC_CLIENT_SECRET} | base64 -w0)
kpt cfg set pkg anthos.servicemesh.user-auth.oidc.issuerURI ${OIDC_ISSUER_URI}
kpt cfg set pkg anthos.servicemesh.user-auth.oidc.redirectURIHost ${OIDC_REDIRECT_HOST}
kpt cfg set pkg anthos.servicemesh.user-auth.oidc.redirectURIPath ${OIDC_REDIRECT_PATH}

应用 kpt 软件包:

# Remove the potential alpha version CRD if exists.
kubectl delete crd userauthconfigs.security.anthos.io
kubectl apply -f ./pkg/asm_user_auth_config_v1beta1.yaml
kubectl apply -f ./pkg

authservice 会使用 UserAuthConfig CRD 来提供最终用户身份验证。在运行时可配置 UserAuthConfig,您可以将其更新,以更改 authservice 行为并使用任意 OIDC 授权服务器的端点对其进行配置。

您可以按 cat pkg/user_auth_config.yaml 查看文件,其中包含以下字段:

kpt v1+

apiVersion: security.anthos.io/v1beta1
kind: UserAuthConfig
metadata:
  name: user-auth-config
  namespace: asm-user-auth
spec:
  authentication:
    oidc:
      certificateAuthorityData: ""  # kpt-set: ${ca-cert}
      issuerURI: "<your issuer uri>"  # kpt-set: ${issuer-uri}
      proxy: ""  # kpt-set: ${proxy}
      oauthCredentialsSecret:
        name: "oauth-secret"  # kpt-set: ${secret-name}
        namespace: "asm-user-auth"  # kpt-set: ${secret-namespace}
      redirectURIHost: ""  # kpt-set: ${redirect-host}
      redirectURIPath: "/_gcp_asm_authenticate"  # kpt-set: ${redirect-path}
      scopes: ""  # kpt-set: ${scopes}
      groupsClaim: ""  # kpt-set: ${groups}
  outputJWTAudience: "test_audience"  # kpt-set: ${jwt-audience}

kpt v1-

apiVersion: security.anthos.io/v1beta1
kind: UserAuthConfig
metadata:
  name: user-auth-config
  namespace: asm-user-auth
spec:
  authentication:
    oidc:
      certificateAuthorityData: ""  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.certificateAuthorityData"}
      oauthCredentialsSecret:
        name: "oauth-secret"  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.oauthCredentialsSecret.name"}
        namespace: "asm-user-auth"  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.oauthCredentialsSecret.namespace"}
      issuerURI: "<your issuer uri>"  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.issuerURI"}
      proxy: ""  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.proxy"}
      redirectURIHost: ""  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.redirectURIHost"}
      redirectURIPath: "/_gcp_asm_authenticate"  # {"$ref":"#/definitions/io.k8s.cli.setters.anthos.servicemesh.user-auth.oidc.redirectURIPath"}
  outputJWTAudience: "test_audience"

如需详细了解 user_auth_config.yaml 字段,请参阅用户身份验证配置详细信息

执行安装后任务

完成上述安装步骤后,您需要执行以下任务。

为您的应用启用用户身份验证

本部分以 httpbin 为例,演示如何启用用户身份验证。

Anthos Service Mesh 用户身份验证使用 CUSTOM 类型的授权政策来触发 OIDC 流程。

安装 Istio 网关后,将其配置为使用您刚才创建的 TLS 证书 userauth-tls-cert 传送 HTTPS 流量。以下是刚刚安装的 pkg/gateway.yaml 配置。

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: userauth
  namespace: asm-user-auth
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: userauth-tls-cert
---
# This ensures the OIDC endpoint has at least some route defined.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: userauth-oidc
  namespace: asm-user-auth
spec:
  gateways:
  - userauth
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: "your-oidc-redirect-path"
    name: user-auth-route
    route:
    - destination:
        host: authservice
        port:
          number: 10004
  1. default 命名空间添加标签,以启用 istio-proxy 自动注入功能。

    kubectl label namespace default istio.io/rev=REVISION --overwrite
    
  2. httpbin 部署到 default 命名空间。

    kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml -n default
    
  3. 更新 httpbin 以使用此网关来处理 HTTPS 流量,并使用端口转发在本地访问该应用:

    kubectl apply -f./samples/httpbin-route.yaml -n default
    kubectl port-forward service/istio-ingressgateway 8443:443 -n asm-user-auth
    

    端口 8443 上的 Ingress 网关将转发到 localhost,以便能够在本地访问应用。

验证用户身份验证

httpbin 提供两个路径,/ip 可公开访问,而 /headers 则要求最终用户通过其配置的 IDP 登录。

  1. 通过访问 https://localhost:8443/ip,验证您能否直接访问 /ip

  2. 通过访问 https://localhost:8443/headers 验证您是否能看到 OIDC 登录页面。

  3. 登录后,点击下一步,然后验证其能否将您重定向到 /headers 页面。

配置授权政策

完成上述步骤中的配置后,每个用户都将通过基于 Web 的身份验证流程进行重定向。该流程完成后,authservice 将以 JWT 格式生成一个 RCToken,用于传输经过身份验证的用户信息。

  1. 在 Ingress 中添加 Istio 授权政策,以确保对每个经过身份验证的用户执行授权检查:

    kubectl apply -f ./samples/httpbin-authz.yaml -n asm-user-auth
    
  2. httpbin-authz.yaml 文件会将 Ingress 网关配置为验证 authservice 发出的 RC 令牌,并且仅在 JWT 包含所需字段(例如目标对象和颁发者)时才授权。

    请参阅以下授权政策示例:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-rc-token
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      action: ALLOW
      rules:
      - to:
        - operation:
            paths: ["/ip"]
      - to:
        when:
        - key: request.auth.claims[iss]
          values:
          - authservice.asm-user-auth.svc.cluster.local
        - key: request.auth.claims[aud]
          values:
          - test_audience
        - key: request.auth.claims[sub]
          values:
          - allowed_user_sub_1  # Change this with the "sub" claim in the RC token. Wildcard '*' will match everything.
    

配置特定于环境的设置

上述步骤使用 localhost 和自签名 HTTPS 证书进行快速设置。对于实际生产用途,请使用您自己的网域,例如 example.com

此外,请确保 certificateAuthorityData 具备所需的根证书内容。例如,如果 IDP 受系统根证书信任,您可以将其留空。如果存在终止 HTTPS 连接的 HTTPS 代理,则应将其设置为该代理的根证书。

管理和轮替密钥

authservice 使用两组密钥。您可以单独轮替每个密钥。不过,在轮替密钥之前,请务必了解轮替的工作原理。

这两组密钥均采用 JSON 格式。useAfter 字段指定应使用密钥时的时间戳。在密钥轮替期间,您应该在 JSON 中同时包含新旧密钥。例如,在以下示例中,仅在时间戳 1712813735 后才使用 new-key

{
   "keys":[
      {
         "kty":"RSA",
         "kid":"old-key",
         "K":"...", # k contains a Base64 encoded PEM format RSA signing key.
         "useAfter": 1612813735, # unix timestamp
      }
      {
      "kty":"RSA",
         "kid":"new-key",
         "K":"...", # k contains a Base64 encoded PEM format RSA signing key.
         "useAfter": 1712813735, # unix timestamp
      }
   ]
}

Anthos Service Mesh 使用对称密钥加密存储在浏览器 Cookie 中的会话数据。为确保现有会话的有效性,authservice 会尝试使用密钥集中的所有密钥进行解密。轮替后,authservice 将使用新密钥来加密新会话,并将继续尝试使用旧密钥进行解密。

公钥/私钥对用于为 RCToken 签名。istiod 会将公钥传输到 Sidecar,以进行 JWT 验证。Sidecar 在 authservice 开始使用新私钥为 RCToken 签名之前先接收新公钥,这一点至关重要。为此,authservice 在添加密钥后会立即开始发布公钥,但要等待很长时间才开始使用该公钥为 RCToken 签名。

总而言之,在执行密钥轮替时,我们建议:

  1. 定期执行密钥轮替或按需执行。
  2. 在 JSON 格式中,添加当前密钥和新密钥。新密钥应与将来的时间戳相关联。我们建议您在当前时间至少提前几个小时指定时间戳。
  3. 监控并确认新密钥使用后服务仍然运行状况良好。在新密钥被使用后,请等待至少一天再继续下一步。
  4. 从 JSON 条目中移除旧密钥。您不再需要它们。

用户身份验证配置详细信息

下表介绍了 CRD 中的每个字段:

字段名称 说明
authentication.oidc 本部分包含 OIDC 端点配置和 OIDC 流程中使用的参数。
authentication.oidc.certificateAuthorityData 这是 OIDC 授权服务器或 HTTPS 代理(如果有)网域的 SSL 根证书。
authentication.oidc.oauthCredentialsSecret Kubernetes Opaque 类型 Secret 的 Secret 引用,其中包含 JSON 载荷中的 OAuth2 OIDC client_id 和 client_secret。
authentication.oidc.issuerURI 用作输出 RCToken 中的颁发者的 URI。
authentication.oidc.proxy 对于 OIDC IDP 的代理服务器(如果适用)。格式为 http://user:password@10.10.10.10:8888。
authentication.oidc.redirectURIHost 用于 OAuth 终止 URI 的主机。如果将此字段留空,则系统将使用目标网址中的主机,并动态组合重定向 URI。
如果需要在更高级层进行用户身份验证 SSO 会话,则可以使用此值。例如,如需启用 profile.example.com/ 和 admin.example.com/ 之间的单点登录,可将此值设置为 example.com。这将允许在 example.com 上建立一个将在所有子网域之间共享的用户身份验证会话。注意:如果多个网域由同一网格 example1.com 和 example2.com 提供服务,则无法使用该功能,建议您将其留空。
authentication.oidc.redirectURIPath authservice 将在其中终止 OAuth 流程的端点路径。您应该将此 URI 路径以及主机注册为 authentication.oidc.clientID 的授权服务器中的授权重定向 URI。
此外,应从启用了 authservice 的同一服务网格和 Ingress 提供此 URI。
authentication.oidc.scopes 应在身份验证请求中请求的 OAuth 范围。 以英文逗号分隔的标识符列表,用于指定除了“openid”范围之外请求哪些访问特权,例如 “groups,allatclaim”。
authentication.oidc.groupsClaim 如果 idtoken 包含群组声明,请使用此字段来指示其名称。如果指定此字段,则服务会将此声明中的数据传入到输出 groups 中的“群组”声明。此声明应包含以英文逗号分隔的字符串列表,例如:["group1", "group2"]。
authentication.outputJWTAudience authservice 生成的 RCToken 的目标对象。Sidecar 可以根据此受众群体值验证传入的 RCToken。

多集群部署

Anthos Service Mesh 用户身份验证支持多集群部署。如上所述,您需要在每个集群中部署用户身份验证。用户身份验证配置(例如 UserAuth 自定义资源、OIDC 客户端密钥、加密密钥)需要在每个集群中复制。

默认情况下,入站流量网关会将身份验证请求负载均衡到任何 authservice 实例。您可以使用目标规则配置入站网关,以将请求发送到 authservice 并且仅故障切换到其他集群 authservice

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: authservice-fail-over
  namespace: asm-user-auth
spec:
  host: authservice.asm-user-auth.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      localityLbSetting:
        enabled: true
        failover:
        - from:  us-east
          to: us-west
        - from: us-west
          to: us-east

与其他配置相同,这需要在每个集群中配置。

问题排查

  1. IDP 的网络可访问性。

    可能的日志:error: TLS handshake failed.

    通过执行从 istio-proxy 容器到 IDP 颁发者 URI 的 curl 实现验证。如果无法连接,用户可检查集群的防火墙规则或其他网络配置。

  2. 根 CA 证书。

    可能的日志:error: The server's TLS certificate did not match expectations.error: TLS handshake failed.

    确保 certificateAuthorityData 保留正确的根 CA 证书。在没有 HTTPS 代理终止 HTTPS 流量时,它会保留该 IDP 的根 CA 证书。如果存在代理,则请改为保留代理的相应证书。

  3. 重定向路径配置。

    可能的观察结果:在 OIDC 身份验证流程中接收到 404 错误页面。

    用户身份验证会返回“Set-Cookie”标头,但不使用路径特性;默认情况下,浏览器使用请求网址的目录作为 Cookie 路径(与路径相关的 Cookie 的范围)。因此,我们建议您不要在重定向路径中包含“/”(除非是故意而为)。

常见问题解答

  1. 如何升级启用了用户身份验证的 Anthos Service Mesh?

    遵循 Anthos Service Mesh 升级过程操作,并在命令行中将 --custom_overlay user-auth-overlay.yaml 添加到 asmcli install 并指定叠加文件。

  2. 我们应该为 authservice 预配多少资源?它每秒能处理多少个请求?

    默认情况下,authservice 配置了 2.0 vCPU 和 256Mi 内存。在此配置下,authservice 每秒可处理 500 个请求。如需处理更多请求,您应该预配更多 CPU,大致与其请求处理能力成正比。您还可以配置多个 authservice 副本,以提高横向可伸缩性。