使用无代理 gRPC 设置服务安全性
本指南介绍如何为无代理 gRPC 服务网格配置安全服务。
要求
在为 gRPC 代理无服务网格配置服务安全之前,请确保满足以下要求。
- 您的部署符合准备使用 Envoy 和无代理工作负载设置服务路由 API 中的要求。
- 必须使用 xDS v3。
- 您可以使用以下任一语言访问所需的 xDS 版本和证书提供商功能:
- gRPC Java
- gRPC C++
- gRPC Python
- gRPC Go 您可以在 github 上找到所需的语言版本
- 您可以访问引导生成器 0.16.0 版。引导生成器映像位于 Google Cloud 容器代码库中。
- 满足 gRPC 无代理服务网格负载均衡的所有前提条件。
- 您拥有足够的权限来创建或更新 Cloud Service Mesh 和 Google Cloud 服务网格资源以使用 PSM 安全。如需了解所需权限的完整信息,请参阅准备使用无代理 gRPC 服务设置 Cloud Service Mesh。
- 您拥有使用 Certificate Authority Service 所需的权限,如为颁发证书创建证书授权机构中所述
配置身份和访问权限管理
您必须具有 使用 Google Kubernetes Engine 所需的权限。您必须至少具有以下角色:
roles/container.clusterAdmin
GKE 角色roles/compute.instanceAdmin
Compute Engine 角色roles/iam.serviceAccountUser
角色
如要创建设置所需的资源,您必须具有 compute.NetworkAdmin
角色。此角色包含创建、更新、删除、列出和使用所需资源(即在其他资源中引用)的所有必要权限。如果您是项目的所有者编辑者,则会自动拥有此角色。
请注意,在后端服务资源中引用这些资源时,系统不会强制执行 networksecurity.googleapis.com.clientTlsPolicies.use
和 networksecurity.googleapis.com.serverTlsPolicies.use
。
如果这是将来强制执行的,并且您使用的是 compute.NetworkAdmin
角色,则在强制执行此检查时不会注意到任何问题。
如果您使用的是自定义角色,并且此检查将强制执行,那么您必须确保添加相应的 .use
权限。否则,将来您可能会发现自定义角色没有必要的权限,无法引用后端服务中的 clientTlsPolicy
或 serverTlsPolicy
。
准备设置
无代理服务网格 (PSM) 安全性为根据无代理 gRPC 服务文档设置的负载均衡服务网格增加了安全性。在无代理服务网格中,gRPC 客户端在 URI 中使用架构 xds:
来访问服务,这将启用 PSM 负载均衡和端点发现功能。
将 gRPC 客户端和服务器更新为正确的版本
使用您的语言支持的最低 gRPC 版本构建或重建您的应用。
更新引导文件
gRPC 应用使用单个引导文件,该文件必须包含 gRPC 客户端和服务器端代码所需的所有字段。引导生成器会自动生成引导文件,其中包含 PSM 安全所需的标志和值。如需了解详情,请参阅引导文件部分,其中包含示例引导文件。
设置概览
此设置过程是对使用 GKE 和无代理 gRPC 服务设置 Cloud Service Mesh 的扩展,会在适用的位置引用该设置过程的现有未修改步骤。
对使用 GKE 设置 Cloud Service Mesh 的主要改进如下:
- 设置 CA Service,在其中创建私有 CA 池和所需的证书授权机构。
- 创建具有 GKE Workload Identity Federation for GKE 和网格证书功能和 CA Service 集成的 GKE 集群。
- 在集群上配置网格证书颁发。
- 创建客户端和服务器服务账号。
- 设置使用 xDS API 和 xDS 服务器凭据从 Cloud Service Mesh 获取安全配置的示例服务器。
- 设置使用 xDS 凭据的示例客户端。
- 更新 Cloud Service Mesh 配置以包含安全配置。
您可以在以下位置查看使用 xDS 凭据的代码示例:
更新 Google Cloud CLI
如需更新 Google Cloud CLI,请运行以下命令:
gcloud components update
设置环境变量
在本指南中,您将使用 Cloud Shell 命令,命令中的重复信息由各种环境变量表示。在执行命令之前,将 Shell 环境中的以下环境变量设置为您的特定值。每个注释行说明关联的环境变量的含义。
# Your project ID PROJECT_ID=PROJECT_ID # GKE cluster name and zone for this example. CLUSTER_NAME=CLUSTER_NAME ZONE=ZONE gcloud config set compute/zone $ZONE # 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 的访问权限。
运行以下命令,启用 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
运行以下命令,允许默认服务账号访问 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 集群,如创建证书授权机构以颁发证书中所述。
创建使用适用于 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
集群创建可能需要几分钟才能完成。
如果您使用的是现有集群,请开启适用于 GKE 的工作负载身份联合和 GKE 网格证书。确保使用
--enable-ip-alias
标志创建集群,该标志无法与update
命令一起使用。gcloud container clusters update CLUSTER_NAME \ --enable-mesh-certificates
运行以下命令切换到作为
kubectl
命令的默认集群的新集群:gcloud container clusters get-credentials CLUSTER_NAME \ --zone ZONE
向舰队注册集群
向舰队注册您在创建 GKE 集群中创建或更新的集群。通过注册集群,您可以更轻松地跨多个项目配置集群。
请注意,每个步骤最长可能需要十分钟才能完成。
向舰队注册集群:
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.
将生成的清单文件应用于您的集群:
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
从集群获取成员资格资源:
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
将 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
将 CA 角色
role/privateca.admin
授予需要修改 IAM 政策的个人,其中MEMBER
是需要此访问权限的个人,具体而言是执行以下步骤的个人这些角色可授予privateca.auditor
和privateca.certificateManager
角色:gcloud projects add-iam-policy-binding PROJECT_ID \ --member=MEMBER \ --role=roles/privateca.admin
创建根 CA 服务池。
gcloud privateca pools create ROOT_CA_POOL_NAME \ --location ROOT_CA_POOL_LOCATION \ --tier enterprise
创建根 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"
创建从属池和从属 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
授予根 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"
授予从属 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"
保存以下
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
- 运行您的集群的项目的 ID:
保存以下
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
- 运行您的集群的项目的 ID:
将配置应用到您的集群:
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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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++
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
请按以下方式完成此过程。
应用规范:
kubectl apply -f example-grpc-server.yaml
向服务账号授予所需角色:
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
运行以下命令验证服务和 Pod 是否已正确创建:
kubectl get deploy/example-grpc-server kubectl get svc/example-grpc-server
验证 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 握手失败时,会产生到服务器的不必要流量。因此,我们建议您使用第一种方法。
创建健康检查。 请注意,只有创建并启动服务器后,健康检查才会启动。
如果要为健康检查创建指定的服务端口,即我们推荐的方法,请使用以下命令:
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
创建防火墙。确保
--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
创建后端服务:
gcloud compute backend-services create grpc-gke-helloworld-service \ --global \ --load-balancing-scheme=INTERNAL_SELF_MANAGED \ --protocol=GRPC \ --health-checks grpc-gke-helloworld-hc
将 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
创建 Mesh
和 GRPCRoute
资源
这与设置无代理 gRPC 服务中设置 Mesh
和 GRPCRoute
资源的方式类似。
创建
Mesh
规范并将其保存到名为mesh.yaml
的文件中。name: grpc-mesh
从规范导入
Mesh
资源。gcloud network-services meshes import grpc-mesh \ --source=mesh.yaml \ --location=global
创建
GRPCRoute
规范并将其保存到名为grpc_route.yaml
的文件中。name: helloworld-grpc-route hostnames: - helloworld-gke:8000 meshes: - projects/PROJECT_NUMBER/locations/global/meshes/grpc-mesh rules: - action: destinations: - serviceName: projects/PROJECT_NUMBER/locations/global/backendServices/grpc-gke-helloworld-service
从
grpc_route.yaml
规范导入GRPCRoute
资源。gcloud network-services grpc-routes import helloworld-grpc-route \ --source=grpc_route.yaml \ --location=global
使用无代理 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
或受支持的值指定元数据标签。您可以在 NetworkServicesEndpointPolicy 文档的 endpointMatcher.metadataLabelMatcher.metadataLabelMatchCriteria
字段中找到支持的元数据标签。您可以使用您已定义的政策和包含端点政策资源的值的临时文件 ep-mtls-psms.yaml
创建端点政策。
使用服务器 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
通过导入临时文件
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
通过创建临时文件
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
通过导入
ep-mtls-psms.yaml
文件来创建端点政策资源:gcloud beta network-services endpoint-policies import ep-mtls-psms \ --source=ep-mtls-psms.yaml --location=global
在客户端配置 mTLS
客户端安全政策附加到后端服务。当客户端通过后端服务访问后端(gRPC 服务器)时,附加的客户端安全政策会被发送到客户端。
在当前目录中名为
client-mtls-policy.yaml
的临时文件中创建客户端 TLS 政策资源内容:name: "client-mtls-policy" clientCertificate: certificateProviderInstance: pluginInstance: google_cloud_private_spiffe serverValidationCa: - certificateProviderInstance: pluginInstance: google_cloud_private_spiffe
通过导入临时文件
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
在临时文件中创建一个代码段来引用此政策,并在
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
将
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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建以下规范:
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: - --config-mesh-experimental - "grpc-mesh" - --output - --config-mesh-experimental - "grpc-mesh" - "/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++
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建以下规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建以下规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
运行以下命令,确保正确设置了项目编号:
if [ -z "$PROJNUM" ] ; then export PROJNUM=$(gcloud projects describe $(gcloud info --format='value(config.project)') --format="value(projectNumber)") ; fi ; echo $PROJNUM
创建以下规范:
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: - --config-mesh-experimental - "grpc-mesh" - --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
请按以下方式完成此过程。
应用规范:
kubectl apply -f example-grpc-client.yaml
向服务账号授予所需角色:
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
验证客户端 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
获取为
example-grpc-server
服务创建的 pod 的名称:kubectl get pods | grep example-grpc-server
您会看到如下所示的反馈:
default example-grpc-server-77548868d-l9hmf 1/1 Running 0 105s
打开一个连接至服务器 pod 的 shell:
kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
在 shell 中,验证
/tmp/grpc-xds/td-grpc-bootstrap.json
中的引导文件与引导文件部分中所述的架构匹配。下载 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
使用
--xds-creds
标志运行服务器,以指示启用 xDS 的安全设置,使用50051
作为侦听端口,xds-server
作为服务器标识名称:./build/install/example-xds/bin/xds-hello-world-server --xds-creds 50051 xds-server
服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:
Listening on port 50051 plain text health service listening on port 50052
C++
获取为
example-grpc-server
服务创建的 pod 的名称:kubectl get pods | grep example-grpc-server
您会看到如下所示的反馈:
default example-grpc-server-77548868d-l9hmf 1/1 Running 0 105s
打开一个连接至服务器 pod 的 shell:
kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
在 shell 中,验证
/tmp/grpc-xds/td-grpc-bootstrap.json
中的引导文件与引导文件部分中所述的架构匹配。下载 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
运行服务器,使用
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
服务器从 Cloud Service Mesh 获取必要的配置后,您会看到以下输出:
Listening on port 50051 plain text health service listening on port 50052
Python
获取为
example-grpc-server
服务创建的 pod 的名称:kubectl get pods | grep example-grpc-server
您会看到如下所示的反馈:
default example-grpc-server-77548868d-l9hmf 1/1 Running 0 105s
打开一个连接至服务器 pod 的 shell:
kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
在 shell 中,验证
/tmp/grpc-xds/td-grpc-bootstrap.json
中的引导文件与引导文件部分中所述的架构匹配。下载 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
使用
--xds-creds
标志运行服务器,以指示启用 xDS 的安全设置,使用50051
作为侦听端口。python3 server.py 50051 --xds-creds
服务器从 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
获取为
example-grpc-server
服务创建的 pod 的名称:kubectl get pods | grep example-grpc-server
您会看到如下所示的反馈:
default example-grpc-server-77548868d-l9hmf 1/1 Running 0 105s
打开一个连接至服务器 pod 的 shell:
kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/sh
在 shell 中,验证
/tmp/grpc-xds/td-grpc-bootstrap.json
中的引导文件与引导文件部分中所述的架构匹配。下载 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
使用
--xds_creds
标志构建并使用50051
作为侦听端口来运行服务器以指示启用 xDS 的安全性:GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY_LEVEL="info" \ go run main.go \ -xds_creds \ -port 50051
服务器从 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
获取客户端 pod 的名称:
kubectl get pods | grep example-grpc-client
您会看到如下所示的反馈:
default example-grpc-client-7c969bb997-9fzjv 1/1 Running 0 105s
打开一个连接至客户端 pod 的 shell:
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
在命令 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
使用
--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++
获取客户端 pod 的名称:
kubectl get pods | grep example-grpc-client
您会看到如下所示的反馈:
default example-grpc-client-7c969bb997-9fzjv 1/1 Running 0 105s
打开一个连接至客户端 pod 的 shell:
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
在 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
使用
--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
获取客户端 pod 的名称:
kubectl get pods | grep example-grpc-client
您会看到如下所示的反馈:
default example-grpc-client-7c969bb997-9fzjv 1/1 Running 0 105s
打开一个连接至客户端 pod 的 shell:
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
进入 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
使用
--xds-creds
标志运行客户端,以指示启用 xDS 的安全设置、客户端名称和目标连接字符串:python3 client.py xds:///helloworld-gke:8000 --xds-creds
您应该会看到如下所示的输出:
Greeter client received: Hello you from example-host!
Go
获取客户端 pod 的名称:
kubectl get pods | grep example-grpc-client
您会看到如下所示的反馈:
default example-grpc-client-7c969bb997-9fzjv 1/1 Running 0 105s
打开一个连接至客户端 pod 的 shell:
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/sh
进入 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
使用
--xds_creds
标志构建并运行客户端,以指示已启用 xDS 的安全性、客户端名称和目标连接字符串:GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY_LEVEL="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
服务。
更新之前存储在
grpc_route.yaml
中的GRPCRoute
规范name: helloworld-grpc-route hostnames: - helloworld-gke:8000 - helloworld-gke-noaccess:8000 meshes: - projects/PROJECT_NUMBER/locations/global/meshes/grpc-mesh rules: - action: destinations: - serviceName: projects/PROJECT_NUMBER/locations/global/backendServices/grpc-gke-helloworld-service
再次从
grpc_route.yaml
规范导入GRPCRoute
资源。gcloud network-services grpc-routes import helloworld-grpc-route \ --source=grpc_route.yaml \ --location=global
以下说明会创建一个授权政策,该政策允许主机名为 helloworld-gke:8000
且端口为 50051
的 example-grpc-client
账号发送请求。
gcloud
通过创建名为
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
导入该政策。
gcloud network-security authorization-policies import \ helloworld-gke-authz-policy \ --source=helloworld-gke-authz-policy.yaml \ --location=global
通过将以下内容附加到文件
ep-mtls-psms.yaml
,将端点政策更新为引用新的授权政策。authorizationPolicy: projects/${PROJECT_ID}/locations/global/authorizationPolicies/helloworld-gke-authz-policy
端点政策现在指定,必须对其 gRPC 引导文件包含
app:helloworld
标签的 Pod 的入站请求强制执行 mTLS 和授权政策。导入政策:
gcloud network-services endpoint-policies import ep-mtls-psms \ --source=ep-mtls-psms.yaml --location=global
验证授权政策
按照以下说明确认授权政策是否正常工作。
Java
打开连接至您之前使用的客户端 pod 的 shell。
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
在命令 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
使用备用服务器名称再次运行客户端。请注意,这是一种失败案例。由于授权政策仅允许访问
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
打开连接至您之前使用的客户端 pod 的 shell。
kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
在命令 shell 中,运行以下命令以验证设置。
cd grpc-go-1.42.0/examples/features/xds/client GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY_LEVEL="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
使用备用服务器名称再次运行客户端。请注意,这是一种失败案例。由于授权政策仅允许访问
helloworld-gke:8000
主机名,因此请求无效。GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY_LEVEL="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 只需稍做更改即可。
在
ServerTlsPolicy
中,删除mtlsPolicy
:cat << EOF > server-tls-policy.yaml name: "server-tls-policy" serverCertificate: certificateProviderInstance: pluginInstance: google_cloud_private_spiffe EOF
在
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
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 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannel
和 XdsServer
凭据。
以下说明介绍了如何使用 Go 配置 Wallet 示例。此过程与 Java 类似。这些说明使用从 Google Cloud 容器代码库获取的已有 Docker 映像。
如需创建此示例,请按照以下说明操作:
- 克隆代码库并获取 gRPC 示例目录中的文件。
- 修改文件
00-common-env.sh
。注释掉将WALLET_DOCKER_IMAGE
的值设置为 Go Docker 映像的现有行,并取消注释将WALLET_DOCKER_IMAGE
的值设置为 Java Docker 映像的行。 - 按照创建和配置 Cloud Router 实例中的说明,或使用脚本
10.apis.sh
中的函数create_cloud_router_instances
创建和配置 Cloud Router 实例。 - 使用
hello world
示例说明或脚本20-cluster.sh
中的函数create_cluster
创建集群。 - 按照 CA 服务说明或使用脚本
30-private-ca-setup.sh
创建私有证书授权机构。 - 使用脚本
40-k8s-resources.sh
为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:account
、stats
、stats_premium
、wallet_v1
、wallet_v2
。 - 对于您创建的每项服务,请使用脚本
50-td-components.sh
中的create_health_check
和create_backend_service
创建健康检查和后端服务。 - 在脚本
60-routing-components.sh
中使用create_routing_components
创建 Cloud Service Mesh 路由组件。 - 使用脚本
70-security-components.sh
中的create_security_components
为每个后端服务创建 Cloud Service Mesh 安全组件。 - 使用脚本
75-client-deployment.sh
中的create_client_deployment
创建 Wallet 客户端部署。 - 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。
C++
您可以在 github 找到 C++ 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannel
和 XdsServer
凭据。
以下说明介绍了如何使用 Go 配置 Wallet 示例。此过程与 C++ 类似。这些说明使用从 Google Cloud 容器代码库获取的现有 Docker 映像。
如需创建此示例,请按照以下说明操作:
- 克隆代码库并获取 gRPC 示例目录中的文件。
- 修改文件
00-common-env.sh
。注释掉将WALLET_DOCKER_IMAGE
的值设置为 Go Docker 映像的现有行,并取消注释将WALLET_DOCKER_IMAGE
的值设置为 C++ Docker 映像的行。 - 按照创建和配置 Cloud Router 实例中的说明,或使用脚本
10.apis.sh
中的函数create_cloud_router_instances
创建和配置 Cloud Router 实例。 - 使用
hello world
示例说明或脚本20-cluster.sh
中的函数create_cluster
创建集群。 - 按照 CA 服务说明或使用脚本
30-private-ca-setup.sh
创建私有证书授权机构。 - 使用脚本
40-k8s-resources.sh
为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:account
、stats
、stats_premium
、wallet_v1
、wallet_v2
。 - 对于您创建的每项服务,请使用脚本
50-td-components.sh
中的create_health_check
和create_backend_service
创建健康检查和后端服务。 - 在脚本
60-routing-components.sh
中使用create_routing_components
创建 Cloud Service Mesh 路由组件。 - 使用脚本
70-security-components.sh
中的create_security_components
为每个后端服务创建 Cloud Service Mesh 安全组件。 - 使用脚本
75-client-deployment.sh
中的create_client_deployment
创建 Wallet 客户端部署。 - 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。
Go
您可以在 github 找到 Go 的示例源代码。当您配置无代理安全性时,代码已使用 XdsChannel
和 XdsServer
凭据。
这些说明使用从 Google Cloud 容器代码库获取的已有 Docker 映像。
如需创建此示例,请按照以下说明操作:
- 克隆代码库并获取 gRPC 示例目录中的文件。
- 修改文件
00-common-env.sh
以正确设置环境变量的值。 - 按照创建和配置 Cloud Router 实例中的说明,或使用脚本
10.apis.sh
中的函数create_cloud_router_instances
创建和配置 Cloud Router 实例。 - 使用
hello world
示例说明或脚本20-cluster.sh
中的函数create_cluster
创建集群。 - 按照 CA 服务说明或使用脚本
30-private-ca-setup.sh
创建私有证书授权机构。 - 使用脚本
40-k8s-resources.sh
为所有服务创建 Kubernetes 资源,包括服务账号、命名空间、Kubernetes 服务、NEG 和服务器端部署:account
、stats
、stats_premium
、wallet_v1
、wallet_v2
。 - 对于您创建的每项服务,请使用脚本
50-td-components.sh
中的create_health_check
和create_backend_service
创建健康检查和后端服务。 - 在脚本
60-routing-components.sh
中使用create_routing_components
创建 Cloud Service Mesh 路由组件。 - 使用脚本
70-security-components.sh
中的create_security_components
为每个后端服务创建 Cloud Service Mesh 安全组件。 - 使用脚本
75-client-deployment.sh
中的create_client_deployment
创建 Wallet 客户端部署。 - 按照使用 grpc-钱包客户端验证中的说明启动客户端,验证配置。
引导文件
本指南中的设置过程使用引导生成器来创建所需的引导文件。本部分提供有关引导文件本身的参考信息。
引导文件包含无代理 gRPC 代码所需的配置信息,包括 xDS 服务器的连接信息。引导文件包含无代理 gRPC 安全功能所需的安全配置。gRPC 服务器还需要一个额外的字段。引导文件示例如下所示:
{ "xds_servers": [ { "server_uri": "trafficdirector.googleapis.com:443", "channel_creds": [ { "type": "google_default" } ], "server_features": [ "xds_v3" ] } ], "authorities": { "traffic-director-c2p.xds.googleapis.com": { "xds_servers": [ { "server_uri": "dns:///directpath-pa.googleapis.com", "channel_creds": [ { "type": "google_default" } ], "server_features": [ "xds_v3", "ignore_resource_deletion" ] } ], "client_listener_resource_name_template": "xdstp://traffic-director-c2p.xds.googleapis.com/envoy.config.listener.v3.Listener/%s" } }, "node": { "id": "projects/9876012345/networks/mesh:grpc-mesh/nodes/b59f49cc-d95a-4462-9126-112f794d5dd3", "cluster": "cluster", "metadata": { "INSTANCE_IP": "10.28.2.8", "TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE": true, "TRAFFICDIRECTOR_GCP_PROJECT_NUMBER": "223606568246", "TRAFFICDIRECTOR_NETWORK_NAME": "default", "app": "helloworld" }, "locality": { "zone": "us-central1-c" } }, "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" } } }, "server_listener_resource_name_template": "grpc/server?xds.resource.listening_address=%s" }
针对安全服务的引导文件更新
以下字段反映了与安全和 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 都有两个字段:
|
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
。--config-mesh-experimental
:使用此选项指定与Mesh
资源匹配的网格名称。--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 gcloud 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 地址范围。
- 防火墙上的目标标记与您创建的集群上的标记不匹配。
工作负载无法在安全设置中通信
如果在您为无代理服务网格设置安全机制后,工作负载无法通信,请按照以下说明确定原因。
- 停用无代理安全机制,并消除无代理服务网格负载平衡使用场景中的问题。要停用网格中的安全机制,请执行以下操作之一:
- 在客户端和服务器端使用明文凭据,或者
- 不要在 Cloud Service Mesh 配置中为后端服务和端点政策配置安全机制。
请按照对无代理 Cloud Service Mesh 部署进行问题排查中的步骤操作,因为部署中没有安全设置。
修改工作负载,使用明文形式的 xDS 凭据或者不安全凭据作为回退凭据。如前所述,使 Cloud Service Mesh 配置停用安全机制。在这种情况下,虽然 gRPC 允许 Cloud Service Mesh 配置安全机制,Cloud Service Mesh 也不会发送安全信息,此时,gRPC 应该回退到明文(或不安全)凭据,其工作原理与上述第一种情况类似。如果不起作用,则执行以下操作:
- 提高客户端和服务器端的日志记录级别,以便您可以看到 gRPC 和 Cloud Service Mesh 之间交换的 xDS 消息。
- 确保 Cloud Service Mesh 在发送给工作负载的 CDS 和 LDS 响应中没有启用安全机制。
- 确保工作负载未在其通道中使用 TLS 或 mTLS 模式。如果您看到任何与 TLS 握手相关的日志消息,请检查应用源代码,并确保使用不安全或明文凭据作为回退凭据。如果应用源代码正确无误,则可能是 gRPC 库中的错误
按照该用户指南中的问题排查步骤,验证 CA Service 与 GKE 的集成可正确用于您的 GKE 集群。确保该功能提供的证书和密钥在指定的目录
/var/run/secrets/workload-spiffe-credentials/
中可用。如上文所述在网格中启用 TLS(而非 mTLS),然后重启客户端和服务器工作负载。
- 提高客户端和服务器端的日志记录级别,以便您可以看到 gRPC 和 Cloud Service Mesh 之间交换的 xDS 消息。
- 确保 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 库,请执行以下操作:
验证 pod 规范包含使用 NEG 创建无代理 gRPC 服务中所述的
security.cloud.google.com/use-workload-certificates
注释。验证可以从 pod 中访问以下路径中的包含证书链和叶证书的文件、私钥和可信 CA 证书:
- 证书链以及叶证书:“/var/run/secrets/workload-spiffe-credentials/certificates.pem”
- 私钥:“/var/run/secrets/workload-spiffe-credentials/private_key.pem”
- CA 软件包:“/var/run/secrets/workload-spinff-credentials/ca_certificates.pem”
如果上一步中的证书不可用,请执行以下操作:
gcloud privateca subordinates describe SUBORDINATE_CA_POOL_NAME
--location=LOCATION验证 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.
验证证书未过期。这是位于
/var/run/secrets/workload-spiffe-credentials/certificates.pem
的证书链和叶证书。要进行检查,请运行以下命令:cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
通过运行以下命令验证您的应用支持该密钥类型:
cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
验证您的 gRPC Java 应用在
WorkloadCertificateConfig
YAML 文件中具有以下keyAlgorithm
:
keyAlgorithm: rsa: modulusSize: 4096
验证 CA 与证书密钥使用相同的密钥系列。
应用的证书被客户端、服务器或对等应用拒绝
- 验证对等应用使用同一信任软件包来验证证书。
- 验证所用证书未过期(证书链以及叶证书:“/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 可能无法启动。在以下情况下可能发生此问题:
WorkloadCertificateConfig
或TrustConfig
配置错误或缺失。- CSR 未被批准。
您可以通过检查 pod 事件来检查证书预配是否失败。
检查 pod 的状态:
kubectl get pod -n POD_NAMESPACE POD_NAME
请替换以下内容:
POD_NAMESPACE
:您的 pod 的命名空间。POD_NAME
:您的 pod 的名称。
检查 pod 的近期事件:
kubectl describe pod -n POD_NAMESPACE POD_NAME
如果证书预配失败,您将看到一个事件具有
Type=Warning
、Reason=FailedMount
、From=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)
如果 pod 无法启动的原因是对象配置错误或 CSR 被拒,请参阅以下问题排查步骤。
WorkloadCertificateConfig
或 TrustConfig
配置错误
确保您已正确创建 WorkloadCertificateConfig
和 TrustConfig
对象。您可以使用 kubectl
诊断这两个对象的配置错误。
检索当前状态。
对于
WorkloadCertificateConfig
:kubectl get WorkloadCertificateConfig default -o yaml
对于
TrustConfig
:kubectl get TrustConfig default -o yaml
检查状态输出。有效对象的条件将包含
type: Ready
和status: "True"
。status: conditions: - lastTransitionTime: "2021-03-04T22:24:11Z" message: WorkloadCertificateConfig is ready observedGeneration: 1 reason: ConfigReady status: "True" type: Ready
无效对象则会显示
status: "False"
。reason
和message
字段包含更多问题排查详细信息。
CSR 未被批准
如果 CSR 审批流程中出现问题,您可以在 CSR 的 type: Approved
和 type: Issued
条件中查看错误详细信息。
使用
kubectl
列出相关 CSR:kubectl get csr \ --field-selector='spec.signerName=spiffe.gke.io/spiffe-leaf-signer'
选择一个
Approved
并且未Issued
,或者未Approved
的 CSR。使用 kubectl 获取所选 CSR 的详细信息:
kubectl get csr CSR_NAME -o yaml
将
CSR_NAME
替换为您选择的 CSR 的名称。
有效 CSR 的条件包含 type: Approved
和 status: "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
message
和 reason
字段中会显示无效 CSR 的问题排查信息。
pod 缺少证书
获取 pod 的 pod 规范:
kubectl get pod -n POD_NAMESPACE POD_NAME -o yaml
请替换以下内容:
POD_NAMESPACE
:您的 pod 的命名空间。POD_NAME
:您的 pod 的名称。
验证 pod 规范包含配置 pod 以接收 mTLS 凭据中所述的
security.cloud.google.com/use-workload-certificates
注释。验证 GKE 网格证书准入控制器是否已将
workloadcertificates.security.cloud.google.com
类型的 CSI 驱动程序卷成功注入到 pod 规范中:volumes: ... -csi: driver: workloadcertificates.security.cloud.google.com name: gke-workload-certificates ...
检查每个容器中是否存在卷装载:
containers: - name: ... ... volumeMounts: - mountPath: /var/run/secrets/workload-spiffe-credentials name: gke-workload-certificates readOnly: true ...
验证 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
- 证书链软件包:
如果文件不可用,请执行以下步骤:
检索集群的 CA Service(预览版)实例:
kubectl get workloadcertificateconfigs default -o jsonpath '{.spec.certificateAuthorityConfig.certificateAuthorityServiceConfig.endpointURI}'
检索 CA Service(预览版)实例的状态:
gcloud privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \ --location ISSUING_CA_LOCATION
请替换以下内容:
ISSUING_CA_TYPE
:发证 CA 类型,必须为subordinates
或roots
。ISSUING_CA_NAME
:发证 CA 的名称。ISSUING_CA_LOCATION
:发证 CA 的区域。
获取根 CA 的 IAM 政策:
gcloud privateca roots get-iam-policy ROOT_CA_NAME
将
ROOT_CA_NAME
替换为您的根 CA 的名称。在 IAM 政策中,验证存在
privateca.auditor
政策绑定:... - members: - serviceAccount:service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com role: roles/privateca.auditor ...
在此示例中,
PROJECT_NUMBER
是您的集群的项目编号。获取从属 CA 的 IAM 政策:
gcloud privateca subordinates get-iam-policy SUBORDINATE_CA_NAME
将
SUBORDINATE_CA_NAME
替换为从属 CA 名称。在 IAM 政策中,验证存在
privateca.certificateManager
政策绑定:... - members: - serviceAccount: service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com role: roles/privateca.certificateManager ...
在此示例中,
PROJECT_NUMBER
是您的集群的项目编号。
应用无法使用已颁发的 mTLS 凭据
验证证书未过期:
cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
检查应用是否支持您使用的密钥类型。
cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
检查发证 CA 是否使用与证书密钥相同的密钥系列。
获取 CA Service(预览版)实例的状态:
gcloud privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \ --location ISSUING_CA_LOCATION
请替换以下内容:
ISSUING_CA_TYPE
:发证 CA 类型,必须为subordinates
或roots
。ISSUING_CA_NAME
:发证 CA 的名称。ISSUING_CA_LOCATION
:发证 CA 的区域。
检查输出中的
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 ...
证书遭拒
- 验证对等应用使用同一信任软件包来验证证书。
验证证书未过期:
cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
如果未使用 gRPC Go Credentials Reloading API,验证客户端代码是否定期刷新来自文件系统的凭据。
验证工作负载与 CA 位于同一信任网域。GKE 网格证书支持单个信任网域中的工作负载之间的通信。
限制
只有 GKE 支持 Cloud Service Mesh 服务安全。您无法使用 Compute Engine 部署服务安全。
如果有两个或更多端点政策资源与同一个端点同等匹配(例如,两个政策具有相同的标签和端口,或者两个或更多政策具有不同的标签,但这些标签与同一个端点的标签同等匹配),Cloud Service Mesh 不支持这一场景。如需详细了解端点政策如何与端点标签匹配,请参阅 EndpointPolicy.EndpointMatcher.MetadataLabelMatcher 的 API。在这种情况下,Cloud Service Mesh 不会根据任何冲突的政策生成安全配置。