为 Cloud Service Mesh 自动管理 TLS 证书 使用 Certificate Authority Service 的入站流量网关

本教程介绍平台运营商如何使用 cert-manager 工具的 Certificate Authority Service (CA Service) 颁发者自动管理 Cloud Service Mesh 入站流量网关的 TLS 证书。通过这些证书,入站流量网关可终止源自 Virtual Private Cloud (VPC) 中但服务网格之外的客户端的 HTTPS 以及其他 TLS 和 mTLS 流量。本教程假定您对 Kubernetes 和 TLS 证书有基本的了解。

简介

Cloud Service Mesh 会为服务网格中的每个工作负载预配 TLS 证书。这些证书会在服务网格中的工作负载之间实现加密和双向身份验证的 TLS (mTLS) 通信。其中一个受支持的 CA 会颁发证书并为证书签名。

但是,对于进入服务网格的流量,Cloud Service Mesh 不会自动向入站流量网关预配证书。常见的解决方案是使用开源 cert-manager 用于自动管理入站流量网关证书的工具。

cert-manager 工具会向代表证书授权机构 (CA)颁发者请求证书。CA Service 是一项 Google Cloud 服务,可让您创建自己的专用 CA。cert-manager 工具可以使用 CA Service 的开源外部颁发者向 CA Service 请求证书。

私有 CA 可以颁发 TLS 证书,对内部网络的流量进行身份验证和加密。Cloud Service Mesh 入站流量网关通常设置为允许来自 VPC 内部但位于服务网格外部的客户端的传入流量。对于内部网络流量,您可以使用 CA Service 中的私有 CA 为入站流量网关颁发证书。

本教程介绍如何设置 cert-manager 工具和 CA Service 颁发者,以自动为入站流量网关预配和续订 TLS 证书。cert-manager 工具会将证书预配为 TLS 类型的 Kubernetes Secret 资源。当 cert-manager 工具续订证书时,它会使用新证书更新 Secret 资源。入站流量网关运行 Envoy 代理,并且支持 Envoy 的 Secret 发现服务 (SDS)。SDS 允许入站流量网关开始使用新证书,而无需管理员重启或重新加载该进程。

属于网格的边车代理可以从 CA Service 或 Cloud Service Mesh 证书授权机构获取 TLS 证书。在本教程中,您要将 CA Service 用于边车代理和入站流量网关证书。这样,您就可以对所有 TLS 证书使用一个根 CA。

下图显示了您在本教程中预配的资源。您可以为入站流量网关预配内部直通网络负载均衡器。内部直通网络负载均衡器不是代理,因此不会终止 TCP 连接或执行 TLS 握手,而是会将连接路由到 istio-ingressgateway 部署的 pod。

hello-example-com-credential Secret 包含证书和私钥。hello 网关会对 istio-ingressgateway 部署的 pod 进行配置,使其使用此证书和私钥对具有主机名 hello.example.com 的请求执行 TLS 握手。

使用 CA 服务的 mtls 管理

cert-manager 命名空间中 google-cas-issuer 部署的 pod 向您在 CA Service 中创建的 CA 请求证书。您可以创建 Identity and Access Management 政策绑定,以允许 ca-service-isser pod 使用 适用于 GKE 的工作负载身份联合 模拟 Google 服务账号。您可以通过针对 CA 池创建 IAM 政策绑定,为此 Google 服务账号授予向 CA Service 中的 CA 请求证书的权限。

目标

费用

本教程使用 Google Cloud 的以下收费组件:

如需根据您的预计使用量来估算费用,请使用价格计算器。Google Cloud 新用户可能有资格申请免费试用。

