SPIRE
SPIRE 是一种符合 SPIFFE 规范的生产就绪实现,它执行节点和工作负载证明,以便为在异构环境中运行的工作负载安全地颁发加密身份。SPIRE 可以配置为通过与 Envoy 的 SDS API 集成,作为 Istio 工作负载的加密身份来源。Istio 可以检测到在定义的套接字路径上实现 Envoy SDS API 的 UNIX 域套接字的存在,从而允许 Envoy 直接与它通信并获取身份。
与 SPIRE 的这种集成提供了默认 Istio 身份管理不可用的灵活证明选项,同时利用了 Istio 强大的服务管理功能。例如,SPIRE 的插件架构支持多种工作负载证明选项,超出了 Istio 提供的 Kubernetes 命名空间和服务帐户证明。SPIRE 的节点证明将证明扩展到工作负载运行的物理或虚拟硬件。
有关 SPIRE 与 Istio 集成工作原理的快速演示,请参阅 通过 Envoy 的 SDS API 集成 SPIRE 作为 CA。
安装 SPIRE
建议您遵循 SPIRE 的安装说明和最佳实践来安装 SPIRE,以及在生产环境中部署 SPIRE。
对于本指南中的示例,将使用 SPIRE Helm 图表 与上游默认值,重点关注仅与集成 SPIRE 和 Istio 相关的配置。
$ helm upgrade --install -n spire-server spire-crds spire-crds --repo https://spiffe.github.io/helm-charts-hardened/ --create-namespace
$ helm upgrade --install -n spire-server spire spire --repo https://spiffe.github.io/helm-charts-hardened/ --wait --set global.spire.trustDomain="example.org"
默认情况下,以上操作还将安装
SPIFFE CSI 驱动程序,它用于将 Envoy 兼容的 SDS 套接字挂载到代理中。Istio 和 SPIRE 都强烈建议使用 SPIFFE CSI 驱动程序来挂载 SDS 套接字,因为
hostMounts
存在更大的安全风险并带来操作障碍。本指南假定使用 SPIFFE CSI 驱动程序。SPIRE 控制器管理器,它简化了 SPIFFE 注册的创建工作。
注册工作负载
根据设计,SPIRE 仅向已在 SPIRE 服务器上注册的工作负载授予身份;这包括用户工作负载以及 Istio 组件。Istio 边车和网关在配置为集成 SPIRE 后,除非事先为它们创建了预先存在的匹配 SPIRE 注册,否则无法获取身份,因此也无法达到 READY 状态。
有关使用多个选择器来加强证明标准以及可用选择器的更多信息,请参阅 注册工作负载的 SPIRE 文档。
本节介绍了在 SPIRE 服务器中注册 Istio 工作负载的可用选项,并提供了一些工作负载注册示例。
选项 1:使用 SPIRE 控制器管理器进行自动注册
对于与 ClusterSPIFFEID 自定义资源中定义的选择器匹配的每个新 Pod,都会自动注册新条目。
Istio 边车和 Istio 网关都需要在 SPIRE 上注册,以便它们可以请求身份。
Istio 网关 ClusterSPIFFEID
以下操作将创建一个 ClusterSPIFFEID
,如果任何 Istio Ingress 网关 Pod 被调度到 istio-system
命名空间,并且具有名为 istio-ingressgateway-service-account
的服务帐户,则该操作将自动在 SPIRE 上注册该 Pod。这些选择器仅用作简单示例;有关更多详细信息,请参阅 SPIRE 控制器管理器文档。
$ kubectl apply -f - <<EOF
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
name: istio-ingressgateway-reg
spec:
spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
workloadSelectorTemplates:
- "k8s:ns:istio-system"
- "k8s:sa:istio-ingressgateway-service-account"
EOF
Istio 边车 ClusterSPIFFEID
以下操作将创建一个 ClusterSPIFFEID
,该操作将自动在 SPIRE 上注册任何具有 spiffe.io/spire-managed-identity: true
标签的 Pod,该 Pod 部署到 default
命名空间中。这些选择器仅用作简单示例;有关更多详细信息,请参阅 SPIRE 控制器管理器文档。
$ kubectl apply -f - <<EOF
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
name: istio-sidecar-reg
spec:
spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
podSelector:
matchLabels:
spiffe.io/spire-managed-identity: "true"
workloadSelectorTemplates:
- "k8s:ns:default"
EOF
选项 2:手动注册
如果您希望手动创建 SPIRE 注册,而不是使用 建议的选项 中提到的 SPIRE 控制器管理器,请参阅 有关手动注册的 SPIRE 文档。
以下是基于 选项 1 中的自动注册的等效手动注册。以下步骤假定您已 按照 SPIRE 文档手动注册了 SPIRE 代理和节点证明,并且您的 SPIRE 代理已使用 SPIFFE 身份 spiffe://example.org/ns/spire/sa/spire-agent
注册。
获取
spire-server
Pod$ SPIRE_SERVER_POD=$(kubectl get pod -l statefulset.kubernetes.io/pod-name=spire-server-0 -n spire-server -o jsonpath="{.items[0].metadata.name}")
为 Istio Ingress 网关 Pod 注册一个条目
$ kubectl exec -n spire "$SPIRE_SERVER_POD" -- \ /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account \ -parentID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s:sa:istio-ingressgateway-service-account \ -selector k8s:ns:istio-system \ -socketPath /run/spire/sockets/server.sock Entry ID : 6f2fe370-5261-4361-ac36-10aae8d91ff7 SPIFFE ID : spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account Parent ID : spiffe://example.org/ns/spire/sa/spire-agent Revision : 0 TTL : default Selector : k8s:ns:istio-system Selector : k8s:sa:istio-ingressgateway-service-account
为注入 Istio 边车的工作负载注册一个条目
$ kubectl exec -n spire "$SPIRE_SERVER_POD" -- \ /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/default/sa/curl \ -parentID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s:ns:default \ -selector k8s:pod-label:spiffe.io/spire-managed-identity:true \ -socketPath /run/spire/sockets/server.sock
安装 Istio
创建 Istio 配置,其中包含针对 Ingress 网关和
istio-proxy
的自定义补丁。Ingress 网关组件包括spiffe.io/spire-managed-identity: "true"
标签。$ cat <<EOF > ./istio.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: namespace: istio-system spec: profile: default meshConfig: trustDomain: example.org values: # This is used to customize the sidecar template. # It adds both the label to indicate that SPIRE should manage the # identity of this pod, as well as the CSI driver mounts. sidecarInjectorWebhook: templates: spire: | labels: spiffe.io/spire-managed-identity: "true" spec: containers: - name: istio-proxy volumeMounts: - name: workload-socket mountPath: /run/secrets/workload-spiffe-uds readOnly: true volumes: - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true components: ingressGateways: - name: istio-ingressgateway enabled: true label: istio: ingressgateway k8s: overlays: # This is used to customize the ingress gateway template. # It adds the CSI driver mounts, as well as an init container # to stall gateway startup until the CSI driver mounts the socket. - apiVersion: apps/v1 kind: Deployment name: istio-ingressgateway patches: - path: spec.template.spec.volumes.[name:workload-socket] value: name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true - path: spec.template.spec.containers.[name:istio-proxy].volumeMounts.[name:workload-socket] value: name: workload-socket mountPath: "/run/secrets/workload-spiffe-uds" readOnly: true - path: spec.template.spec.initContainers value: - name: wait-for-spire-socket image: busybox:1.36 volumeMounts: - name: workload-socket mountPath: /run/secrets/workload-spiffe-uds readOnly: true env: - name: CHECK_FILE value: /run/secrets/workload-spiffe-uds/socket command: - sh - "-c" - |- echo "$(date -Iseconds)" Waiting for: ${CHECK_FILE} while [[ ! -e ${CHECK_FILE} ]] ; do echo "$(date -Iseconds)" File does not exist: ${CHECK_FILE} sleep 15 done ls -l ${CHECK_FILE} EOF
应用配置
$ istioctl install --skip-confirmation -f ./istio.yaml
检查 Ingress 网关 Pod 状态
$ kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE istio-ingressgateway-5b45864fd4-lgrxs 1/1 Running 0 17s istiod-989f54d9c-sg7sn 1/1 Running 0 23s
Ingress 网关 Pod 处于
Ready
状态,因为在 SPIRE 服务器上会为它自动创建相应的注册条目。Envoy 能够从 SPIRE 获取加密身份。此配置还在网关中添加了一个
initContainer
,该容器将在 SPIRE 创建 UNIX 域套接字之前等待 SPIRE 创建该套接字,然后才启动istio-proxy
。如果 SPIRE 代理未准备好,或未正确配置为使用相同的套接字路径,则 Ingress 网关initContainer
将永远等待。部署示例工作负载
$ istioctl kube-inject --filename @samples/security/spire/curl-spire.yaml@ | kubectl apply -f -
除了需要
spiffe.io/spire-managed-identity
标签之外,工作负载还需要 SPIFFE CSI 驱动程序卷才能访问 SPIRE 代理套接字。要实现这一点,您可以利用 安装 Istio 部分中的spire
Pod 注释模板,或将 CSI 卷添加到工作负载的部署规范中。这两个选项都在以下示例代码片段中突出显示apiVersion: apps/v1 kind: Deployment metadata: name: curl spec: replicas: 1 selector: matchLabels: app: curl template: metadata: labels: app: curl # Injects custom sidecar template annotations: inject.istio.io/templates: "sidecar,spire" spec: terminationGracePeriodSeconds: 0 serviceAccountName: curl containers: - name: curl image: curlimages/curl command: ["/bin/sleep", "3650d"] imagePullPolicy: IfNotPresent volumeMounts: - name: tmp mountPath: /tmp securityContext: runAsUser: 1000 volumes: - name: tmp emptyDir: {} # CSI volume - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true
Istio 配置与 Ingress 网关以及将被注入到工作负载 Pod 中的边车共享 spiffe-csi-driver
,从而授予它们访问 SPIRE 代理的 UNIX 域套接字的权限。
请参阅 验证是否已为工作负载创建了身份 以检查已颁发的身份。
验证是否为工作负载创建了身份
使用以下命令确认是否已为工作负载创建了身份
$ kubectl exec -t "$SPIRE_SERVER_POD" -n spire-server -c spire-server -- ./bin/spire-server entry show
Found 2 entries
Entry ID : c8dfccdc-9762-4762-80d3-5434e5388ae7
SPIFFE ID : spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account
Parent ID : spiffe://example.org/spire/agent/k8s_psat/demo-cluster/bea19580-ae04-4679-a22e-472e18ca4687
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:pod-uid:88b71387-4641-4d9c-9a89-989c88f7509d
Entry ID : af7b53dc-4cc9-40d3-aaeb-08abbddd8e54
SPIFFE ID : spiffe://example.org/ns/default/sa/curl
Parent ID : spiffe://example.org/spire/agent/k8s_psat/demo-cluster/bea19580-ae04-4679-a22e-472e18ca4687
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:pod-uid:ee490447-e502-46bd-8532-5a746b0871d6
检查 Ingress-gateway Pod 状态
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-5b45864fd4-lgrxs 1/1 Running 0 60s
istiod-989f54d9c-sg7sn 1/1 Running 0 45s
为 Ingress-gateway Pod 注册条目后,Envoy 会收到 SPIRE 颁发的身份,并将其用于所有 TLS 和 mTLS 通信。
检查工作负载身份是否由 SPIRE 发行
获取 Pod 信息
$ CURL_POD=$(kubectl get pod -l app=curl -o jsonpath="{.items[0].metadata.name}")
使用 istioctl proxy-config secret 命令检索 curl 的 SVID 身份文档
$ istioctl proxy-config secret "$CURL_POD" -o json | jq -r \ '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | base64 --decode > chain.pem
检查证书并验证 SPIRE 是否为颁发者
$ openssl x509 -in chain.pem -text | grep SPIRE Subject: C = US, O = SPIRE, CN = curl-5f4d47c948-njvpk
SPIFFE 联合
SPIRE 服务器能够对来自不同信任域的 SPIFFE 身份进行身份验证。这被称为 SPIFFE 联合。
可以将 SPIRE 代理配置为通过 Envoy SDS API 将联合捆绑包推送到 Envoy,从而允许 Envoy 使用 验证上下文 来验证对等证书并信任来自另一个信任域的工作负载。要使 Istio 能够通过 SPIRE 集成联合 SPIFFE 身份,请参阅 SPIRE 代理 SDS 配置,并为 SPIRE 代理配置文件设置以下 SDS 配置值。
配置 | 描述 | 资源名称 |
---|---|---|
default_svid_name | 用于 Envoy SDS 的默认 X509-SVID 的 TLS 证书资源名称 | default |
default_bundle_name | 用于 Envoy SDS 的默认 X.509 捆绑包的验证上下文资源名称 | null |
default_all_bundles_name | 用于 Envoy SDS 的所有捆绑包(包括联合捆绑包)的验证上下文资源名称 | ROOTCA |
这将允许 Envoy 直接从 SPIRE 获取联合捆绑包。
创建联合注册条目
如果使用 SPIRE 控制器管理器,请通过将 ClusterSPIFFEID CR 的
federatesWith
字段设置为要与 Pod 联合的信任域来为工作负载创建联合条目apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterSPIFFEID metadata: name: federation spec: spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}" podSelector: matchLabels: spiffe.io/spire-managed-identity: "true" federatesWith: ["example.io", "example.ai"]
有关手动注册,请参阅 为联合创建注册条目。
清理 SPIRE
通过卸载其 Helm 图表来删除 SPIRE
$ helm delete -n spire-server spire
$ helm delete -n spire-server spire-crds