将 IAP 与 Cloud Service Mesh 集成

本指南介绍了如何集成 搭配使用 Identity-Aware Proxy (IAP) 与 Cloud Service Mesh。借助 IAP 与 Cloud Service Mesh 的集成,您可以基于 Google 的 BeyondCorp 原则安全地访问服务。IAP 会验证用户身份和请求的上下文,以确定是否应允许用户访问应用或资源。IAP 与 Cloud Service Mesh 的集成 可为您带来以下好处:

  • 对在 Cloud Service Mesh 上运行的工作负载完成情境感知访问权限控制。您可以根据发出请求的特性(例如用户身份、IP 地址和设备类型)设置精细的访问权限政策。您可以根据请求网址的主机名和路径来结合您的访问权限政策与限制。

  • 在 Cloud Service Mesh 授权中启用对情境感知声明的支持。

  • 通过 Google Cloud 负载均衡器对应用的可扩缩、安全和可用性高的访问权限。高性能负载均衡提供分布式拒绝服务攻击 (DDoS) 的内置保护,并且支持全局任播 IP 寻址

前提条件

按照安装依赖工具并验证集群中的步骤操作:

此外,本指南假定您拥有:

使用 Anthos Service Mesh 设置集群

本部分介绍了如何设置 IAP 集成 (包括新安装的 Cloud Service Mesh 和升级)。

新安装

  1. 启用 iap.googleapis.com。 在以下命令中,将 PROJECT_ID 替换为您将安装的项目 Cloud Service Mesh 位于:

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. 要更新的集群必须设置 --addons=HttpLoadBalancing 选项。HttpLoadBalancing 插件为该集群启用 HTTP (L7) 负载平衡控制器。运行以下命令 使用 Cloud Service Mesh 所需的选项更新集群。除非您已设置默认可用区或区域,否则需要在命令中提供区域 (--region=REGION) 或可用区 (--zone=ZONE)。

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID \
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. 默认情况下,iap-operator.yaml 文件将端口 31223 设置为状态端口,并将端口 31224 设置为 http 端口。 如果在您的集群中端口 31223 已被使用,请运行以下命令设置另一个状态端口:

    kpt cfg set asm gcloud.container.cluster.ingress.statusPort STATUS_PORT
    

    如果在您的集群中端口 31224 已被使用,请运行以下命令设置另一个 http 端口:

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort HTTP_PORT
    
  4. 按照 安装默认功能和 Mesh CA 使用 Google 提供的脚本安装 Cloud Service Mesh。运行脚本时,请添加以下选项:

    --option iap-operator
    

    例如:

    ./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 \
      --option iap-operator
    

    安装 Cloud Service Mesh 时,iap-operator.yaml 文件会将 istio-ingressgateway 服务上的 type 字段设置为 NodePort,从而将网关配置为打开服务网格上的特定端口。这样,您就可以设置负载均衡器,以将发送到您的域名的流量路由到此端口。

  5. 如果您要安装托管式 Cloud Service Mesh,还需要完成以下步骤:

    1. 将修订版本标签添加istio-system 命名空间。

    2. 下载 IAP 的 Istio Ingress 网关服务规范并将其命名为 iap_operator.yaml

    3. 将 Ingress 安装为 NodePort 服务。如需了解详情,请参阅从 IstioOperator 迁移

      asmcli experimental mcp-migrate-check -f iap_operator.yaml
      
      istioctl install -f /asm-generated-configs/gateways-istiooperator/"GATEWAY_NAME".yaml
      

安装 Cloud Service Mesh 后,请返回本指南,继续完成 设置与 IAP 的集成。

升级