完成本教程后,您可以通过删除您创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 在 Google Cloud 控制台中,转到项目选择器页面,然后选择或创建一个项目。

  2. 确保您的 Google Cloud 项目已启用结算功能

  3. 在 Google Cloud 控制台中,转到 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台底部打开,并显示命令行提示符。您可以使用 Cloud Shell 运行本教程中的所有命令。

  4. 设置您要在本教程中使用的 Google Cloud 控制台项目:

    gcloud config set core/project PROJECT_ID
    

    PROJECT_ID 替换为您的 Cloud 项目 ID。

    在“为 Cloud Shell 提供授权”对话框中,点击授权。点击授权,即表示您允许在 Cloud Shell 中运行的 gcloud 命令使用您的用户凭据来向 Google API 进行身份验证。

  5. 启用 Resource Manager、GKE、GKE Hub、Cloud Service Mesh 证书授权机构和 CA Service API:

    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        container.googleapis.com \
        gkehub.googleapis.com \
        meshca.googleapis.com \
        privateca.googleapis.com
    

配置 CA Service

在本部分中,您将在 CA Service 中创建一个根 CA 和两个从属 CA。一个从属 CA 用于向入站流量网关颁发证书,另一个从属 CA 用于向网格中的边车代理颁发证书。

为简单起见,在本教程中,您为 GKE 集群以及根 CA 和从属 CA 使用同一个项目。在您自己的环境中,您可以为 GKE 集群和 CA 使用其他项目。

  1. 在 Cloud Shell 中,创建用于根 CA 的 CA 池:

    gcloud privateca pools create ROOT_CA_POOL \
        --location CA_LOCATION \
        --tier enterprise
    
    • ROOT_CA_POOL 是 CA 池的名称。例如 root-ca-pool-tutorial
    • CA_LOCATION 是 CA 池的位置。例如 us-central1

    您可以使用以下命令列出可用的 CA Service 位置:gcloud privateca locations list

  2. 创建和启用根 CA:

    gcloud privateca roots create ROOT_CA \
        --auto-enable \
        --key-algorithm ec-p384-sha384 \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --subject "CN=Example Root CA, O=Example Organization" \
        --use-preset-profile root_unconstrained
    
    • ROOT_CA 是您要用于根 CA 的名称。例如 root-ca-tutorial
  3. 创建 CA 池,以用于向入站流量网关颁发证书的从属 CA:

    gcloud privateca pools create SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --tier devops
    
    • SUBORDINATE_CA_POOL_GATEWAYS 是 CA 池的名称。例如 subordinate-ca-mtls-pool-gateways-tutorial
  4. 创建和启用向入站流量网关颁发证书的从属 CA:

    gcloud privateca subordinates create SUBORDINATE_CA_GATEWAYS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --subject "CN=Example Gateway mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS 是您要用于从属 CA 的名称。例如 subordinate-ca-mtls-gateways-tutorial
    • --use-preset-profile 标志将从属 CA 配置为使用从属 mTLS 证书配置文件。此配置文件使从属 CA 能够为 mTLS 颁发客户端和服务器 TLS 证书。

    如果您希望入站流量网关使用简单 TLS 而不是 mTLS,则从属 CA 只需要颁发服务器 TLS 证书。在这种情况下,您可以改用从属服务器 TLS (subordinate_server_tls_pathlen_0) 证书配置文件。

  5. 创建证书颁发政策

    cat << EOF > policy.yaml
    baselineValues:
      keyUsage:
        baseKeyUsage:
          digitalSignature: true
          keyEncipherment: true
        extendedKeyUsage:
          serverAuth: true
          clientAuth: true
      caOptions:
        isCa: false
    identityConstraints:
      allowSubjectPassthrough: false
      allowSubjectAltNamesPassthrough: true
      celExpression:
        expression: subject_alt_names.all(san, san.type == URI && san.value.startsWith("spiffe://PROJECT_ID.svc.id.goog/ns/") )
    EOF
    

    此颁发政策将 CA 限制为只能为网格中的工作负载颁发证书。

  6. 创建 CA 池,以用于向网格中的边车代理颁发证书的从属 CA。将发布政策应用于 CA 池:

    gcloud privateca pools create SUBORDINATE_CA_POOL_SIDECARS \
     --issuance-policy policy.yaml \
     --location CA_LOCATION \
     --tier devops
    
    • SUBORDINATE_CA_POOL_SIDECARS 是 CA 池的名称。例如 subordinate-ca-mtls-pool-sidecars-tutorial
  7. 创建和启用向网格中的边车代理颁发证书的从属 CA:

    gcloud privateca subordinates create SUBORDINATE_CA_SIDECARS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --subject "CN=Example Sidecar mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS 是您要用于从属 CA 的名称。例如 subordinate-ca-mtls-sidecars-tutorial

