使用无代理 gRPC(旧版)设置服务安全性

本指南介绍如何为无代理 gRPC 服务网格配置安全服务。

本文档仅适用于使用负载均衡 API 的 Cloud Service Mesh。这是旧版文档。

使用要求

在为 gRPC 代理无服务网格配置服务安全之前,请确保满足以下要求。

配置身份和访问权限管理

您必须具有 使用 Google Kubernetes Engine 所需的权限。您必须至少具有以下角色:

  • roles/container.clusterAdmin GKE 角色
  • roles/compute.instanceAdmin Compute Engine 角色
  • roles/iam.serviceAccountUser 角色

如要创建设置所需的资源,您必须具有 compute.NetworkAdmin 角色。此角色包含创建、更新、删除、列出和使用所需资源(即在其他资源中引用)的所有必要权限。如果您是项目的所有者编辑者,则会自动拥有此角色。

请注意,在后端服务和目标 HTTPS 代理资源中引用这些资源时,系统不会强制执行 networksecurity.googleapis.com.clientTlsPolicies.usenetworksecurity.googleapis.com.serverTlsPolicies.use

如果这是将来强制执行的,并且您使用的是 compute.NetworkAdmin 角色,则在强制执行此检查时不会注意到任何问题。

如果您使用的是自定义角色,并且此检查将强制执行,那么您必须确保添加相应的 .use 权限。否则,将来,您可能会发现自定义角色没有必要的权限,分别引用后端服务或目标 HTTPS 代理中的 clientTlsPolicyserverTlsPolicy

准备设置

无代理服务网格 (PSM) 安全性为根据无代理 gRPC 服务文档设置的负载均衡服务网格增加了安全性。在无代理服务网格中,gRPC 客户端在 URI 中使用架构 xds: 来访问服务,这将启用 PSM 负载均衡和端点发现功能。

将 gRPC 客户端和服务器更新为正确的版本

使用您的语言支持的最低 gRPC 版本构建或重建您的应用。

更新引导文件

gRPC 应用使用单个引导文件,该文件必须包含 gRPC 客户端和服务器端代码所需的所有字段。引导生成器会自动生成引导文件,其中包含 PSM 安全所需的标志和值。如需了解详情,请参阅引导文件部分,其中包含示例引导文件。

设置概览

此设置过程是对使用 GKE 和无代理 gRPC 服务设置 Cloud Service Mesh 的扩展,会在适用的位置引用该设置过程的现有未修改步骤。

对使用 GKE 设置 Cloud Service Mesh 的主要改进如下:

  1. 设置 CA Service,在其中创建私有 CA 池和所需的证书授权机构。
  2. 创建具有 GKE Workload Identity Federation for GKE 和网格证书功能和 CA Service 集成的 GKE 集群。
  3. 在集群上配置网格证书颁发。
  4. 创建客户端和服务器服务账号。
  5. 设置使用 xDS API 和 xDS 服务器凭据从 Cloud Service Mesh 获取安全配置的示例服务器
  6. 设置使用 xDS 凭据的示例客户端
  7. 更新 Cloud Service Mesh 配置以包含安全配置。

您可以在以下位置查看使用 xDS 凭据的代码示例:

更新 Google Cloud CLI

如需更新 Google Cloud CLI,请运行以下命令:

gcloud components update

设置环境变量

在本指南中,您将使用 Cloud Shell 命令,命令中的重复信息由各种环境变量表示。在执行命令之前,将 Shell 环境中的以下环境变量设置为您的特定值。每个注释行说明关联的环境变量的含义。

# Your project ID
PROJECT_ID=YOUR_PROJECT_ID

# GKE cluster name and zone for this example.
CLUSTER_NAME="secure-psm-cluster"
ZONE="us-east1-d"

# GKE cluster URL derived from the above
GKE_CLUSTER_URL="https://container.googleapis.com/v1/projects/${PROJECT_ID}/locations/${ZONE}/clusters/${CLUSTER_NAME}"

# Workload pool to be used with the GKE cluster
WORKLOAD_POOL="${PROJECT_ID}.svc.id.goog"

# Kubernetes namespace to run client and server demo.
K8S_NAMESPACE='default'
DEMO_BACKEND_SERVICE_NAME='grpc-gke-helloworld-service'

# Compute other values
# Project number for your project
PROJNUM=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")

# VERSION is the GKE cluster version. Install and use the most recent version
# from the rapid release channel and substitute its version for
# CLUSTER_VERSION, for example:
# VERSION=latest available version
# Note that the minimum required cluster version is 1.21.4-gke.1801.
VERSION="CLUSTER_VERSION"
SA_GKE=service-${PROJNUM}@container-engine-robot.iam.gserviceaccount.com

启用对所需 API 的访问权限

此部分介绍如何启用对必需的 API 的访问权限。

  1. 运行以下命令,启用 Cloud Service Mesh 和无代理 gRPC 服务网格安全所需的其他 API。

    gcloud services enable \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        compute.googleapis.com \
        trafficdirector.googleapis.com \
        networkservices.googleapis.com \
        networksecurity.googleapis.com \
        privateca.googleapis.com \
        gkehub.googleapis.com
    
  2. 运行以下命令,允许默认服务账号访问 Cloud Service Mesh 安全 API。

    GSA_EMAIL=$(gcloud iam service-accounts list --format='value(email)' \
        --filter='displayName:Compute Engine default service account')
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member serviceAccount:${GSA_EMAIL} \
       --role roles/trafficdirector.client
    

创建或更新 GKE 集群

Cloud Service Mesh 服务安全取决于 CA Service 与 GKE 的集成。除了设置要求之外,GKE 集群还必须满足以下要求:

  • 使用的最低集群版本为 1.21.4-gke.1801。如果您需要更高版本的功能,则可以从快速发布渠道中获取该版本。
  • 必须使用网格证书启用和配置 GKE 集群,如创建证书授权机构以颁发证书中所述。
  1. 创建使用适用于 GKE 的工作负载身份联合的新集群。如果您要更新现有集群,请跳至下一步。您为 --tags 指定的值必须与使用 Cloud Load Balancing 组件配置 Cloud Service Mesh 部分中传递给 firewall-rules create 命令的 --target-tags 标志的名称匹配。

    # Create a GKE cluster with GKE managed mesh certificates.
    gcloud container clusters create CLUSTER_NAME \
      --release-channel=rapid \
      --scopes=cloud-platform \
      --image-type=cos_containerd \
      --machine-type=e2-standard-2 \
      --zone=ZONE \
      --workload-pool=PROJECT_ID.svc.id.goog \
      --enable-mesh-certificates \
      --cluster-version=CLUSTER_VERSION \
      --enable-ip-alias \
      --tags=allow-health-checks \
      --workload-metadata=GKE_METADATA
    

    集群创建可能需要几分钟才能完成。

  2. 如果您使用的是现有集群,请开启适用于 GKE 的工作负载身份联合和 GKE 网格证书。确保使用 --enable-ip-alias 标志创建集群,该标志无法与 update 命令一起使用。

    gcloud container clusters update CLUSTER_NAME \
      --enable-mesh-certificates
    
  3. 运行以下命令切换到作为 kubectl 命令的默认集群的新集群:

    gcloud container clusters get-credentials CLUSTER_NAME \
      --zone ZONE
    

向舰队注册集群

向舰队注册您在创建 GKE 集群中创建或更新的集群。通过注册集群,您可以更轻松地跨多个项目配置集群。

请注意,每个步骤最长可能需要十分钟才能完成。

  1. 向舰队注册集群:

    gcloud container fleet memberships register CLUSTER_NAME \
      --gke-cluster=ZONE/CLUSTER_NAME \
      --enable-workload-identity --install-connect-agent \
      --manifest-output-file=MANIFEST-FILE_NAME
    

    替换如下变量:

    • CLUSTER_NAME:您的集群的名称。
    • ZONE:您的集群的可用区。
    • MANIFEST-FILE_NAME:这些命令生成注册清单的路径。

    注册成功后,您会看到如下消息:

    Finished registering the cluster CLUSTER_NAME with the fleet.
  2. 将生成的清单文件应用于您的集群:

    kubectl apply -f MANIFEST-FILE_NAME
    

    应用成功后,您会看到如下消息:

    namespace/gke-connect created
    serviceaccount/connect-agent-sa created
    podsecuritypolicy.policy/gkeconnect-psp created
    role.rbac.authorization.k8s.io/gkeconnect-psp:role created
    rolebinding.rbac.authorization.k8s.io/gkeconnect-psp:rolebinding created
    role.rbac.authorization.k8s.io/agent-updater created
    rolebinding.rbac.authorization.k8s.io/agent-updater created
    role.rbac.authorization.k8s.io/gke-connect-agent-20210416-01-00 created
    clusterrole.rbac.authorization.k8s.io/gke-connect-impersonation-20210416-01-00 created
    clusterrolebinding.rbac.authorization.k8s.io/gke-connect-impersonation-20210416-01-00 created
    clusterrolebinding.rbac.authorization.k8s.io/gke-connect-feature-authorizer-20210416-01-00 created
    rolebinding.rbac.authorization.k8s.io/gke-connect-agent-20210416-01-00 created
    role.rbac.authorization.k8s.io/gke-connect-namespace-getter created
    rolebinding.rbac.authorization.k8s.io/gke-connect-namespace-getter created
    secret/http-proxy created
    deployment.apps/gke-connect-agent-20210416-01-00 created
    service/gke-connect-monitoring created
    secret/creds-gcp create
    
  3. 从集群获取成员资格资源:

    kubectl get memberships membership -o yaml
    

    输出应包含由舰队分配的 Workoad Identity 池,其中 PROJECT_ID 是您的项目 ID:

    workload_identity_pool: PROJECT_ID.svc.id.goog
    

    这表示集群已成功注册。

创建证书授权机构以颁发证书

如需向 Pod 颁发证书,请创建 CA 服务池和以下证书授权机构 (CA):

  • 根 CA。这是所有已颁发的网格证书的信任根。可以使用现有的根 CA。在 enterprise 层级创建根 CA,用于颁发长期、少量的证书。
  • 从属 CA。此 CA 会为工作负载颁发证书。请在部署集群的区域中创建从属 CA。在 devops 层级创建从属 CA,用于颁发短期、大量的证书。

创建从属 CA 是可选操作,但我们强烈建议您创建一个,而不是使用根 CA 颁发 GKE 网格证书。如果您决定使用根 CA 颁发网格证书,请确保允许使用默认的基于配置的颁发模式

从属 CA 的区域可以不同于集群的区域,但我们强烈建议您在集群所在的区域中创建从属 CA 以优化性能。但是,您可以在不同区域中创建根 CA 和从属 CA,这不会影响性能或可用性。