本部分介绍以下升级使用场景:

  • 您已经设置了 IAP 集成,并且正在升级 Cloud Service Mesh。在这种情况下,您已经在项目上启用 iap.googleapis.com 并在集群上启用 HttpLoadBalancing 插件。请跳至第 3 步,下载 asm 软件包并升级 Cloud Service Mesh。

  • 您正在升级 Cloud Service Mesh,并希望设置 IAP 收入增幅。在这种情况下,您需要完成以下所有步骤,升级 Cloud Service Mesh,并在升级后返回到本指南以完成集成。

  1. 启用 iap.googleapis.com。 在以下命令中,将 PROJECT_ID 替换为您将安装的项目 Cloud Service Mesh

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. 要更新的集群必须设置 --addons=HttpLoadBalancing 选项。HttpLoadBalancing 插件为该集群启用 HTTP (L7) 负载平衡控制器。运行以下命令,使用 Cloud Service Mesh 所需的选项更新集群。除非您已设置默认可用区或区域,否则需要在命令中提供区域 (--region=REGION) 或可用区 (--zone=ZONE)。

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. 如果要更新正常运行的现有 HTTP Cloud Load Balancer,请运行以下命令来保留现有的 HTTP 和状态端口:

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
    
    kpt cfg set asm gcloud.container.cluster.ingress.statusPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')
    
  4. 按照 升级 Cloud Service Mesh ,使用 Google 提供的脚本升级 Cloud Service Mesh。

    升级 Cloud Service Mesh 时,iap-operator.yaml 文件会设置 将 istio-ingressgateway 服务上的 type 字段设置为 NodePort, 将网关配置为在服务网格上打开特定端口。这样,您就可以设置负载均衡器,以将发送到您的域名的流量路由到此端口。

    默认情况下,iap-operator.yaml 文件将端口 31223 设置为状态端口,并将端口 31224 设置为 http 端口。

    运行脚本时,请添加以下选项:

    --option iap-operator
    

    例如:

    ./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 \
      --option iap-operator
    
  5. 通过在工作负载上触发自动边车代理注入来完成升级。如需了解详情,请参阅部署和重新部署工作负载

    完成升级后,返回到本指南并继续执行下一部分以设置与 IAP 的集成。

预留静态 IP 地址并配置 DNS

如需将 Identity-Aware Proxy 与 Cloud Service Mesh 集成,您必须设置 Google Cloud HTTP(S) 负载均衡器,需要一个指向 静态 IP 地址您可以预留一个静态外部 IP 地址,这样会将该地址无限期地分配给您的项目,直到您明确释放该地址。

  1. 预留静态外部 IP 地址:

    gcloud compute addresses create example-static-ip --global
    
  2. 获取静态 IP 地址:

    gcloud compute addresses describe example-static-ip --global
    
  3. 在您的域名注册商中,使用静态 IP 地址配置完全限定域名 (FQDN)。通常情况下,将 A 记录添加到 DNS 设置。为 FQDN 添加 A 记录的配置步骤和术语会因您的域名注册商而异。

    DNS 设置可能需要 24 到 48 小时才能生效。您可以继续设置本指南中的所有内容,但在 DNS 设置生效之前无法测试设置。

部署示例应用

启用 IAP 之前,您需要在 GKE 集群上运行的应用,以便验证所有请求是否都具有身份。本指南使用 Bookinfo 示例来演示如何设置 HTTP(S) 负载均衡器并启用 IAP。

按照相应步骤部署 Bookinfo。在您部署负载均衡器之前,您将无法在 GKE 集群之外(例如通过浏览器)访问 Bookinfo 应用。

外部请求

Bookinfo 的网关资源(在 samples/bookinfo/networking/bookinfo-gateway.yaml 中定义)使用预配置的 istio-ingressgateway。如前文所述,当您部署 Cloud Service Mesh 时,您为 istio-ingressgateway 指定了 NodePort,这会在服务网格上打开特定端口。虽然集群中的节点具有外部 IP 地址,但来自集群外部的请求会被 Google Cloud 防火墙规则阻止。启用 IAP 后,向公共互联网公开应用的正确方法是使用负载均衡器。请勿使用会绕过 IAP 的防火墙规则公开节点地址。

如需将请求路由到 Bookinfo,请在您的 Google Cloud 项目中设置 HTTP(S) 负载均衡器。由于负载均衡器在您的项目中,因此它位于防火墙内,可以访问集群中的节点。使用静态 IP 地址和您的域名配置负载均衡器后,您可以向域名发送请求,负载均衡器会将请求转发到集群中的节点。

启用 IAP

以下步骤介绍了如何启用 IAP。

  1. 使用客户端 ID(上一步中的 CLIENT_ID)和 CLIENT_SECRET 启用 Cloud Service Mesh。使用 OAuth 客户端中的材料创建 kubernetes Secret:

    kubectl create secret generic -n istio-system my-secret --from-literal=client_id=CLIENT_ID \
    --from-literal=client_secret=CLIENT_SECRET