创建一个 Google Kubernetes Engine 集群

  1. 在 Cloud Shell 中创建一个 GKE 集群:

    gcloud container clusters create CLUSTER_NAME \
        --enable-ip-alias \
        --num-nodes 4 \
        --release-channel regular \
        --scopes cloud-platform \
        --workload-pool PROJECT_ID.svc.id.goog \
        --zone ZONE
    

    CLUSTER_NAME 替换为您要用于集群的名称。例如 asm-ingress-cert-manager-ca-service

    ZONE 替换为您要用于集群的可用区。例如 us-central1-f

    对于该命令,请注意以下事项:

    • --release-channel 标志用于选择集群的 GKE 发布渠道
    • Cloud Service Mesh 和 cert-manager 工具要求您设置 cloud-platform 集群节点
    • --workload-pool 参数用于启用适用于 GKE 的工作负载身份联合, 允许 CA Service Issuer Kubernetes 服务账号 模拟 Google 服务账号。此模拟意味着 CA Service 颁发者 pod 无需下载 Google 服务账号的密钥文件即可访问 CA Service API。
  2. 授予集群管理员权限 您的用户账号:

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value core/account)
    

    您需要 Kubernetes cluster-admin 提供的权限 用于创建基于角色的访问权限控制 (RBAC) 的 ClusterRole 规则,以及安装 cert-manager 工具。

安装 Anthos Service Mesh 控制平面

在本教程中,您将为 Google Cloud 上的 GKE 集群安装托管式 Cloud Service Mesh,并将所有资源都保存在一个项目中。在您自己的环境中,您可以应用 使用代管式 Cloud Service Mesh 或 集群内控制平面

Cloud Service Mesh 提供了一系列适用于不同场景的安装选项。 完成本教程后,我们建议您查看安装指南,以选择最适合您的环境的选项。

  1. 在 Cloud Shell 中,下载 asmcli 安装工具:

    curl --location --output asmcli https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.18
    
    chmod +x asmcli
    

    您将使用 asmcli 安装 Cloud Service Mesh 控制平面。

  2. 安装 Cloud Service Mesh 控制平面:

    ./asmcli install \
        --ca gcp_cas \
        --ca_pool projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_SIDECARS \
        --cluster_location ZONE \
        --cluster_name CLUSTER_NAME \
        --enable_all \
        --enable_registration \
        --fleet_id PROJECT_ID \
        --managed \
        --output_dir asm-files \
        --project_id PROJECT_ID \
        --verbose
    

    安装需要几分钟时间。安装完成后,您会看到以下输出:

    asmcli: Successfully installed ASM.
    

安装入站流量网关

  1. 在 Cloud Shell 中,为入站流量网关创建 Kubernetes 命名空间:

    kubectl create namespace GATEWAY_NAMESPACE
    
    • GATEWAY_NAMESPACE 是您要用于入站流量网关的命名空间的名称。例如 istio-ingress
  2. 保留要用于入站流量网关内部直通网络负载均衡器的静态内部 IP 地址

    LOAD_BALANCER_IP=$(gcloud compute addresses create \
        asm-ingress-gateway-ilb \
        --region REGION \
        --subnet default \
        --format 'value(address)')
    
    • REGION 替换为包含 GKE 集群节点使用的一个或多个可用区的地区。例如,如果您的集群使用 us-central1-f 可用区,请将 REGION 替换为 us-central1

    此命令会保留您指定的地区内默认子网中的 IP 地址。

  3. 为入站流量网关创建 Operator 清单

    cat << EOF > ingressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: ingressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: asm-managed
      components:
        ingressGateways:
        - name: istio-ingressgateway
          namespace: GATEWAY_NAMESPACE
          enabled: true
          k8s:
            overlays:
            - apiVersion: apps/v1
              kind: Deployment
              name: istio-ingressgateway
              patches:
              - path: spec.template.metadata.annotations
                value:
                  inject.istio.io/templates: gateway
              - path: spec.template.metadata.labels.sidecar\.istio\.io/inject
                value: "true"
              - path: spec.template.spec.containers[name:istio-proxy]
                value:
                  name: istio-proxy
                  image: auto
            service:
              loadBalancerIP: $LOAD_BALANCER_IP
            serviceAnnotations:
              networking.gke.io/load-balancer-type: Internal
              networking.gke.io/internal-load-balancer-allow-global-access: "true"
    EOF
    

    对于 Operator 清单,请注意以下事项:

  4. 使用运营商清单创建入站流量网关安装清单 和 istioctl 安装控制平面时,asmcli 脚本下载的工具:

    ./asm-files/istioctl manifest generate \
        --filename ingressgateway-operator.yaml \
        --output ingressgateway
    
  5. 安装入站流量网关

    kubectl apply --recursive --filename ingressgateway/
    