CA Service 支持以下区域:

区域名称 区域说明
asia-east1 台湾
asia-east2 香港
asia-northeast1 东京
asia-northeast2 大阪
asia-northeast3 首尔
asia-south1 孟买
asia-south2 德里
asia-southeast1 新加坡
asia-southeast2 雅加达
australia-southeast1 悉尼
australia-southeast2 墨尔本
europe-central2 华沙
europe-north1 芬兰
europe-southwest1 马德里
europe-west1 比利时
europe-west2 伦敦
europe-west3 法兰克福
europe-west4 荷兰
europe-west6 苏黎世
europe-west8 米兰
europe-west9 巴黎
europe-west10 柏林
europe-west12 都灵
me-central1 多哈
me-central2 达曼
me-west1 特拉维夫
northamerica-northeast1 蒙特利尔
northamerica-northeast2 多伦多
southamerica-east1 圣保罗
southamerica-west1 圣地亚哥
us-central1 爱荷华
us-east1 南卡罗来纳
us-east4 北弗吉尼亚
us-east5 哥伦布
us-south1 达拉斯
us-west1 俄勒冈
us-west2 洛杉矶
us-west3 盐湖城
us-west4 拉斯维加斯

此外,还可以通过运行以下命令来查看受支持位置的列表:

gcloud privateca locations list
  1. 将 IAM roles/privateca.caManager 授予创建 CA 池和 CA 的个人。请注意,对于 MEMBER,正确格式为 user:userid@example.com。如果该人员是当前用户,您可以使用 shell 命令 $(gcloud auth list --filter=status:ACTIVE --format="value(account)") 获取当前用户 ID。

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member=MEMBER \
      --role=roles/privateca.caManager
    
  2. 将 CA 角色 role/privateca.admin 授予需要修改 IAM 政策的个人,其中 MEMBER 是需要此访问权限的个人,具体而言是执行以下步骤的个人这些角色可授予 privateca.auditorprivateca.certificateManager 角色:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member=MEMBER \
      --role=roles/privateca.admin
    
  3. 创建根 CA 服务池。

    gcloud privateca pools create ROOT_CA_POOL_NAME \
      --location ROOT_CA_POOL_LOCATION \
      --tier enterprise
    
  4. 创建根 CA。

    gcloud privateca roots create ROOT_CA_NAME --pool ROOT_CA_POOL_NAME \
      --subject "CN=ROOT_CA_NAME, O=ROOT_CA_ORGANIZATION" \
      --key-algorithm="ec-p256-sha256" \
      --max-chain-length=1 \
      --location ROOT_CA_POOL_LOCATION
    

    对于此演示设置,请为变量使用以下值:

    • ROOT_CA_POOL_NAME=td_sec_pool
    • ROOT_CA_NAME=pkcs2-ca
    • ROOT_CA_POOL_LOCATION=us-east1
    • ROOT_CA_ORGANIZATION="TestCorpLLC"
  5. 创建从属池和从属 CA。确保允许使用默认的基于配置的颁发模式

    gcloud privateca pools create SUBORDINATE_CA_POOL_NAME \
      --location SUBORDINATE_CA_POOL_LOCATION \
      --tier devops
    
    gcloud privateca subordinates create SUBORDINATE_CA_NAME \
      --pool SUBORDINATE_CA_POOL_NAME \
      --location SUBORDINATE_CA_POOL_LOCATION \
      --issuer-pool ROOT_CA_POOL_NAME \
      --issuer-location ROOT_CA_POOL_LOCATION \
      --subject "CN=SUBORDINATE_CA_NAME, O=SUBORDINATE_CA_ORGANIZATION" \
      --key-algorithm "ec-p256-sha256" \
      --use-preset-profile subordinate_mtls_pathlen_0
    

    对于此演示设置,请为变量使用以下值:

    • SUBORDINATE_CA_POOL_NAME="td-ca-pool"
    • SUBORDINATE_CA_POOL_LOCATION=us-east1
    • SUBORDINATE_CA_NAME="td-ca"
    • SUBORDINATE_CA_ORGANIZATION="TestCorpLLC"
    • ROOT_CA_POOL_NAME=td_sec_pool
    • ROOT_CA_POOL_LOCATION=us-east1
  6. 授予根 CA 池 IAM privateca.auditor 角色,以允许从 GKE 服务账号进行访问:

    gcloud privateca pools add-iam-policy-binding ROOT_CA_POOL_NAME \
     --location ROOT_CA_POOL_LOCATION \
     --role roles/privateca.auditor \
     --member="serviceAccount:service-PROJNUM@container-engine-robot.iam.gserviceaccount.com"
    
  7. 授予从属 CA 池 IAM privateca.certificateManager 角色,以允许从 GKE 服务账号进行访问:

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_NAME \
      --location SUBORDINATE_CA_POOL_LOCATION \
      --role roles/privateca.certificateManager \
      --member="serviceAccount:service-PROJNUM@container-engine-robot.iam.gserviceaccount.com"
    
  8. 保存以下 WorkloadCertificateConfig YAML 配置,以告知集群如何颁发网格证书:

    apiVersion: security.cloud.google.com/v1
    kind: WorkloadCertificateConfig
    metadata:
      name: default
    spec:
      # Required. The CA service that issues your certificates.
      certificateAuthorityConfig:
        certificateAuthorityServiceConfig:
          endpointURI: ISSUING_CA_POOL_URI
    
      # Required. The key algorithm to use. Choice of RSA or ECDSA.
      #
      # To maximize compatibility with various TLS stacks, your workloads
      # should use keys of the same family as your root and subordinate CAs.
      #
      # To use RSA, specify configuration such as:
      #   keyAlgorithm:
      #     rsa:
      #       modulusSize: 4096
      #
      # Currently, the only supported ECDSA curves are "P256" and "P384", and the only
      # supported RSA modulus sizes are 2048, 3072 and 4096.
      keyAlgorithm:
        rsa:
          modulusSize: 4096
    
      # Optional. Validity duration of issued certificates, in seconds.
      #
      # Defaults to 86400 (1 day) if not specified.
      validityDurationSeconds: 86400
    
      # Optional. Try to start rotating the certificate once this
      # percentage of validityDurationSeconds is remaining.
      #
      # Defaults to 50 if not specified.
      rotationWindowPercentage: 50
    
    

    替换以下内容:

    • 运行您的集群的项目的 ID:
      PROJECT_ID
    • 颁发网格证书的 CA 的完全限定 URI (ISSUING_CA_POOL_URI)。这可以是从属 CA(推荐)也可以是根 CA。格式为:
      //privateca.googleapis.com/projects/PROJECT_ID/locations/SUBORDINATE_CA_POOL_LOCATION/caPools/SUBORDINATE_CA_POOL_NAME
  9. 保存以下 TrustConfig YAML 配置,以告知集群如何信任颁发的证书:

    apiVersion: security.cloud.google.com/v1
    kind: TrustConfig
    metadata:
      name: default
    spec:
      # You must include a trustStores entry for the trust domain that
      # your cluster is enrolled in.
      trustStores:
      - trustDomain: PROJECT_ID.svc.id.goog
        # Trust identities in this trustDomain if they appear in a certificate
        # that chains up to this root CA.
        trustAnchors:
        - certificateAuthorityServiceURI: ROOT_CA_POOL_URI
    

    替换以下内容:

    • 运行您的集群的项目的 ID:
      PROJECT_ID
    • 根 CA 池的完全限定 URI (ROOT_CA_POOL_URI)。格式为:
      //privateca.googleapis.com/projects/PROJECT_ID/locations/ROOT_CA_POOL_LOCATION/caPools/ROOT_CA_POOL_NAME
  10. 将配置应用到您的集群:

    kubectl apply -f WorkloadCertificateConfig.yaml
    kubectl apply -f TrustConfig.yaml
    

使用 NEG 创建无代理 gRPC 服务

为实现 PSM 安全机制,您需要一个能够使用 xDS 从 Cloud Service Mesh 获取安全配置的无代理 gRPC 服务器。此步骤类似于 PSM 负载均衡设置指南中的使用 NEG 配置 GKE 服务,但您使用 grpc-java 代码库的 xDS 示例中启用 xDS 的 helloworld 服务器,而不是 java-example-hostname 映像。

在通过 openjdk:8-jdk 映像构建的容器中构建和运行此服务器。您还可以使用命名 NEG 功能,为 NEG 指定名称。这可以简化后续步骤,因为部署知道 NEG 的名称,从而无需进行查找。

以下是 gRPC 服务器 Kubernetes 规范的完整示例。请注意以下事项:

  • 此规范创建一个由 gRPC 服务器 pod 使用的 Kubernetes 服务账号 example-grpc-server
  • 此规范使用服务的 cloud.google.com/neg 注释中的 name 字段指定 NEG 名称 example-grpc-server
  • 变量 ${PROJNUM} 表示您的项目的项目编号。
  • 此规范使用 initContainers 部分运行引导生成器,以填充无代理 gRPC 库所需的引导文件。此引导文件位于名为 example-grpc-server 的 gRPC 服务器容器中的 /tmp/grpc-xds/td-grpc-bootstrap.json

将以下注释添加到 pod 规范:

 annotations:
   security.cloud.google.com/use-workload-certificates: ""

您可以在下面的完整规范中看到正确的显示位置。

创建后,每个 pod 都会获得一个位于 /var/run/secrets/workload-spiffe-credentials 的卷。此卷包含以下内容:

  • private_key.pem 是自动生成的私钥。
  • certificates.pem 是一组 PEM 格式的证书,可作为客户端证书链提供给其他 pod,也可以用作服务器证书链。
  • ca_certificates.pem 是一组 PEM 格式的证书,在验证其他 pod 提供的客户端证书链或连接到其他 pod 时收到的服务器证书链时用作信任锚。

请注意,ca_certificates.pem 包含工作负载的本地信任网域(即集群的工作负载池)的证书。

certificates.pem 中的叶证书包含以下纯文本 SPIFFE 身份断言:

spiffe://WORKLOAD_POOL/ns/NAMESPACE/sa/KUBERNETES_SERVICE_ACCOUNT

在此断言中:

  • WORKLOAD_POOL 是集群工作负载池的名称。
  • NAMESPACE 是您的 Kubernetes 服务账号的命名空间。
  • KUBERNETES_SERVICE_ACCOUNT 是您的 Kubernetes 服务账号名称。

以下针对您的语言的说明创建了要在此示例中使用的规范。