部署负载均衡器

您可以使用 Ingress 资源创建使用自动配置的 SSL 证书的 HTTP(S) 负载均衡器。系统会为您的网域预配、续订和管理代管式 SSL 证书

  1. 创建 ManagedCertificate 资源。此资源会指定 SSL 证书的网域。spec.domains 列表只能包含一个网域。不支持通配符网域。 在以下 YAML 中,将 DOMAIN_NAME 替换为您为外部静态 IP 地址配置的域名。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.gke.io/v1
    kind: ManagedCertificate
    metadata:
      name: example-certificate
      namespace: istio-system
    spec:
      domains:
        - DOMAIN_NAME
    EOF
  2. 创建 BackendConfig 资源。 此资源指示 GCLB 如何对入站流量网关执行健康检查,以及如何配置 Identity-Aware Proxy。首先,从入站流量网关收集一些有关健康检查的值:

    • 健康检查入站流量端口:这是 istio-ingress 的健康检查端口。

      export HC_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')

    • 健康检查入站流量路径:这是 istio-ingress 的健康检查路径。

      export HC_INGRESS_PATH=$(kubectl get pods -n istio-system -l app=istio-ingressgateway -o jsonpath='{.items[0].spec.containers[?(@.name=="istio-proxy")].readinessProbe.httpGet.path}')

    cat <<EOF | kubectl apply -n istio-system -f -
    apiVersion: cloud.google.com/v1
    kind: BackendConfig
    metadata:
      name: http-hc-config
    spec:
      healthCheck:
        checkIntervalSec: 2
        timeoutSec: 1
        healthyThreshold: 1
        unhealthyThreshold: 10
        port: ${HC_INGRESS_PORT}
        type: HTTP
        requestPath: ${HC_INGRESS_PATH}
      iap:
        enabled: true
        oauthclientCredentials:
          secretName: my-secret
    EOF
  3. 使用 BackendConfig 为入站流量服务添加注释。

        kubectl annotate -n istio-system service/istio-ingressgateway --overwrite \
          cloud.google.com/backend-config='{"default": "http-hc-config"}' \
          cloud.google.com/neg='{"ingress":false}'
    
  4. 通过定义 Ingress 资源来创建负载均衡器。

    • networking.gke.io/managed-certificates 注释设置为您在上一步中创建的证书的名称 example-certificate

    • kubernetes.io/ingress.global-static-ip-name 注释设置为您预留的静态 IP 地址的名称 example-static-ip

    • serviceName 设置为 istio-ingressgateway,后者会在 Bookinfo 示例的 Gateway 资源中使用。

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
      namespace: istio-system
      annotations:
        kubernetes.io/ingress.global-static-ip-name: example-static-ip
        networking.gke.io/managed-certificates: example-certificate
    spec:
      defaultBackend:
        service:
          name: istio-ingressgateway
          port:
            number: 80
    EOF
  5. 在 Google Cloud 控制台中,前往 Kubernetes Engine > Service 和 Ingress 页面。

    前往“Service 和 Ingress”页面

    您应该会在状态列中看到“正在创建 Ingress”(Creating ingress) 消息。等待 GKE 完全预配 Ingress,然后再继续。每隔几分钟刷新一次页面以获取 Ingress 的最新状态。预配 Ingress 后,您可能会看到“正常”状态,或者错误“所有后端服务均处于运行状况并不佳的状态”。GKE 预配的其中一项资源是默认健康检查。如果您看到错误消息,这表示已预配 Ingress,并且已运行默认健康检查。当您看到“正常”状态或错误时,请继续下一部分,为负载均衡器配置健康检查。

配置 IAP 访问权限列表

向 IAP 的访问权限政策添加用户:

gcloud beta iap web add-iam-policy-binding \
    --member=user:EMAIL_ADDRESS \
    --role=roles/iap.httpsResourceAccessor

其中 EMAIL_ADDRESS 是用户的完整电子邮件地址,例如 alice@example.com

  1. 测试负载均衡器。将浏览器指向:

    http://DOMAIN_NAME/productpage

    其中,DOMAIN_NAME 是您使用外部静态 IP 地址配置的域名。

    您应该会看到 Bookinfo 应用的 productpage。如果您多次刷新该页面,您应该会看到不同版本的评论,以轮询方式呈现:红色星标、黑色星标、无星标。

    您还应测试对 Bookinfo 的 https 访问权限。