安装 cert-manager 工具

  1. 在 Cloud Shell 中,下载并应用 cert-manager 工具安装清单:

    CERT_MANAGER_VERSION=v1.5.4
    
    curl --location --output cert-manager.yaml "https://github.com/jetstack/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"
    
    kubectl apply --filename cert-manager.yaml
    

    安装 cert-manager 工具大约需要一分钟的时间。

安装 CA Service 颁发者控制器

CA Service 颁发者控制器支持 cert-manager 工具使用 CA Service 请求证书。控制器使用 cert-manager 工具外部颁发者扩展程序机制。

  1. 在 Cloud Shell 中,创建一个 Google 服务账号

    gcloud iam service-accounts create CAS_ISSUER_GSA \
        --display-name "CA Service issuer for cert-manager"
    
    • CAS_ISSUER_GSA 是 Google 服务账号的名称。例如 cert-manager-ca-service-issuer

    Certificate Authority Service 颁发者控制器使用此 Google 服务账号向 Certificate Authority Service API 进行身份验证。

  2. 创建 Identity and Access Management 政策绑定,以允许 Certificate Authority Service 颁发者控制器 Google 服务账号从包含从属 CA 的 CA 池请求证书:

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  3. 下载 Certificate Authority Service 颁发者控制器安装清单:

    CAS_ISSUER_VERSION=v0.5.3
    
    curl --location --output ca-service-issuer.yaml "https://github.com/jetstack/google-cas-issuer/releases/download/${CAS_ISSUER_VERSION}/google-cas-issuer-${CAS_ISSUER_VERSION}.yaml"
    
  4. 创建 IAM 策略绑定以允许 cert-manager 中有 ksa-google-cas-issuer 个 Kubernetes 服务账号 模拟 Google 服务账号 (GSA), 适用于 GKE 的 Workload Identity Federation

    gcloud iam service-accounts add-iam-policy-binding \
     CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    

    CA Service 颁发者控制器 pod 使用的是 ksa-google-cas-issuer Kubernetes 服务账号。

  5. 在 GKE 集群中安装 CA Service 颁发者控制器:

    kubectl apply --filename ca-service-issuer.yaml
    
  6. 将 Workload Identity Federation for GKE 注解 iam.gke.io/gcp-service-account 添加到 CA Service 颁发者使用的 Kubernetes 服务账号 控制器 Pod:

    kubectl annotate serviceaccount ksa-google-cas-issuer --namespace cert-manager \
       "iam.gke.io/gcp-service-account=CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com"
    

    此注释告知 GKE Kubernetes 服务账号可以模拟 Google 服务账号来访问 Google API。

创建证书颁发者

  1. 在 Cloud Shell 中,创建并应用 GoogleCASIssuer manifest:

    cat << EOF > gateway-cas-issuer.yaml
    apiVersion: cas-issuer.jetstack.io/v1beta1
    kind: GoogleCASIssuer
    metadata:
      name: gateway-cas-issuer
      namespace: GATEWAY_NAMESPACE
    spec:
      caPoolId: SUBORDINATE_CA_POOL_GATEWAYS
      location: CA_LOCATION
      project: PROJECT_ID
    EOF
    
    kubectl apply --filename gateway-cas-issuer.yaml
    

    颁发者支持 cert-manager 工具从入站流量网关命名空间中的从属 CA 池预配证书

