使用无代理 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 安全所需的标志和值。如需了解详情,请参阅引导文件部分,其中包含示例引导文件。

设置概览

此设置流程是 Cloud Service Mesh 设置 GKE 和无代理 gRPC 服务实现连接。会在适用的位置引用该设置过程的现有未修改步骤。

对使用 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 和其他 API 是实现无代理 gRPC 服务网格安全性的必要条件。

    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 Security 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”指定的值 必须与传递给 --target-tags 标志的 部分中的 firewall-rules create 命令 使用 Cloud Load Balancing 组件配置 Cloud Service Mesh

    # 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 Cloud 控制台中创建路由规则映射的方式类似。 使用 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. 在名为 的临时文件中创建客户端 TLS 政策资源内容 client-mtls-policy.yaml

    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. 使用以下命令创建 Cloud Service Mesh 路由组件 脚本 60-routing-components.sh 中的 create_routing_components
  9. 为每个后端服务创建 Cloud Service Mesh 安全组件 在脚本 70-security-components.sh 中使用 create_security_components
  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. 使用以下命令创建 Cloud Service Mesh 路由组件 脚本 60-routing-components.sh 中的 create_routing_components
  9. 为每个后端服务创建 Cloud Service Mesh 安全组件 在脚本 70-security-components.sh 中使用 create_security_components
  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. 使用以下命令创建 Cloud Service Mesh 路由组件 脚本 60-routing-components.sh 中的 create_routing_components
  9. 为每个后端服务创建 Cloud Service Mesh 安全组件 在脚本 70-security-components.sh 中使用 create_security_components
  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 不会发送安全信息 应回退到有效的纯文本(或不安全)凭据 这与之前介绍的第一种情况类似。如果上述情况不奏效 执行以下操作:

    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 不会生成安全配置 来自任何存在冲突的政策