在服务网格上启用 RCToken 支持

默认情况下,IAP 会生成一个 JSON Web 令牌 (JWT),其作用范围仅限于 OAuth 客户端。对于 Cloud Service Mesh,您可以配置 IAP 以生成 RequestContextToken (RCToken),这是一个 JWT,但具有可配置的受众群体。 通过 RCToken,您可以将 JWT 的受众群体配置为任意字符串,该字符串可在 Cloud Service Mesh 政策中使用以实现精细授权。

如需配置 RCToken,请执行以下操作:

  1. 为 RCToken 受众群体创建环境变量。这可以是您所需的任何字符串。

    export RCTOKEN_AUD="your-rctoken-aud"
    
  2. 可选:以下步骤需要 BACKEND_SERVICE_ID。如果您需要查找 BACKEND_SERVICE_ID,请运行以下命令:

    kubectl -n istio-system get Ingress example-ingress -o json | jq \
     '.metadata.annotations."ingress.kubernetes.io/backends"'
    

    预期输出类似于 "{\"BACKEND_SERVICE_ID\":\"HEALTHY\"}"。 例如: "ingress.kubernetes.io/backends": "{\"k8s-be-31224--51f3b55cd1457fb6\":\"HEALTHY\"}"。 此示例中的 BACKEND_SERVICE_IDk8s-be-31224--51f3b55cd1457fb6

  3. 提取现有 IAP 设置

    gcloud iap settings get --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID > iapSettings.json
    
  4. 使用 RCToken 受众群体更新 IapSettings

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR $RCTOKEN_AUD \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > updatedIapSettings.json
    
    gcloud iap settings set updatedIapSettings.json --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID
    
  5. 在 Istio 入站流量网关上启用 RCToken 身份验证。

    cat <<EOF | kubectl apply -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "RequestAuthentication"
    metadata:
      name: "ingressgateway-jwt-policy"
      namespace: "istio-system"
    spec:
      selector:
        matchLabels:
          app: istio-ingressgateway
      jwtRules:
      - issuer: "https://cloud.google.com/iap"
        jwksUri: "https://www.gstatic.com/iap/verify/public_key-jwk"
        audiences:
        - $RCTOKEN_AUD
        fromHeaders:
        - name: ingress-authorization
          prefix: "Istio "
        outputPayloadToHeader: "verified-jwt"
        forwardOriginalToken: true
    EOF
    
  6. 可选:确保没有有效 JWT 的请求被拒绝:

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: iap-gateway-require-jwt
        namespace: istio-system
      spec:
        selector:
          matchLabels:
            app: istio-iap-ingressgateway
        action: DENY
        rules:
          - from:
              - source:
                  notRequestPrincipals: ["*"]
      EOF
      

  7. 确保向 Bookinfo productpage 发出的请求仍成功:

    http://DOMAIN_NAME/productpage

如需测试政策,请执行以下操作:

  1. 创建 IapSettings 请求对象,但将 rctokenAud 设置为不同的字符串:

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR wrong-rctoken-aud \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > wrongIapSettings.json
    
  2. 调用 IapSettings API 来设置 RCtoken 受众群体。

    gcloud beta iap settings set wrongIapSettings.json --project=PROJECT_ID --resource-type=compute --service=BACKEND_SERVICE
  3. 向 Bookinfo productpage 发出请求,请求应会失败:

    http://DOMAIN_NAME/productpage

清理

完成本教程后,移除以下资源以防止您的账号产生不必要的费用:

  1. 删除代管式证书:

    kubectl delete managedcertificates example-certificate
  2. 删除 Ingress 以释放负载均衡资源:

    kubectl -n istio-system delete ingress example-ingress

  3. 删除静态 IP 地址:

    gcloud compute addresses delete example-static-ip --global

    如果您这样做,请务必从您的域名注册商中删除 IP 地址。

  4. 删除集群,以删除组成集群的资源,例如计算实例、磁盘和网络资源:

    gcloud container clusters delete CLUSTER_NAME