部署一个示例应用

在本部分中,您将验证 cert-manager 工具是否可以使用 CA Service 颁发者从 CA Service 获取证书。为了进行验证,您可以使用请求路由配置和入站流量网关的证书部署示例应用。

  1. 在 Cloud Shell 中,为示例应用资源创建命名空间:

    cat << EOF > sample-app-namespace.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: APP_NAMESPACE
      annotations:
        mesh.cloud.google.com/proxy: '{"managed":"true"}'
      labels:
        istio.io/rev: asm-managed
    EOF
    
    kubectl apply --filename sample-app-namespace.yaml
    
    • APP_NAMESPACE 是示例应用的命名空间名称。例如 sample-app

    mesh.cloud.google.com/proxy 注解用于为命名空间启用代管式数据平面

    istio.io/rev: asm-managed 标签用于为示例应用命名空间中的代管式数据平面选择常规发布渠道。如果您使用快速或稳定的发布渠道,请更改此标签的值。

  2. 为示例应用创建 Deployment 资源:

    cat << EOF > deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello
      namespace: APP_NAMESPACE
      labels:
        app: hello
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hello
      template:
        metadata:
          labels:
            app: hello
        spec:
          containers:
          - image: gcr.io/google-samples/hello-app:1.0
            name: hello-app
            ports:
            - containerPort: 8080
    EOF
    
    kubectl apply --filename deployment.yaml
    
  3. 为示例应用创建 Service 资源:

    cat << EOF > service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: SERVICE_NAME
      namespace: APP_NAMESPACE
    spec:
      ports:
      - name: http-hello
        port: 8080
      selector:
        app: hello
      type: ClusterIP
    EOF
    
    kubectl apply --filename service.yaml
    
    • SERVICE_NAME 是服务的名称。例如 hello
  4. 使用证书颁发者为域名 hello.example.com 创建证书资源:

    cat << EOF > certificate.yaml
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: hello-example-com-certificate
      namespace: GATEWAY_NAMESPACE
    spec:
      secretName: hello-example-com-credential
      commonName: hello.example.com
      dnsNames:
      - hello.example.com
      duration: 24h
      renewBefore: 8h
      issuerRef:
        group: cas-issuer.jetstack.io
        kind: GoogleCASIssuer
        name: gateway-cas-issuer
    EOF
    
    kubectl apply --filename certificate.yaml
    

    证书命名空间必须与入站流量网关命名空间匹配。通常,只有平台管理员可以更改此命名空间中的资源,因为更改可能会影响整个服务网格。cert-manager 工具在同一命名空间中为 TLS 证书创建 Secret 资源。这意味着应用管理员无需拥有入站流量网关命名空间的访问权限。

    您可以在证书的 dnsNames 列表中添加其他主机名。这些主机名会以主题备用名称 (SAN) 的形式包含在证书中。

  5. 创建网关 资源:

    cat << EOF > gateway.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: GATEWAY_NAME
      namespace: GATEWAY_NAMESPACE
    spec:
      selector:
        istio: ingressgateway
      servers:
      - hosts:
        - APP_NAMESPACE/hello.example.com
        port:
          name: https-hello
          number: 443
          protocol: HTTPS
        tls:
          credentialName: hello-example-com-credential
          mode: MUTUAL
    EOF
    
    kubectl apply --filename gateway.yaml
    
    • GATEWAY_NAME 是网关名称。例如 hello
    • 网关中的 credentialName 字段与证书中的 secretName 字段匹配。cert-manager 工具使用 CA Service 中的 TLS 证书创建 Kubernetes Secret。此证书可让入站流量网关终止流向 hello.example.com 的 TLS 流量。

    网关清单会指定 MUTUAL TLS (mTLS)。如果要为常规 TLS 配置网关,请将网关的 TLS 模式设置为 SIMPLE

  6. 为示例应用创建 VirtualService 资源:

    cat << EOF > virtual-service.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: hello
      namespace: APP_NAMESPACE
    spec:
      hosts:
      - hello.example.com
      gateways:
      - GATEWAY_NAMESPACE/GATEWAY_NAME
      http:
      - route:
        - destination:
            host: SERVICE_NAME
            port:
              number: 8080
    EOF
    
    kubectl apply --filename virtual-service.yaml
    

    网关和 VirtualService 使用的是不同的命名空间。此常见模式会施加限制,只允许有权更改入站流量网关命名空间中的资源的平台管理员更改网关中基于主机的路由。

    有权在示例应用命名空间中修改 VirtualService 的应用管理员可以通过其他请求字段(如网址路径)更改路由,而无需与平台管理员协调。