Java

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建规范:

    cat << EOF > example-grpc-server.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: example-grpc-server
     namespace: default
     annotations:
       iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
     annotations:
       cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
    spec:
     ports:
     - name: helloworld
       port: 8080
       protocol: TCP
       targetPort: 50051
     selector:
       k8s-app: example-grpc-server
     type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
    spec:
     replicas: 1
     selector:
       matchLabels:
         k8s-app: example-grpc-server
     strategy: {}
     template:
       metadata:
         annotations:
            security.cloud.google.com/use-workload-certificates: ""
         labels:
           k8s-app: example-grpc-server
       spec:
         containers:
         - image: openjdk:8-jdk
           imagePullPolicy: IfNotPresent
           name: example-grpc-server
           command:
           - /bin/sleep
           - inf
           env:
           - name: GRPC_XDS_BOOTSTRAP
             value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
           ports:
           - protocol: TCP
             containerPort: 50051
           resources:
             limits:
               cpu: 800m
               memory: 512Mi
             requests:
               cpu: 100m
               memory: 512Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/grpc-xds/
         initContainers:
         - name: grpc-td-init
           image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
           imagePullPolicy: Always
           args:
           - --output
           - "/tmp/bootstrap/td-grpc-bootstrap.json"
           - --node-metadata=app=helloworld
           resources:
             limits:
               cpu: 100m
               memory: 100Mi
             requests:
               cpu: 10m
               memory: 100Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/bootstrap/
         serviceAccountName: example-grpc-server
         volumes:
         - name: grpc-td-conf
           emptyDir:
             medium: Memory
    EOF
    

C++

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建规范:

    cat << EOF > example-grpc-server.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: example-grpc-server
     namespace: default
     annotations:
       iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
     annotations:
       cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
    spec:
     ports:
     - name: helloworld
       port: 8080
       protocol: TCP
       targetPort: 50051
     selector:
       k8s-app: example-grpc-server
     type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
    spec:
     replicas: 1
     selector:
       matchLabels:
         k8s-app: example-grpc-server
     strategy: {}
     template:
       metadata:
         annotations:
            security.cloud.google.com/use-workload-certificates: ""
         labels:
           k8s-app: example-grpc-server
       spec:
         containers:
         - image: phusion/baseimage:18.04-1.0.0
           imagePullPolicy: IfNotPresent
           name: example-grpc-server
           command:
           - /bin/sleep
           - inf
           env:
           - name: GRPC_XDS_BOOTSTRAP
             value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
           ports:
           - protocol: TCP
             containerPort: 50051
           resources:
             limits:
               cpu: 8
               memory: 8Gi
             requests:
               cpu: 300m
               memory: 512Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/grpc-xds/
         initContainers:
         - name: grpc-td-init
           image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
           imagePullPolicy: Always
           args:
           - --output
           - "/tmp/bootstrap/td-grpc-bootstrap.json"
           - --node-metadata=app=helloworld
           resources:
             limits:
               cpu: 100m
               memory: 100Mi
             requests:
               cpu: 10m
               memory: 100Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/bootstrap/
         serviceAccountName: example-grpc-server
         volumes:
         - name: grpc-td-conf
           emptyDir:
             medium: Memory
    EOF
    

Python

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建规范:

    cat << EOF > example-grpc-server.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: example-grpc-server
     namespace: default
     annotations:
       iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
     annotations:
       cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
    spec:
     ports:
     - name: helloworld
       port: 8080
       protocol: TCP
       targetPort: 50051
     selector:
       k8s-app: example-grpc-server
     type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
    spec:
     replicas: 1
     selector:
       matchLabels:
         k8s-app: example-grpc-server
     strategy: {}
     template:
       metadata:
         annotations:
            security.cloud.google.com/use-workload-certificates: ""
         labels:
           k8s-app: example-grpc-server
       spec:
         containers:
         - image: phusion/baseimage:18.04-1.0.0
           imagePullPolicy: IfNotPresent
           name: example-grpc-server
           command:
           - /bin/sleep
           - inf
           env:
           - name: GRPC_XDS_BOOTSTRAP
             value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
           ports:
           - protocol: TCP
             containerPort: 50051
           resources:
             limits:
               cpu: 8
               memory: 8Gi
             requests:
               cpu: 300m
               memory: 512Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/grpc-xds/
         initContainers:
         - name: grpc-td-init
           image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
           imagePullPolicy: Always
           args:
           - --output
           - "/tmp/bootstrap/td-grpc-bootstrap.json"
           - --node-metadata=app=helloworld
           resources:
             limits:
               cpu: 100m
               memory: 100Mi
             requests:
               cpu: 10m
               memory: 100Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/bootstrap/
         serviceAccountName: example-grpc-server
         volumes:
         - name: grpc-td-conf
           emptyDir:
             medium: Memory
    EOF
    

Go

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建规范:

    cat << EOF > example-grpc-server.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: example-grpc-server
     namespace: default
     annotations:
       iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
     annotations:
       cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
    spec:
     ports:
     - name: helloworld
       port: 8080
       protocol: TCP
       targetPort: 50051
     selector:
       k8s-app: example-grpc-server
     type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: example-grpc-server
     namespace: default
     labels:
       k8s-app: example-grpc-server
    spec:
     replicas: 1
     selector:
       matchLabels:
         k8s-app: example-grpc-server
     strategy: {}
     template:
       metadata:
         annotations:
            security.cloud.google.com/use-workload-certificates: ""
         labels:
           k8s-app: example-grpc-server
       spec:
         containers:
         - image: golang:1.16-alpine
           imagePullPolicy: IfNotPresent
           name: example-grpc-server
           command:
           - /bin/sleep
           - inf
           env:
           - name: GRPC_XDS_BOOTSTRAP
             value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
           ports:
           - protocol: TCP
             containerPort: 50051
           resources:
             limits:
               cpu: 8
               memory: 8Gi
             requests:
               cpu: 300m
               memory: 512Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/grpc-xds/
         initContainers:
         - name: grpc-td-init
           image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
           imagePullPolicy: Always
           args:
           - --output
           - "/tmp/bootstrap/td-grpc-bootstrap.json"
           - --node-metadata=app=helloworld
           resources:
             limits:
               cpu: 100m
               memory: 100Mi
             requests:
               cpu: 10m
               memory: 100Mi
           volumeMounts:
           - name: grpc-td-conf
             mountPath: /tmp/bootstrap/
         serviceAccountName: example-grpc-server
         volumes:
         - name: grpc-td-conf
           emptyDir:
             medium: Memory
    EOF
    

    请按以下方式完成此过程。

  1. 应用规范:

    kubectl apply -f example-grpc-server.yaml
    
  2. 向服务账号授予所需角色:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-server]" \
      ${PROJNUM}-compute@developer.gserviceaccount.com
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-server]" \
      --role roles/trafficdirector.client
    
  3. 运行以下命令验证服务和 Pod 是否已正确创建:

    kubectl get deploy/example-grpc-server
    kubectl get svc/example-grpc-server
    
  4. 验证 NEG 名称正确无误:

    gcloud compute network-endpoint-groups list \
        --filter "name=example-grpc-server" --format "value(name)"
    

    上述命令应返回 NEG 名称 example-grpc-server

使用 Google Cloud 负载均衡组件配置 Cloud Service Mesh

此部分中的步骤与使用负载均衡组件配置 Cloud Service Mesh 中的步骤类似,但进行了一些变更,如下文所述。

创建健康检查、防火墙规则和后端服务

gRPC 服务器配置为使用 mTLS 时,gRPC 健康检查将不起作用,因为健康检查客户端无法向服务器提供有效的客户端证书。您可以通过以下两种方法之一来解决此问题:

在第一种方法中,您使服务器创建一个额外的服务端口,它被指定为健康检查端口。该端口连接到一个特殊健康检查服务,它以纯文本或 TLS 形式访问该端口。

xDS helloworld 示例服务器使用 PORT_NUMBER + 1 作为明文健康检查端口。该示例使用 50052 作为健康检查端口,因为 50051 是 gRPC 应用服务器端口。

在第二种方法中,您将健康检查配置为仅检查到应用服务端口的 TCP 连接性。此方法仅检查连接性,并且当 TLS 握手失败时,会产生到服务器的不必要流量。因此,我们建议您使用第一种方法。

  1. 创建健康检查。 请注意,只有创建并启动服务器后,健康检查才会启动。

    • 如果要为健康检查创建指定的服务端口,即我们推荐的方法,请使用以下命令:

      gcloud compute health-checks create grpc grpc-gke-helloworld-hc \
       --enable-logging --port 50052
      
    • 如果要创建 TCP 健康检查,即我们不推荐的方法,请使用以下命令:

      gcloud compute health-checks create tcp grpc-gke-helloworld-hc \
      --use-serving-port
      
  2. 创建防火墙。确保 --target-tags 的值与您在创建或更新 GKE 集群部分中为 --tags 提供的值匹配。

    gcloud compute firewall-rules create grpc-gke-allow-health-checks \
      --network default --action allow --direction INGRESS \
      --source-ranges 35.191.0.0/16,130.211.0.0/22 \
      --target-tags allow-health-checks \
      --rules tcp:50051-50052
    
  3. 创建后端服务:

    gcloud compute backend-services create grpc-gke-helloworld-service \
       --global \
       --load-balancing-scheme=INTERNAL_SELF_MANAGED \
       --protocol=GRPC \
       --health-checks grpc-gke-helloworld-hc
    
  4. 将 NEG 附加到后端服务:

    gcloud compute backend-services add-backend grpc-gke-helloworld-service \
       --global \
       --network-endpoint-group example-grpc-server \
       --network-endpoint-group-zone ${ZONE} \
       --balancing-mode RATE \
       --max-rate-per-endpoint 5
    

创建路由规则映射

这类似于您在使用 Google Kubernetes Engine 和无代理 gRPC 服务设置 Cloud Service Mesh创建路由规则映射的方式。

  1. 创建网址映射:

    gcloud compute url-maps create grpc-gke-url-map \
       --default-service grpc-gke-helloworld-service
    
  2. 向网址映射添加路径匹配器:

    gcloud compute url-maps add-path-matcher grpc-gke-url-map \
       --default-service grpc-gke-helloworld-service \
       --path-matcher-name grpc-gke-path-matcher \
       --new-hosts helloworld-gke:8000
    
  3. 创建目标 gRPC 代理:

    gcloud compute target-grpc-proxies create grpc-gke-proxy \
       --url-map grpc-gke-url-map --validate-for-proxyless
    
  4. 创建转发规则:

    gcloud compute forwarding-rules create grpc-gke-forwarding-rule \
      --global \
      --load-balancing-scheme=INTERNAL_SELF_MANAGED \
      --address=0.0.0.0 \
      --target-grpc-proxy=grpc-gke-proxy \
      --ports 8000 \
      --network default
    