如果要浏览其他配置选项,请阅读证书网关VirtualService 资源的相关 API 文档。

您可以对通过入站流量网关进入服务网格的流量应用身份验证和授权政策。为此,请参阅 Istio PeerAuthenticationAuthorizationPolicy API。

验证解决方案

在本部分中,您将验证是否可以使用 mTLS 从服务网格外部向示例应用发送 HTTPS 请求。如需进行验证,请创建一个 Compute Engine 虚拟机实例,向 CA Service 请求客户端 TLS 证书,然后使用此证书对向示例应用发出的请求进行身份验证。

您需要通过 SSH 访问虚拟机实例。默认网络包含允许通过 SSH 访问的防火墙规则。如果您没有通过 SSH 进行访问的权限,请按照防火墙规则文档创建防火墙规则,以允许在端口 22 上建立传入的 TCP 连接。

  1. 在 Cloud Shell 中,创建一个 Google 服务账号:

    gcloud iam service-accounts create CLIENT_VM_GSA \
        --display-name "CA Service tutorial VM instance service account"
    
    • CLIENT_VM_GSA 是 Google 服务账号的名称。例如 cas-tutorial-client

    您可以将此 Google 服务账号分配给 Compute Engine 虚拟机实例。

  2. 将网关从属 CA 池中的 CA Service Certificate Requester 角色授予 Google 服务账号:

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    

    此角色提供从 CA 池请求证书的权限。

  3. 在 GKE 集群所在的同一 VPC 中创建 Compute Engine 虚拟机实例:

    gcloud compute instances create cas-tutorial-client \
        --scopes cloud-platform \
        --service-account CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --zone ZONE
    

    虚拟机实例需要 cloud-platform 范围才能访问 CA Service API

  4. 将入站流量网关内部直通网络负载均衡器的 IP 地址保存到文件中:

    kubectl get services istio-ingressgateway \
       --namespace GATEWAY_NAMESPACE \
       --output jsonpath='{.status.loadBalancer.ingress[0].ip}' > ilb-ip.txt
    
  5. 将根 CA 的公钥证书保存到文件中:

    gcloud privateca roots describe ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --format 'value(pemCaCertificates)' > root-ca-cert.pem
    
  6. 将根 CA 证书和包含入站流量网关内部直通网络负载均衡器的 IP 地址的文件复制到虚拟机实例:

    gcloud compute scp root-ca-cert.pem ilb-ip.txt cas-tutorial-client:~ \
       --zone ZONE
    
  7. 使用 SSH 连接到虚拟机实例:

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    在 SSH 会话中运行本部分中的其余命令。

  8. 安装 ca-certificatescoreutils 软件包,以及 curlopenssljq 命令行工具:

    sudo apt-get update --yes
    
    sudo apt-get install --yes ca-certificates coreutils curl jq openssl
    
  9. 为客户端 TLS 证书创建密钥对:

    openssl genrsa -out private-key.pem 2048
    
    openssl rsa -in private-key.pem -pubout -out public-key.pem
    
  10. 查询元数据服务器,以获取关联到虚拟机实例的 Google 服务账号身份的电子邮件地址:

    GSA_EMAIL=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email)
    
  11. 创建在通过 Certificate Authority Service API 请求客户端 TLS 证书时用作请求正文的 JSON 文件:

    cat << EOF > request.json
    {
      "config": {
        "publicKey": {
          "format": "PEM",
          "key": "$(base64 --wrap 0 public-key.pem)"
        },
        "subjectConfig": {
          "subject": {
            "commonName": "$(hostname --short)",
            "organization": "Example Organization"
          },
          "subjectAltName": {
            "dnsNames": [
              "$(hostname --fqdn)"
            ],
            "emailAddresses": [
              "$GSA_EMAIL"
            ]
          }
        },
        "x509Config": {
          "caOptions": {
            "isCa": false
          },
          "keyUsage": {
            "baseKeyUsage": {
              "digitalSignature": true,
              "keyEncipherment": true
            },
            "extendedKeyUsage": {
              "clientAuth": true
            }
          }
        }
      },
      "lifetime": "86400s"
    }
    EOF
    

    如需详细了解配置部分中的字段,请参阅 CA Service API 文档中的 CertificateConfig 类型。

  12. 元数据服务器请求 OAuth 2.0 访问令牌

    TOKEN=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token | jq --raw-output ".access_token")
    

    此访问令牌提供向关联到虚拟机实例的 Google 服务账号授予的权限。

  13. 通过 CA Service API 请求客户端 TLS 证书,并将响应正文存储在文件中:

    curl --silent --request POST \
        --header "Authorization: Bearer $TOKEN" \
        --header "Content-Type: application/json" \
        --data @request.json \
        --output response.json \
        "https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_GATEWAYS/certificates"
    

    该命令使用访问令牌对 API 请求进行身份验证。

  14. 将客户端证书和证书链保存到文件中:

    jq --raw-output --join-output ".pemCertificate , .pemCertificateChain[]" response.json > client-cert-chain.pem
    
  15. 使用 curl 从虚拟机实例向示例应用发送 HTTPS 请求:

    curl --cert client-cert-chain.pem --key private-key.pem \
        --cacert root-ca-cert.pem \
        --resolve hello.example.com:443:$(cat ilb-ip.txt) \
        --silent https://hello.example.com | head -n1
    

    输出如下所示:

    Hello, world!
    

    此响应显示 curl 已成功使用 mTLS 发送 HTTPS 请求。示例应用使用您在终端输出中看到的消息进行了响应。

    curl 命令执行以下操作:

    • --cert--key 标志指示 curl 使用客户端 TLS 证书和私钥来对请求进行身份验证。客户端证书文件包含完整的证书链(从客户端证书到根 CA)。

    • --cacert 标志指示 curl 验证您在本教程中创建的根 CA 或其某个从属 CA 是否颁发了服务器证书。

      如果省略此标志,curl 将尝试使用操作系统的默认 CA 软件包(例如 Debian 上 ca-certificates 软件包)验证服务器证书。验证失败,因为默认 CA 软件包不包含您在本教程中创建的根 CA。

    • --resolve 标志指示 curl 使用内部直通网络负载均衡器 IP 地址作为在端口 443 上托管 hello.example.com 的请求的目标。

      如果您省略此标志,则 curl 会尝试使用 DNS 来解析 hello.example.com 主机名。DNS 解析失败,因为此主机名没有 DNS 条目。

      在您自己的环境中,我们建议您创建指向内部直通网络负载均衡器 IP 地址 ($LOAD_BALANCER_IP) 的 DNS A 记录。您可以按照有关管理记录的文档的说明,使用 Cloud DNS 创建此记录。

    • --silent 标志禁止在终端输出中报告响应下载进度。

    • 该命令将 curl 输出传送到 head -n1。结果是终端中的输出仅包含响应正文的第一行。

  16. 退出 SSH 会话:

    exit
    