使用无代理 gRPC 安全功能配置 Cloud Service Mesh

此示例演示了如何在客户端和服务器端配置 mTLS。

政策引用的格式

请注意引用服务器 TLS 和客户端 TLS 政策的以下所需格式:

projects/PROJECT_ID/locations/global/[serverTlsPolicies|clientTlsPolicies]/[server-tls-policy|client-mtls-policy]

例如:

projects/PROJECT_ID/locations/global/serverTlsPolicies/server-tls-policy
projects/PROJECT_ID/locations/global/clientTlsPolicies/client-mtls-policy

在服务器端配置 mTLS

首先,您需要创建服务器 TLS 政策。此政策要求 gRPC 服务器端为身份证书使用由名称 google_cloud_private_spiffe 标识的 certificateProvicerInstance 插件配置,这是 serverCertificate 的一部分。mtlsPolicy 部分指示 mTLS 安全,并使用与 clientValidationCa 的插件配置相同的 google_cloud_private_spiffe,后者是根(验证)证书规范。

接下来,您需要创建端点政策。此政策指定后端(例如 gRPC 服务器)使用 50051 端口以及任何元数据标签或者不使用元数据标签,接收名为 server-mtls-policy 的附加服务器 TLS 政策。您可以使用 MATCH_ALL 指定元数据标签。您可以使用您已定义的政策和包含端点政策资源的值的临时文件 ep-mtls-psms.yaml 创建端点政策。

  1. 使用服务器 TLS 政策资源的值在当前目录中创建临时文件 server-mtls-policy.yaml

    name: "projects/${PROJECT_ID}/locations/global/serverTlsPolicies/server-mtls-policy"
    serverCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    mtlsPolicy:
      clientValidationCa:
      - certificateProviderInstance:
          pluginInstance: google_cloud_private_spiffe
    
  2. 通过导入临时文件 server-mtls-policy.yaml 创建名为 server-mtls-policy 的服务器 TLS 政策资源:

    gcloud network-security server-tls-policies import server-mtls-policy \
      --source=server-mtls-policy.yaml --location=global
    
  3. 通过创建临时文件 ep-mtls-psms.yaml 创建端点政策:

    name: "ep-mtls-psms"
    type: "GRPC_SERVER"
    serverTlsPolicy: "projects/${PROJECT_ID}/locations/global/serverTlsPolicies/server-mtls-policy"
    trafficPortSelector:
      ports:
      - "50051"
    endpointMatcher:
      metadataLabelMatcher:
        metadataLabelMatchCriteria: "MATCH_ALL"
        metadataLabels:
        - labelName: app
          labelValue: helloworld
    
  4. 通过导入 ep-mtls-psms.yaml 文件来创建端点政策资源:

    gcloud beta network-services endpoint-policies import ep-mtls-psms \
      --source=ep-mtls-psms.yaml --location=global
    

在客户端配置 mTLS

客户端安全政策附加到后端服务。当客户端通过后端服务访问后端(gRPC 服务器)时,附加的客户端安全政策会被发送到客户端。

  1. 在当前目录中名为 client-mtls-policy.yaml 的临时文件中创建客户端 TLS 政策资源内容:

    name: "client-mtls-policy"
    clientCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    serverValidationCa:
    - certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    
  2. 通过导入临时文件 client-mtls-policy.yaml 创建名为 client-mtls-policy 的客户端 TLS 政策资源:

    gcloud network-security client-tls-policies import client-mtls-policy \
      --source=client-mtls-policy.yaml --location=global
    
  3. 在临时文件中创建一个代码段来引用此政策,并在 SecuritySettings 消息中添加 subjectAltNames 的详细信息,如以下示例所示。将 ${PROJECT_ID} 替换为您的项目 ID 值,即前面所述 ${PROJECT_ID} 环境变量的值。请注意,subjectAltNames 中的 example-grpc-server 是部署规范中用于 gRPC 服务器 pod 的 Kubernetes 服务账号名称。

    if [ -z "$PROJECT_ID" ] ; then echo Please make sure PROJECT_ID is set. ; fi
    cat << EOF > client-security-settings.yaml
    securitySettings:
      clientTlsPolicy: projects/${PROJECT_ID}/locations/global/clientTlsPolicies/client-mtls-policy
      subjectAltNames:
        - "spiffe://${PROJECT_ID}.svc.id.goog/ns/default/sa/example-grpc-server"
    EOF
    
  4. securitySettings 消息添加到您已创建的后端服务中。这些步骤会导出当前后端服务内容,添加客户端 securitySetting 消息并重新导入新内容以更新后端服务。

    gcloud compute backend-services export grpc-gke-helloworld-service --global \
      --destination=/tmp/grpc-gke-helloworld-service.yaml
    
    cat /tmp/grpc-gke-helloworld-service.yaml client-security-settings.yaml \
      >/tmp/grpc-gke-helloworld-service1.yaml
    
    gcloud compute backend-services import grpc-gke-helloworld-service --global \
      --source=/tmp/grpc-gke-helloworld-service1.yaml -q
    

验证配置

Cloud Service Mesh 配置现已完成,包括服务器端和客户端安全。接下来,准备并运行服务器和客户端工作负载。这样就完成了示例。

创建无代理 gRPC 客户端

此步骤与上一部分中的创建无代理 gRPC 服务类似。您使用 grpc-java 代码库的 xDS 示例目录中启用 xDS 的 helloworld 客户端。在通过 openjdk:8-jdk 映像构建的容器中构建和运行客户端。gRPC 客户端 Kubernetes 规范执行以下操作。

  • 它会创建一个供 gRPC 客户端 Pod 使用的 Kubernetes 服务账号 example-grpc-client
  • ${PROJNUM} 表示您的项目的项目编号,需要替换为实际编号。

将以下注释添加到 pod 规范:

  annotations:
    security.cloud.google.com/use-workload-certificates: ""

创建后,每个 pod 都会获得一个位于 /var/run/secrets/workload-spiffe-credentials 的卷。此卷包含以下内容:

  • private_key.pem 是自动生成的私钥。
  • certificates.pem 是一组 PEM 格式的证书,可作为客户端证书链提供给其他 pod,也可以用作服务器证书链。
  • ca_certificates.pem 是一组 PEM 格式的证书,在验证其他 pod 提供的客户端证书链或连接到其他 pod 时收到的服务器证书链时用作信任锚。

请注意,ca_certificates.pem 包含工作负载的本地信任网域(即集群的工作负载池)的根证书。

certificates.pem 中的叶证书包含以下纯文本 SPIFFE 身份断言:

spiffe://WORKLOAD_POOL/ns/NAMESPACE/sa/KUBERNETES_SERVICE_ACCOUNT

在此断言中:

  • WORKLOAD_POOL 是集群工作负载池的名称。
  • NAMESPACE 是您的 Kubernetes 服务账号名称。
  • KUBERNETES_SERVICE_ACCOUNT 是您的 Kubernetes 服务账号的命名空间。

以下针对您的语言的说明创建了要在此示例中使用的规范。

Java

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建以下规范:

    cat << EOF > example-grpc-client.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: example-grpc-client
      namespace: default
      annotations:
        iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-grpc-client
      namespace: default
      labels:
        k8s-app: example-grpc-client
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: example-grpc-client
      strategy: {}
      template:
        metadata:
          annotations:
            security.cloud.google.com/use-workload-certificates: ""
          labels:
            k8s-app: example-grpc-client
        spec:
          containers:
          - image: openjdk:8-jdk
            imagePullPolicy: IfNotPresent
            name: example-grpc-client
            command:
            - /bin/sleep
            - inf
            env:
            - name: GRPC_XDS_BOOTSTRAP
              value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 800m
                memory: 512Mi
              requests:
                cpu: 100m
                memory: 512Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/grpc-xds/
          initContainers:
          - name: grpc-td-init
            image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
            imagePullPolicy: Always
            args:
            - --output
            - "/tmp/bootstrap/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
              requests:
                cpu: 10m
                memory: 100Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/bootstrap/
          serviceAccountName: example-grpc-client
          volumes:
          - name: grpc-td-conf
            emptyDir:
              medium: Memory
    EOF
    

C++

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建以下规范:

    cat << EOF > example-grpc-client.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: example-grpc-client
      namespace: default
      annotations:
        iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-grpc-client
      namespace: default
      labels:
        k8s-app: example-grpc-client
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: example-grpc-client
      strategy: {}
      template:
        metadata:
          annotations:
            security.cloud.google.com/use-workload-certificates: ""
          labels:
            k8s-app: example-grpc-client
        spec:
          containers:
          - image: phusion/baseimage:18.04-1.0.0
            imagePullPolicy: IfNotPresent
            name: example-grpc-client
            command:
            - /bin/sleep
            - inf
            env:
            - name: GRPC_XDS_BOOTSTRAP
              value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 8
                memory: 8Gi
              requests:
                cpu: 300m
                memory: 512Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/grpc-xds/
          initContainers:
          - name: grpc-td-init
            image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
            imagePullPolicy: Always
            args:
            - --output
            - "/tmp/bootstrap/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
              requests:
                cpu: 10m
                memory: 100Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/bootstrap/
          serviceAccountName: example-grpc-client
          volumes:
          - name: grpc-td-conf
            emptyDir:
              medium: Memory
    EOF
    

Python

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建以下规范:

    cat << EOF > example-grpc-client.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: example-grpc-client
      namespace: default
      annotations:
        iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-grpc-client
      namespace: default
      labels:
        k8s-app: example-grpc-client
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: example-grpc-client
      strategy: {}
      template:
        metadata:
          annotations:
            security.cloud.google.com/use-workload-certificates: ""
          labels:
            k8s-app: example-grpc-client
        spec:
          containers:
          - image: phusion/baseimage:18.04-1.0.0
            imagePullPolicy: IfNotPresent
            name: example-grpc-client
            command:
            - /bin/sleep
            - inf
            env:
            - name: GRPC_XDS_BOOTSTRAP
              value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 8
                memory: 8Gi
              requests:
                cpu: 300m
                memory: 512Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/grpc-xds/
          initContainers:
          - name: grpc-td-init
            image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
            imagePullPolicy: Always
            args:
            - --output
            - "/tmp/bootstrap/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
              requests:
                cpu: 10m
                memory: 100Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/bootstrap/
          serviceAccountName: example-grpc-client
          volumes:
          - name: grpc-td-conf
            emptyDir:
              medium: Memory
    EOF
    

Go

  1. 运行以下命令,确保正确设置了项目编号:

    if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
    
  2. 创建以下规范:

    cat << EOF > example-grpc-client.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: example-grpc-client
      namespace: default
      annotations:
        iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-grpc-client
      namespace: default
      labels:
        k8s-app: example-grpc-client
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: example-grpc-client
      strategy: {}
      template:
        metadata:
          annotations:
            security.cloud.google.com/use-workload-certificates: ""
          labels:
            k8s-app: example-grpc-client
        spec:
          containers:
          - image: golang:1.16-alpine
            imagePullPolicy: IfNotPresent
            name: example-grpc-client
            command:
            - /bin/sleep
            - inf
            env:
            - name: GRPC_XDS_BOOTSTRAP
              value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 8
                memory: 8Gi
              requests:
                cpu: 300m
                memory: 512Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/grpc-xds/
          initContainers:
          - name: grpc-td-init
            image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0
            imagePullPolicy: Always
            args:
            - --output
            - "/tmp/bootstrap/td-grpc-bootstrap.json"
            resources:
              limits:
                cpu: 100m
                memory: 100Mi
              requests:
                cpu: 10m
                memory: 100Mi
            volumeMounts:
            - name: grpc-td-conf
              mountPath: /tmp/bootstrap/
          serviceAccountName: example-grpc-client
          volumes:
          - name: grpc-td-conf
            emptyDir:
              medium: Memory
    EOF
    

请按以下方式完成此过程。

  1. 应用规范:

    kubectl apply -f example-grpc-client.yaml
    
  2. 向服务账号授予所需角色:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-client]" \
      ${PROJNUM}-compute@developer.gserviceaccount.com
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-client]" \
      --role roles/trafficdirector.client
    
  3. 验证客户端 pod 正在运行:

    kubectl get pods
    

    该命令会返回类似于以下内容的文本:

    NAMESPACE   NAME                                    READY   STATUS    RESTARTS   AGE
    default     example-grpc-client-7c969bb997-9fzjv    1/1     Running   0          104s
    [..skip..]
    

运行服务器

在您之前创建的服务器 pod 中构建并运行启用 xDS 的 helloworld 服务器。

Java

  1. 获取为 example-grpc-server 服务创建的 pod 的名称:

    kubectl get pods | grep example-grpc-server
    

    您会看到如下所示的反馈:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. 打开一个连接至服务器 pod 的 shell:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. 在 shell 中,验证 /tmp/grpc-xds/td-grpc-bootstrap.json 中的引导文件与引导文件部分中所述的架构匹配。

  4. 下载 gRPC Java 1.42.1 版并构建 xds-hello-world 服务器应用。

    curl -L https://github.com/grpc/grpc-java/archive/v1.42.1.tar.gz | tar -xz
    
    cd grpc-java-1.42.1/examples/example-xds
    
    ../gradlew --no-daemon installDist
    
  5. 使用 --xds-creds 标志运行服务器,以指示启用 xDS 的安全设置,使用 50051 作为侦听端口,xds-server 作为服务器标识名称:

    ./build/install/example-xds/bin/xds-hello-world-server --xds-creds 50051 xds-server
    
  6. 服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:

    Listening on port 50051
    plain text health service listening on port 50052
    

C++

  1. 获取为 example-grpc-server 服务创建的 pod 的名称:

    kubectl get pods | grep example-grpc-server
    

    您会看到如下所示的反馈:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. 打开一个连接至服务器 pod 的 shell:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. 在 shell 中,验证 /tmp/grpc-xds/td-grpc-bootstrap.json 中的引导文件与引导文件部分中所述的架构匹配。

  4. 下载 gRPC C++ 并构建 xds-hello-world 服务器应用。

    apt-get update -y && \
            apt-get install -y \
                build-essential \
                clang \
                python3 \
                python3-dev
    
    curl -L https://github.com/grpc/grpc/archive/master.tar.gz | tar -xz
    
    cd grpc-master
    
    tools/bazel build examples/cpp/helloworld:xds_greeter_server
    
  5. 运行服务器,使用 50051 作为侦听端口,xds_greeter_server 作为服务器标识名称:

    bazel-bin/examples/cpp/helloworld/xds_greeter_server --port=50051 --maintenance_port=50052 --secure
    

    如需不使用凭据运行服务器,您可以指定以下内容:

    bazel-bin/examples/cpp/helloworld/xds_greeter_server --nosecure
    
  6. 服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:

    Listening on port 50051
    plain text health service listening on port 50052
    

Python

  1. 获取为 example-grpc-server 服务创建的 pod 的名称:

    kubectl get pods | grep example-grpc-server
    

    您会看到如下所示的反馈:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. 打开一个连接至服务器 pod 的 shell:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. 在 shell 中,验证 /tmp/grpc-xds/td-grpc-bootstrap.json 中的引导文件与引导文件部分中所述的架构匹配。

  4. 下载 gRPC Python 1.41.0 版并构建示例应用。

    apt-get update -y
    
    apt-get install -y python3 python3-pip
    
    curl -L https://github.com/grpc/grpc/archive/v1.41.x.tar.gz | tar -xz
    
    cd grpc-1.41.x/examples/python/xds/
    
    python3 -m virtualenv venv
    
    source venv/bin/activate
    
    python3 -m pip install -r requirements.txt
    

  5. 使用 --xds-creds 标志运行服务器,以指示启用 xDS 的安全设置,使用 50051 作为侦听端口。

    python3 server.py 50051 --xds-creds
    
  6. 服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:

    2021-05-06 16:10:34,042: INFO     Running with xDS Server credentials
    2021-05-06 16:10:34,043: INFO     Greeter server listening on port 50051
    2021-05-06 16:10:34,046: INFO     Maintenance server listening on port 50052
    

Go

  1. 获取为 example-grpc-server 服务创建的 pod 的名称:

    kubectl get pods | grep example-grpc-server
    

    您会看到如下所示的反馈:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. 打开一个连接至服务器 pod 的 shell:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/sh
    
  3. 在 shell 中,验证 /tmp/grpc-xds/td-grpc-bootstrap.json 中的引导文件与引导文件部分中所述的架构匹配。

  4. 下载 gRPC Go 1.41.0 版,并导航到包含 xds-hello-world 服务器应用的目录。

    apk add curl
    
    curl -L https://github.com/grpc/grpc-go/archive/v1.42.0.tar.gz | tar -xz
    
    cd grpc-go-1.42.0/examples/features/xds/server
    
    
  5. 使用 --xds_creds 标志构建并使用 50051 作为侦听端口来运行服务器以指示启用 xDS 的安全性:

    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -port 50051
    
  6. 服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:

    Using xDS credentials...
    Serving GreeterService on 0.0.0.0:50051 and HealthService on 0.0.0.0:50052
    

健康检查过程需要 3 到 5 分钟,才能显示服务在服务器启动后运行状况良好。

运行客户端并验证该配置

在您之前创建的客户端 pod 中构建并运行启用 xDS 的 helloworld 客户端。

Java

  1. 获取客户端 pod 的名称:

    kubectl get pods | grep example-grpc-client
    

    您会看到如下所示的反馈:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. 打开一个连接至客户端 pod 的 shell:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. 在命令 shell 中,下载 gRPC Java 1.42.1 版并构建 xds-hello-world 客户端应用。

    curl -L https://github.com/grpc/grpc-java/archive/v1.42.1.tar.gz | tar -xz
    
    cd grpc-java-1.42.1/examples/example-xds
    
    ../gradlew --no-daemon installDist
    
  4. 使用 --xds-creds 标志运行客户端,以指示启用 xDS 的安全设置、客户端名称和目标连接字符串:

    ./build/install/example-xds/bin/xds-hello-world-client --xds-creds xds-client \
          xds:///helloworld-gke:8000
    

    您应该会看到如下所示的输出:

    Greeting: Hello xds-client, from xds-server
    

C++

  1. 获取客户端 pod 的名称:

    kubectl get pods | grep example-grpc-client
    

    您会看到如下所示的反馈:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. 打开一个连接至客户端 pod 的 shell:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. 在 shell 中,下载 gRPC C++ 并构建 xds-hello-world 客户端应用。

    apt-get update -y && \
            apt-get install -y \
                build-essential \
                clang \
                python3 \
                python3-dev
    
    curl -L https://github.com/grpc/grpc/archive/master.tar.gz | tar -xz
    
    cd grpc-master
    
    tools/bazel build examples/cpp/helloworld:xds_greeter_client
    
  4. 使用 --xds-creds 标志运行客户端,以指示启用 xDS 的安全设置、客户端名称和目标连接字符串:

    bazel-bin/examples/cpp/helloworld/xds_greeter_client --target=xds:///helloworld-gke:8000
    

    如需不使用凭据运行客户端,请使用以下命令:

    bazel-bin/examples/cpp/helloworld/xds_greeter_client --target=xds:///helloworld-gke:8000 --nosecure
    

    您应该会看到如下所示的输出:

    Greeter received: Hello world
    

Python

  1. 获取客户端 pod 的名称:

    kubectl get pods | grep example-grpc-client
    

    您会看到如下所示的反馈:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. 打开一个连接至客户端 pod 的 shell:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. 进入 shell 后,下载 gRPC Python 1.41.0 版并构建示例客户端应用。

    apt-get update -y
    apt-get install -y python3 python3-pip
    python3 -m pip install virtualenv
    curl -L https://github.com/grpc/grpc/archive/v1.41.x.tar.gz | tar -xz
    cd grpc-1.41.x/examples/python/xds/
    python3 -m virtualenv venv
    source venv/bin/activate
    python3 -m pip install -r requirements.txt
    
  4. 使用 --xds-creds 标志运行客户端,以指示启用 xDS 的安全设置、客户端名称和目标连接字符串:

    python3 client.py xds:///helloworld-gke:8000 --xds-creds
    

    您应该会看到如下所示的输出:

    Greeter client received: Hello you from example-host!
    

Go

  1. 获取客户端 pod 的名称:

    kubectl get pods | grep example-grpc-client
    

    您会看到如下所示的反馈:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. 打开一个连接至客户端 pod 的 shell:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/sh
    
  3. 进入 shell 后,下载 gRPC Go 1.42.0 版,然后转到包含 xds-hello-world 客户端应用的目录。

    apk add curl
    
    curl -L https://github.com/grpc/grpc-go/archive/v1.42.0.tar.gz | tar -xz
    
    cd grpc-go-1.42.0/examples/features/xds/client
    
  4. 使用 --xds_creds 标志构建并运行客户端,以指示已启用 xDS 的安全性、客户端名称和目标连接字符串:

    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -name xds-client \
      -target xds:///helloworld-gke:8000
    

    您应该会看到如下所示的输出:

    Greeting: Hello xds-client, from example-grpc-server-77548868d-l9hmf
    

使用授权政策配置服务级访问权限

授权政策支持需要 gRFC A41 支持。您可以在 github 上找到所需的语言版本