在本部分中,您直接通过 CA Service API 请求客户端 TLS 证书。如果客户端是单独的 Kubernetes 集群中其他服务网格的出站流量网关,您可以使用 cert-manager 工具和具有相同根 CA 的 Certificate Authority Service 颁发者来提供向出站流量网关提供客户端证书。

在其他情况下,您可以使用 Hashicorp Vault、Terraform 或 gcloud 等工具为服务网格外部的工作负载请求客户端 TLS 证书。如需了解详情,请参阅 CA Service 文档查看示例解决方案,并参阅 gcloud 文档了解 CA Service。

(可选)向信任库中添加 CA 证书

此可选部分介绍了如何将 CA 证书添加到 Linux Debian 发行版的可信 CA 证书存储区。这些说明也适用于源自 Debian 的发行版,例如 Ubuntu。

将 CA 证书添加到此存储区意味着在使用 curl、Python、Go 和 Ruby 发送 HTTPS 请求时无需指定可信 CA 证书的位置。

  1. 使用 SSH 连接到虚拟机实例:

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    在 SSH 会话中运行本部分中的其余命令。

  2. 将根 CA 证书复制到 /usr/local/share/ca-certificates 目录,并确保文件扩展名为 .crt

    sudo cp root-ca-cert.pem /usr/local/share/ca-certificates/cas-rootca.crt
    
  3. 设置文件权限,以便所有用户都能读取根 CA 证书文件:

    sudo chmod 644 /usr/local/share/ca-certificates/cas-rootca.crt
    
  4. 运行 update-ca-certificates 脚本:

    sudo update-ca-certificates
    

    此脚本会将证书添加到 /etc/ssl/certs 目录中的可信证书集以及 /etc/ssl/certs/ca-certificates.crt 文件中。

    输出如下所示:

    Updating certificates in /etc/ssl/certs...
    1 added, 0 removed; done.
    Running hooks in /etc/ca-certificates/update.d...
    done.
    
  5. 使用 curl 从虚拟机实例向示例应用发送 HTTPS 请求:

    curl --cert client-cert-chain.pem --key private-key.pem \
       --resolve hello.example.com:443:$(cat ilb-ip.txt) \
       --silent https://hello.example.com | head -n1
    

    输出如下所示:

    Hello, world!
    

    此响应显示 curl 已成功使用 mTLS 发送 HTTPS 请求,并使用默认 CA 证书存储区验证了来自入站流量网关的服务器 TLS 证书。

  6. 退出 SSH 会话:

    exit
    