按照以下说明使用授权政策配置服务级层访问权限。在创建授权政策之前,请阅读使用授权限制访问权限中的注意事项。

为了更轻松地验证配置,请创建一个额外的主机名,供客户端用来引用 helloworld-gke 服务。

gcloud compute url-maps add-host-rule grpc-gke-url-map \
   --path-matcher-name grpc-gke-path-matcher \
   --hosts helloworld-gke-noaccess:8000

以下说明会创建一个授权政策,该政策允许主机名为 helloworld-gke:8000 且端口为 50051example-grpc-client 账号发送请求。

gcloud

  1. 通过创建名为 helloworld-gke-authz-policy.yaml 的文件创建授权政策。

    action: ALLOW
    name: helloworld-gke-authz-policy
    rules:
    - sources:
      - principals:
        - spiffe://PROJECT_ID.svc.id.goog/ns/default/sa/example-grpc-client
      destinations:
      - hosts:
        - helloworld-gke:8000
        ports:
        - 50051
    
  2. 导入该政策。

    gcloud network-security authorization-policies import \
      helloworld-gke-authz-policy \
      --source=helloworld-gke-authz-policy.yaml \
      --location=global
    
  3. 通过将以下内容附加到文件 ep-mtls-psms.yaml,将端点政策更新为引用新的授权政策。

    authorizationPolicy: projects/${PROJECT_ID}/locations/global/authorizationPolicies/helloworld-gke-authz-policy
    

    端点政策现在指定,必须对其 gRPC 引导文件包含 app:helloworld 标签的 Pod 的入站请求强制执行 mTLS 和授权政策。

  4. 导入政策:

    gcloud network-services endpoint-policies import ep-mtls-psms \
      --source=ep-mtls-psms.yaml --location=global
    

验证授权政策

按照以下说明确认授权政策是否正常工作。

Java

  1. 打开连接至您之前使用的客户端 pod 的 shell。

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  2. 在命令 shell 中,运行以下命令以验证设置。

    cd grpc-java-1.42.1/examples/example-xds
    ./build/install/example-xds/bin/xds-hello-world-client --xds-creds xds-client \
          xds:///helloworld-gke:8000
    

    您应该会看到如下所示的输出:

    Greeting: Hello xds-client, from xds-server
    
  3. 使用备用服务器名称再次运行客户端。请注意,这是一种失败案例。由于授权政策仅允许访问 helloworld-gke:8000 主机名,因此请求无效。

    ./build/install/example-xds/bin/xds-hello-world-client --xds-creds xds-client \
          xds:///helloworld-gke-noaccess:8000
    

    您应该会看到如下所示的输出:

    WARNING: RPC failed: Status{code=PERMISSION_DENIED}
    

    如果您没有看到此输出,则表示授权政策可能尚未使用。等待几分钟,然后再次尝试执行整个验证过程。

Go

  1. 打开连接至您之前使用的客户端 pod 的 shell。

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  2. 在命令 shell 中,运行以下命令以验证设置。

    cd grpc-go-1.42.0/examples/features/xds/client
    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -name xds-client \
      -target xds:///helloworld-gke:8000
    

    您应该会看到如下所示的输出:

    Greeting: Hello xds-client, from example-grpc-server-77548868d-l9hmf
    
  3. 使用备用服务器名称再次运行客户端。请注意,这是一种失败案例。由于授权政策仅允许访问 helloworld-gke:8000 主机名,因此请求无效。

    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -name xds-client \
      -target xds:///helloworld-gke-noaccess:8000
    

    您应该会看到如下所示的输出:

    could not greet: rpc error: code = PermissionDenied desc = Incoming RPC is not allowed: rpc error: code = PermissionDenied desc = incoming RPC did not match an allow policy
    exit status 1
    

    如果您没有看到此输出,则表示授权政策可能尚未使用。等待几分钟,然后再次尝试执行整个验证过程。

使用 TLS 而非 mTLS

在本示例中使用 TLS 只需稍做更改即可。

  1. ServerTlsPolicy 中,删除 mtlsPolicy

    cat << EOF > server-tls-policy.yaml
    name: "server-tls-policy"
    serverCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    EOF
    
  2. EndpointPolicy 中改为使用此政策:

    cat << EOF > ep-tls-psms.yaml
    name: "ep-mtls-psms"
    type: "GRPC_SERVER"
    serverTlsPolicy: "projects/${PROJECT_ID}/locations/global/serverTlsPolicies/server-tls-policy"
    trafficPortSelector:
      ports:
      - "50051"
    endpointMatcher:
      metadataLabelMatcher:
        metadataLabelMatchCriteria: "MATCH_ALL"
        metadataLabels: []
    EOF
    
  3. mTLS 的 ClientTlsPolicy 也适用于 TLS 的情况,但政策的 clientCertificate 部分可以删除,因为 TLS 不需要该部分:

    cat << EOF > client-tls-policy.yaml
    name: "client-tls-policy"
    serverValidationCa:
    - certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    EOF
    

将服务安全性和 Wallet 示例结合使用

本部分简要介绍了如何为 Java、C++ 和 Go 启用服务安全性和 Wallet 示例

Java

您可以在 github 中找到 Java 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannelXdsServer 凭据。

以下说明介绍了如何使用 Go 配置 Wallet 示例。此过程与 Java 类似。这些说明使用从 Google Cloud 容器代码库获取的已有 Docker 映像。

如需创建此示例,请按照以下说明操作:

  1. 克隆代码库并获取 gRPC 示例目录中的文件。
  2. 修改文件 00-common-env.sh。注释掉将 WALLET_DOCKER_IMAGE 的值设置为 Go Docker 映像的现有行,并取消注释将 WALLET_DOCKER_IMAGE 的值设置为 Java Docker 映像的行。
  3. 按照创建和配置 Cloud Router 实例中的说明,或使用脚本 10.apis.sh 中的函数 create_cloud_router_instances 创建和配置 Cloud Router 实例。
  4. 使用 hello world 示例说明或脚本 20-cluster.sh 中的函数 create_cluster 创建集群。
  5. 按照 CA 服务说明或使用脚本 30-private-ca-setup.sh 创建私有证书授权机构。
  6. 使用脚本 40-k8s-resources.sh 为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:accountstatsstats_premiumwallet_v1wallet_v2
  7. 对于您创建的每项服务,请使用脚本 50-td-components.sh 中的 create_health_checkcreate_backend_service 创建健康检查和后端服务。
  8. 在脚本 60-routing-components.sh 中使用 create_routing_components 创建 Cloud Service Mesh 路由组件。
  9. 使用脚本 70-security-components.sh 中的 create_security_components 为每个后端服务创建 Cloud Service Mesh 安全组件。
  10. 使用脚本 75-client-deployment.sh 中的 create_client_deployment 创建 Wallet 客户端部署。
  11. 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。

C++

您可以在 github 找到 C++ 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannelXdsServer 凭据。

以下说明介绍了如何使用 Go 配置 Wallet 示例。此过程与 C++ 类似。这些说明使用从 Google Cloud 容器代码库获取的现有 Docker 映像。

如需创建此示例,请按照以下说明操作:

  1. 克隆代码库并获取 gRPC 示例目录中的文件。
  2. 修改文件 00-common-env.sh。注释掉将 WALLET_DOCKER_IMAGE 的值设置为 Go Docker 映像的现有行,并取消注释将 WALLET_DOCKER_IMAGE 的值设置为 C++ Docker 映像的行。
  3. 按照创建和配置 Cloud Router 实例中的说明,或使用脚本 10.apis.sh 中的函数 create_cloud_router_instances 创建和配置 Cloud Router 实例。
  4. 使用 hello world 示例说明或脚本 20-cluster.sh 中的函数 create_cluster 创建集群。
  5. 按照 CA 服务说明或使用脚本 30-private-ca-setup.sh 创建私有证书授权机构。
  6. 使用脚本 40-k8s-resources.sh 为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:accountstatsstats_premiumwallet_v1wallet_v2
  7. 对于您创建的每项服务,请使用脚本 50-td-components.sh 中的 create_health_checkcreate_backend_service 创建健康检查和后端服务。
  8. 在脚本 60-routing-components.sh 中使用 create_routing_components 创建 Cloud Service Mesh 路由组件。
  9. 使用脚本 70-security-components.sh 中的 create_security_components 为每个后端服务创建 Cloud Service Mesh 安全组件。
  10. 使用脚本 75-client-deployment.sh 中的 create_client_deployment 创建 Wallet 客户端部署。
  11. 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。

Go

您可以在 github 找到 Go 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannelXdsServer 凭据。

这些说明使用从 Google Cloud 容器代码库获取的已有 Docker 映像。

如需创建此示例,请按照以下说明操作:

  1. 克隆代码库并获取 gRPC 示例目录中的文件。
  2. 修改文件 00-common-env.sh 以正确设置环境变量的值。
  3. 按照创建和配置 Cloud Router 实例中的说明,或使用脚本 10.apis.sh 中的函数 create_cloud_router_instances 创建和配置 Cloud Router 实例。
  4. 使用 hello world 示例说明或脚本 20-cluster.sh 中的函数 create_cluster 创建集群。
  5. 按照 CA 服务说明或使用脚本 30-private-ca-setup.sh 创建私有证书授权机构。
  6. 使用脚本 40-k8s-resources.sh 为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:accountstatsstats_premiumwallet_v1wallet_v2
  7. 对于您创建的每项服务,请使用脚本 50-td-components.sh 中的 create_health_checkcreate_backend_service 创建健康检查和后端服务。
  8. 在脚本 60-routing-components.sh 中使用 create_routing_components 创建 Cloud Service Mesh 路由组件。
  9. 使用脚本 70-security-components.sh 中的 create_security_components 为每个后端服务创建 Cloud Service Mesh 安全组件。
  10. 使用脚本 75-client-deployment.sh 中的 create_client_deployment 创建 Wallet 客户端部署。
  11. 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。

引导文件

本指南中的设置过程使用引导生成器来创建所需的引导文件。本部分提供有关引导文件本身的参考信息。

引导文件包含无代理 gRPC 代码所需的配置信息,包括 xDS 服务器的连接信息。引导文件包含无代理 gRPC 安全功能所需的安全配置。gRPC 服务器还需要一个额外的字段,如以下部分所述。引导文件示例如下所示:

{
  "xds_servers": [
    {
      "server_uri": "trafficdirector.googleapis.com:443",
      "channel_creds": [
        {
          "type": "google_default"
        }
      ],
      "server_features": [
        "xds_v3"
      ]
    }
  ],
  "node": {
    "cluster": "cluster",
    "id": "projects/9876012345/networks/default/nodes/client1",
    "metadata": {
      "TRAFFICDIRECTOR_GCP_PROJECT_NUMBER": "9876012345",
      "TRAFFICDIRECTOR_NETWORK_NAME": "default",
      "INSTANCE_IP": "10.0.0.3"
    },
    "locality": {
      "zone": "us-central1-a"
    }
  },
  "server_listener_resource_name_template": "grpc/server?xds.resource.listening_address=%s",
  "certificate_providers": {
    "google_cloud_private_spiffe": {
      "plugin_name": "file_watcher",
      "config": {
        "certificate_file": "/var/run/secrets/workload-spiffe-credentials/certificates.pem",
        "private_key_file": "/var/run/secrets/workload-spiffe-credentials/private_key.pem",
        "ca_certificate_file": "/var/run/secrets/workload-spiffe-credentials/ca_certificates.pem",
        "refresh_interval": "600s"
      }
    }
  }
}

针对安全服务的引导文件更新

以下字段反映了与安全和 xDS v3 用法相关的修改:

node 内的 id 字段向 Cloud Service Mesh 提供 gRPC 客户端的唯一身份。您必须按以下格式使用节点 ID 提供 Google Cloud 项目编号和网络名称:

projects/{project number}/networks/{network name}/nodes/[UNIQUE_ID]

以下是使用项目编号 1234 和默认网络的示例:

projects/1234/networks/default/nodes/client1

INSTANCE_IP 字段是 pod 的 IP 地址,或者为 0.0.0.0,表示 INADDR_ANY。gRPC 服务器使用此字段从 Cloud Service Mesh 提取监听器资源,用于服务器端安全设置。

引导文件中的安全配置字段

JSON 键 类型 备注
server_listener_resource_name_template 字符串 grpc/server?xds.resource.listening_address=%s 对于 gRPC 服务器是必需的。gRPC 使用此值构建资源名称,以从 Cloud Service Mesh 提取 `Listener` 资源,用于服务器端安全和其他配置。gRPC 使用此值构成资源名称字符串
certificate_providers JSON struct google_cloud_private_spiffe 必需。此值是一个 JSON struct,表示名称与证书提供商实例的映射。证书提供商实例用于提取身份和根证书。示例引导文件包含名称 google_cloud_private_spiffe,其值为证书提供商实例 JSON struct。每个证书提供商实例 JSON struct 都有两个字段:
  • plugin_name。必填值,用于标识 gRPC 的插件架构要求使用的证书提供商插件。gRPC 内置了对此设置中使用的 file-watcher 插件的支持。plugin_name 为 file_watcher
  • config。必填值,用于标识 file_watcher 插件的 JSON 配置。架构和内容取决于插件。

file_watcher 插件的 config JSON 结构的内容如下:

  • certificate_file:必需的字符串。此值是身份证书的位置。
  • private_key_file:必需的字符串。此值是私钥文件的位置,应与身份证书匹配。
  • ca_certificate_file:必需的字符串。此值是根证书(也称为信任软件包)的位置。
  • refresh_interval:可选字符串。此值表示使用“时长”的 JSON 映射的字符串表示形式指定的刷新间隔。默认值为“600s”,即 10 分钟的时长。

引导生成器

您可从 gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.16.0 获取引导生成器容器映像,您可以从 https://github.com/GoogleCloudPlatform/traffic-director-grpc-bootstrap 获取其源代码。最常用的命令行选项包括:

  • --output:使用此选项指定输出引导文件的写入位置,例如,--output /tmp/bootstrap/td-grpc-bootstrap.json 命令将引导文件生成到 pod 的文件系统中的 /tmp/bootstrap/td-grpc-bootstrap.json
  • --node-metadata:使用此标志在引导文件中填充节点元数据。在 EndpointPolicy 中使用元数据标签匹配器时此选项是必需的,其中 Cloud Service Mesh 使用引导文件的节点元数据部分中提供的标签数据。此参数以 key=value 格式提供,例如:--node-metadata version=prod --node-metadata type=grpc

这些选项会在引导文件的节点元数据部分中添加以下内容:

{
  "node": {
...
    "metadata": {
      "version": "prod",
      "type": "grpc",
...
    },
...
  },
...
}

删除部署

您可以选择运行这些命令,删除使用本指南创建的部署。

如需删除集群,请运行以下命令:

gcloud container clusters delete CLUSTER_NAME --zone ZONE --quiet

如需删除您创建的资源,请运行以下命令:

gcloud compute backend-services delete grpc-gke-helloworld-service --global --quiet
cloud compute network-endpoint-groups delete example-grpc-server --zone ZONE --quiet
gcloud compute firewall-rules delete grpc-gke-allow-health-checks --quiet
gcloud compute health-checks delete grpc-gke-helloworld-hc --quiet
gcloud network-services endpoint-policies delete ep-mtls-psms \
    --location=global --quiet
gcloud network-security authorization-policies delete helloworld-gke-authz-policy \
   --location=global --quiet
gcloud network-security client-tls-policies delete client-mtls-policy \
    --location=global --quiet
gcloud network-security server-tls-policies delete server-tls-policy \
    --location=global --quiet
gcloud network-security server-tls-policies delete server-mtls-policy \
    --location=global --quiet

问题排查

以下说明可帮助您解决安全部署问题。

工作负载无法从 Cloud Service Mesh 获取配置

如果您看到类似以下内容的错误:

PERMISSION_DENIED: Request had insufficient authentication scopes.

为此,您需要确保实现以下各项安排:

  • 使用参数 --scopes=cloud-platform 创建了 GKE 集群。
  • 已将 roles/trafficdirector.client 分配给 Kuberneters 服务账号。
  • 已将 roles/trafficdirector.client 分配给默认 Google Cloud 服务账号(上文的 ${GSA_EMAIL})。
  • 启用了 trafficdirector.googleapis.com 服务 (API)。

即使 Cloud Service Mesh 配置正确,gRPC 服务器也不使用 TLS 或 mTLS

请务必在端点政策配置中指定 GRPC_SERVER。如果指定 SIDECAR_PROXY,gRPC 会忽略配置。

无法使用所请求的集群版本创建 GKE 集群

GKE 集群创建命令可能失败,并显示如下错误:

Node version "1.20.5-gke.2000" is unsupported.

请确保在集群创建命令中使用了 --release-channel rapid 参数。您需要使用快速发布版本来获得此版本的正确版本。

显示 No usable endpoint 错误

如果客户端因 No usable endpoint 错误而无法与服务器通信,健康检查程序可能已将服务器后端标记为健康状况不佳。如需检查后端的运行状况,请运行此 gcloud 命令:

gcloud compute backend-services get-health grpc-gke-helloworld-service --global

如果该命令返回后端状态为运行状况不佳,则可能是以下某个原因导致的:

  • 未创建防火墙或者防火墙未包含正确的源 IP 地址范围。
  • 防火墙上的目标标记与您创建的集群上的标记不匹配。

工作负载无法在安全设置中通信

如果在您为无代理服务网格设置安全机制后,工作负载无法通信,请按照以下说明确定原因。

  1. 停用无代理安全机制,并消除无代理服务网格负载平衡使用场景中的问题。要停用网格中的安全机制,请执行以下操作之一:
    1. 在客户端和服务器端使用明文凭据,或者
    2. 不要在 Cloud Service Mesh 配置中为后端服务和端点政策配置安全机制。

请按照对无代理 Cloud Service Mesh 部署进行问题排查中的步骤操作,因为部署中没有安全设置。

  1. 修改工作负载,使用明文形式的 xDS 凭据或者不安全凭据作为回退凭据。如前所述,使 Cloud Service Mesh 配置停用安全机制。在这种情况下,虽然 gRPC 允许 Cloud Service Mesh 配置安全机制,Cloud Service Mesh 也不会发送安全信息,此时,gRPC 应该回退到明文(或不安全)凭据,其工作原理与前面所述的第一种情况类似。如果不起作用,请执行以下操作:

    1. 提高客户端和服务器端的日志记录级别,以便您可以看到 gRPC 和 Cloud Service Mesh 之间交换的 xDS 消息。
    2. 确保 Cloud Service Mesh 在发送给工作负载的 CDS 和 LDS 响应中没有启用安全机制。
    3. 确保工作负载未在其通道中使用 TLS 或 mTLS 模式。如果您看到任何与 TLS 握手相关的日志消息,请检查应用源代码,并确保使用不安全或明文凭据作为回退凭据。如果应用源代码正确无误,则可能是 gRPC 库中的错误
  2. 按照该用户指南中的问题排查步骤,验证 CA Service 与 GKE 的集成可正确用于您的 GKE 集群。确保该功能提供的证书和密钥在指定的目录 /var/run/secrets/workload-spiffe-credentials/ 中可用。

  3. 如上文所述在网格中启用 TLS(而非 mTLS),然后重启客户端和服务器工作负载。

    1. 提高客户端和服务器端的日志记录级别,以便您可以看到 gRPC 和 Cloud Service Mesh 之间交换的 xDS 消息。
    2. 确保 Cloud Service Mesh 在发送给工作负载的 CDS 和 LDS 响应中启用了安全机制。

客户端失败并显示 CertificateException 和消息 Peer certificate SAN check failed

这表示 SecuritySettings 消息中的 subjectAltNames 值存在问题。请注意,这些值基于您为后端服务创建的 Kubernetes 服务。您创建的每个此类 Kubernetes 服务都有一个相关联的 SPIFFE ID,格式如下:

spiffe://${WORKLOAD_POOL}/ns/${K8S_NAMESPACE}/sa/${SERVICE_ACCOUNT}

这些值是:

  • WORKLOAD_POOL:集群的工作负载池,即 ${PROJECT_ID}.svc.id.goog
  • K8S_NAMESPACE:您在服务部署中使用的 Kubernetes 命名空间
  • SERVICE_ACCOUNT:您在服务部署中使用的 Kubernetes 服务账号

对于作为网络端点组附加到后端服务的每个 Kubernetes 服务,请确保正确计算 SPIFFE ID,并将该 SPIFFE ID 添加到 SecuritySettings 消息的 subjectAltNames 字段。

应用无法将 mTLS 证书用于 gRPC 库