问题排查

如果 CA Service 颁发者控制器未创建 TLS 证书 Secret,请查看 CA Service 颁发者控制器的日志:

kubectl logs deployment/google-cas-issuer --namespace cert-manager

如果您在安装 Cloud Service Mesh 时遇到问题,请运行 asmcli 工具验证您的 Cloud 项目和 GKE 集群

如果您在使用本教程时遇到其他问题,我们建议您查看以下文档:

清理

为避免系统因本教程中使用的资源而导致您的 Google Cloud 账号继续产生费用,您可以删除项目或删除各个资源。

删除项目

  1. 在 Cloud Shell 中,删除项目:

    gcloud projects delete PROJECT_ID
    

删除资源

如果您希望保留在本教程中使用的 Google Cloud 项目,请删除单个资源:

  1. 在 Cloud Shell 中,从 GKE Hub 取消注册 GKE 集群:

    gcloud container hub memberships unregister CLUSTER_NAME \
        --gke-cluster ZONE/CLUSTER_NAME
    
  2. 删除 GKE 集群:

    gcloud container clusters delete CLUSTER_NAME \
        --zone ZONE --async --quiet
    
  3. 删除针对从属 CA 池的 IAM 政策绑定:

    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  4. 停用从属 CA 和根 CA 并安排将其删除:

    gcloud privateca subordinates disable SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca subordinates disable SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca roots disable ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --quiet
    
    gcloud privateca roots delete ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --ignore-active-certificates \
        --quiet
    
  5. 删除 CA Service 颁发者控制器 Google 服务账号的 IAM 政策绑定:

    gcloud iam service-accounts remove-iam-policy-binding \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    
  6. 删除 Google 服务账号:

    gcloud iam service-accounts delete --quiet \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com
    
    gcloud iam service-accounts delete --quiet \
        CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com
    
  7. 删除预留的负载均衡器 IP 地址:

    gcloud compute addresses delete asm-ingress-gateway-ilb \
        --region REGION --quiet
    
  8. 删除 Compute Engine 虚拟机实例:

    gcloud compute instances delete cas-tutorial-client \
        --zone ZONE --quiet
    

后续步骤