如果应用无法将 mTLS 证书用于 gRPC 库,请执行以下操作:

  1. 验证 pod 规范包含使用 NEG 创建无代理 gRPC 服务中所述的 security.cloud.google.com/use-workload-certificates 注释。

  2. 验证可以从 pod 中访问以下路径中的包含证书链和叶证书的文件、私钥和可信 CA 证书:

    1. 证书链以及叶证书:“/var/run/secrets/workload-spiffe-credentials/certificates.pem”
    2. 私钥:“/var/run/secrets/workload-spiffe-credentials/private_key.pem”
    3. CA 软件包:“/var/run/secrets/workload-spinff-credentials/ca_certificates.pem”
  3. 如果上一步中的证书不可用,请执行以下操作:

      gcloud privateca subordinates describe SUBORDINATE_CA_POOL_NAME 
    --location=LOCATION

    1. 验证 GKE 的控制层面具有正确的 IAM 角色绑定,从而授予其访问 CA Service 的权限:

      # Get the IAM policy for the CA
      gcloud privateca roots get-iam-policy ROOT_CA_POOL_NAME
      
      # Verify that there is an IAM binding granting access in the following format
      - members:
      - serviceAccount:service-projnumber@container-engine-robot.iam.gserviceaccount.com
      role: roles/privateca.certificateManager
      
      # Where projnumber is the project number (e.g. 2915810291) for the GKE cluster.
      
    2. 验证证书未过期。这是位于 /var/run/secrets/workload-spiffe-credentials/certificates.pem 的证书链和叶证书。要进行检查,请运行以下命令:

      cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
      

    3. 通过运行以下命令验证您的应用支持该密钥类型:

      cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
      

    4. 验证您的 gRPC Java 应用在 WorkloadCertificateConfig YAML 文件中具有以下 keyAlgorithm

      keyAlgorithm:
        rsa:
          modulusSize: 4096
    
  4. 验证 CA 与证书密钥使用相同的密钥系列。

应用的证书被客户端、服务器或对等应用拒绝

  1. 验证对等应用使用同一信任软件包来验证证书。
  2. 验证所用证书未过期(证书链以及叶证书:“/var/run/secrets/workload-spinff-credentials/certificates.pem”)。

Pod 仍处于待处理状态

如果 Pod 在设置过程中保持待处理状态,请增加部署规范中 Pod 的 CPU 和内存资源。

无法使用 --enable-mesh-certificates 标志创建集群

确保您运行的是最新版本的 gcloud CLI:

gcloud components update

请注意,--enable-mesh-certificates 标志仅适用于 gcloud beta

pod 无法启动

如果证书预配失败,使用 GKE 网格证书的 pod 可能无法启动。在以下情况下可能发生此问题:

  • WorkloadCertificateConfigTrustConfig 配置错误或缺失。
  • CSR 未被批准。

您可以通过检查 pod 事件来检查证书预配是否失败。

  1. 检查 pod 的状态:

    kubectl get pod -n POD_NAMESPACE POD_NAME
    

    请替换以下内容:

    • POD_NAMESPACE:您的 pod 的命名空间。
    • POD_NAME:您的 pod 的名称。
  2. 检查 pod 的近期事件:

    kubectl describe pod -n POD_NAMESPACE POD_NAME
    
  3. 如果证书预配失败,您将看到一个事件具有 Type=WarningReason=FailedMountFrom=kubelet 以及以 MountVolume.SetUp failed for volume "gke-workload-certificates" 开头的 Message 字段。Message 字段包含问题排查信息。

    Events:
      Type     Reason       Age                From       Message
      ----     ------       ----               ----       -------
      Warning  FailedMount  13s (x7 over 46s)  kubelet    MountVolume.SetUp failed for volume "gke-workload-certificates" : rpc error: code = Internal desc = unable to mount volume: store.CreateVolume, err: unable to create volume "csi-4d540ed59ef937fbb41a9bf5380a5a534edb3eedf037fe64be36bab0abf45c9c": caPEM is nil (check active WorkloadCertificateConfig)
    
  4. 如果 pod 无法启动的原因是对象配置错误或 CSR 被拒,请参阅以下问题排查步骤。

WorkloadCertificateConfigTrustConfig 配置错误

确保您已正确创建 WorkloadCertificateConfigTrustConfig 对象。您可以使用 kubectl 诊断这两个对象的配置错误。

  1. 检索当前状态。

    对于 WorkloadCertificateConfig

    kubectl get WorkloadCertificateConfig default -o yaml
    

    对于 TrustConfig

    kubectl get TrustConfig default -o yaml
    
  2. 检查状态输出。有效对象的条件将包含 type: Readystatus: "True"

    status:
      conditions:
      - lastTransitionTime: "2021-03-04T22:24:11Z"
        message: WorkloadCertificateConfig is ready
        observedGeneration: 1
        reason: ConfigReady
        status: "True"
        type: Ready
    

    无效对象则会显示 status: "False"reasonmessage 字段包含更多问题排查详细信息。

CSR 未被批准

如果 CSR 审批流程中出现问题,您可以在 CSR 的 type: Approvedtype: Issued 条件中查看错误详细信息。

  1. 使用 kubectl 列出相关 CSR:

    kubectl get csr \
      --field-selector='spec.signerName=spiffe.gke.io/spiffe-leaf-signer'
    
  2. 选择一个 Approved 并且未 Issued,或者未 Approved 的 CSR。

  3. 使用 kubectl 获取所选 CSR 的详细信息:

    kubectl get csr CSR_NAME -o yaml
    

    CSR_NAME 替换为您选择的 CSR 的名称。

有效 CSR 的条件包含 type: Approvedstatus: "True",且 status.certificate 字段中为有效证书:

status:
  certificate: <base64-encoded data>
  conditions:
  - lastTransitionTime: "2021-03-04T21:58:46Z"
    lastUpdateTime: "2021-03-04T21:58:46Z"
    message: Approved CSR because it is a valid SPIFFE SVID for the correct identity.
    reason: AutoApproved
    status: "True"
    type: Approved

messagereason 字段中会显示无效 CSR 的问题排查信息。

pod 缺少证书

  1. 获取 pod 的 pod 规范:

    kubectl get pod -n POD_NAMESPACE POD_NAME -o yaml
    

    请替换以下内容:

    • POD_NAMESPACE:您的 pod 的命名空间。
    • POD_NAME:您的 pod 的名称。
  2. 验证 pod 规范包含配置 pod 以接收 mTLS 凭据中所述的 security.cloud.google.com/use-workload-certificates 注释。

  3. 验证 GKE 网格证书准入控制器是否已将 workloadcertificates.security.cloud.google.com 类型的 CSI 驱动程序卷成功注入到 pod 规范中:

    volumes:
    ...
    -csi:
      driver: workloadcertificates.security.cloud.google.com
      name: gke-workload-certificates
    ...
    
  4. 检查每个容器中是否存在卷装载:

    containers:
    - name: ...
      ...
      volumeMounts:
      - mountPath: /var/run/secrets/workload-spiffe-credentials
        name: gke-workload-certificates
        readOnly: true
      ...
    
  5. 验证 pod 中的以下位置提供以下证书软件包和私钥:

    • 证书链软件包:/var/run/secrets/workload-spiffe-credentials/certificates.pem
    • 私钥:/var/run/secrets/workload-spiffe-credentials/private_key.pem
    • CA 信任锚软件包:/var/run/secrets/workload-spiffe-credentials/ca_certificates.pem
  6. 如果文件不可用,请执行以下步骤:

    1. 检索集群的 CA Service(预览版)实例:

      kubectl get workloadcertificateconfigs default -o jsonpath '{.spec.certificateAuthorityConfig.certificateAuthorityServiceConfig.endpointURI}'
      
    2. 检索 CA Service(预览版)实例的状态:

      gcloud privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \
        --location ISSUING_CA_LOCATION
      

      请替换以下内容:

      • ISSUING_CA_TYPE:发证 CA 类型,必须为 subordinatesroots
      • ISSUING_CA_NAME:发证 CA 的名称。
      • ISSUING_CA_LOCATION:发证 CA 的区域。
    3. 获取根 CA 的 IAM 政策:

      gcloud privateca roots get-iam-policy ROOT_CA_NAME
      

      ROOT_CA_NAME 替换为您的根 CA 的名称。

    4. 在 IAM 政策中,验证存在 privateca.auditor 政策绑定:

      ...
      - members:
        - serviceAccount:service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
        role: roles/privateca.auditor
      ...
      

      在此示例中,PROJECT_NUMBER 是您的集群的项目编号。

    5. 获取从属 CA 的 IAM 政策:

      gcloud privateca subordinates get-iam-policy SUBORDINATE_CA_NAME
      

      SUBORDINATE_CA_NAME 替换为从属 CA 名称。

    6. 在 IAM 政策中,验证存在 privateca.certificateManager 政策绑定:

      ...
      - members:
        - serviceAccount: service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
        role: roles/privateca.certificateManager
      ...
      

      在此示例中,PROJECT_NUMBER 是您的集群的项目编号。

应用无法使用已颁发的 mTLS 凭据

  1. 验证证书未过期:

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
    
  2. 检查应用是否支持您使用的密钥类型。

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
    
  3. 检查发证 CA 是否使用与证书密钥相同的密钥系列。

    1. 获取 CA Service(预览版)实例的状态:

      gcloud privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \
        --location ISSUING_CA_LOCATION
      

      请替换以下内容:

      • ISSUING_CA_TYPE:发证 CA 类型,必须为 subordinatesroots
      • ISSUING_CA_NAME:发证 CA 的名称。
      • ISSUING_CA_LOCATION:发证 CA 的区域。
    2. 检查输出中的 keySpec.algorithm 是否与您在 WorkloadCertificateConfig YAML 清单中定义的密钥算法相同。输出如下所示:

      config:
        ...
        subjectConfig:
          commonName: td-sub-ca
          subject:
            organization: TestOrgLLC
          subjectAltName: {}
      createTime: '2021-05-04T05:37:58.329293525Z'
      issuingOptions:
        includeCaCertUrl: true
      keySpec:
        algorithm: RSA_PKCS1_2048_SHA256
       ...
      

证书遭拒

  1. 验证对等应用使用同一信任软件包来验证证书。
  2. 验证证书未过期:

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
    
  3. 如果未使用 gRPC Go Credentials Reloading API,验证客户端代码是否定期刷新来自文件系统的凭据。

  4. 验证工作负载与 CA 位于同一信任网域。GKE 网格证书支持单个信任网域中的工作负载之间的通信。

限制

只有 GKE 支持 Cloud Service Mesh 服务安全。您无法使用 Compute Engine 部署服务安全。

如果有两个或更多端点政策资源与同一个端点同等匹配(例如,两个政策具有相同的标签和端口,或者两个或更多政策具有不同的标签,但这些标签与同一个端点的标签同等匹配),Cloud Service Mesh 不支持这一场景。如需详细了解端点政策如何与端点标签匹配,请参阅 EndpointPolicy.EndpointMatcher.MetadataLabelMatcher 的 API。在这种情况下,Cloud Service Mesh 不会根据任何冲突的政策生成安全配置。