SPIRE

SPIRE 是 SPIFFE 规范的一个可用于生产环境的实现,它执行节点和工作负载的认证,以便在异构环境中安全地为运行的工作负载颁发加密身份。 通过与 Envoy 的 SDS API 集成,可以将 SPIRE 配置为 Istio 工作负载的加密身份源。Istio 可以检测到实现了 Envoy SDS API 的 UNIX 域套接字的存在, 并允许 Envoy 直接从其中通信并获取身份。

与默认的 Istio 身份管理相比,与 SPIRE 的集成提供了灵活的认证选项。例如,SPIRE 的插件架构使您可以选择 Kubernetes 命名空间和服务账户认证之外的多样化工作负载认证选项。SPIRE 的节点认证将认证扩展到工作负载所运行的物理或虚拟硬件上。

要了解关于如何将 SPIRE 与 Istio 集成的快速演示,请参阅通过 Envoy 的 SDS API 将 SPIRE 集成为 CA

安装 SPIRE

我们建议您遵循 SPIRE 的安装说明和最佳实践来安装 SPIRE,并在生产环境中部署 SPIRE。

对于本指南中的示例,SPIRE Helm Chart 将与上游默认值一起使用,以仅关注集成 SPIRE 和 Istio 所需的配置。

  1. $ helm upgrade --install -n spire-server spire-crds spire-crds --repo https://spiffe.github.io/helm-charts-hardened/ --create-namespace
  1. $ helm upgrade --install -n spire-server spire spire --repo https://spiffe.github.io/helm-charts-hardened/ --wait --set global.spire.trustDomain="example.org"

请参阅 SPIRE Helm Chart 文档, 了解您可以为安装配置的其他值。

重要的是,SPIRE 和 Istio 配置了完全相同的信任域,以防止身份验证和授权错误, 并且启用并安装了 SPIFFE CSI 驱动程序

默认情况下,以上操作还将安装:

  • SPIFFE CSI 驱动程序, 用于将与 Envoy 兼容的 SDS 套接字挂载到代理中。 Istio 和 SPIRE 都强烈建议使用 SPIFFE CSI 驱动程序挂载 SDS 套接字, 因为 hostMounts 具有更大的安全风险并会带来操作障碍。本指南假设使用 SPIFFE CSI 驱动程序。

  • SPIRE 控制器管理器, 它简化了为工作负载创建 SPIFFE 注册的过程。

注册工作负载

根据设计,SPIRE 仅向已在 SPIRE 服务器上注册的工作负载授予身份; 这包括用户工作负载以及 Istio 组件。Istio Sidecar 和 Gateway 一旦配置为 SPIRE 集成, 就无法获取身份,因此无法达到 READY 状态,除非事先为它们创建了预先存在的匹配 SPIRE 注册。

有关使用多个选择器来加强证明标准以及可用选择器的更多信息, 请参阅有关注册工作负载的 SPIRE 文档

本节介绍在 SPIRE 服务器中注册 Istio 工作负载的可用选项,并提供一些工作负载注册示例。

Istio 目前要求工作负载采用特定的 SPIFFE ID 格式。 所有注册都必须遵循 Istio SPIFFE ID 模式:spiffe://<trust.domain>/ns/<namespace>/sa/<service-account>

选项 1:使用 SPIRE 控制器管理器自动注册

每个与 ClusterSPIFFEID 自定义资源中定义的选择器匹配的新 Pod 将自动注册新 Entry。

Istio Sidecar 和 Istio Gateway 都需要在 SPIRE 上注册,以便它们可以请求身份。

Istio Gateway ClusterSPIFFEID

下面将创建一个 ClusterSPIFFEID,如果 Istio Ingress Gateway Pod 被调度到 istio-system 命名空间,它将自动向 SPIRE 注册该 Pod, 并且该 Pod 有一个名为 istio-ingressgateway-service-account 的服务帐户。 这些选择器用作简单示例;有关更多详细信息, 请参阅 SPIRE Con​​troller Manager 文档

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: spire.spiffe.io/v1alpha1
  3. kind: ClusterSPIFFEID
  4. metadata:
  5. name: istio-ingressgateway-reg
  6. spec:
  7. spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
  8. workloadSelectorTemplates:
  9. - "k8s:ns:istio-system"
  10. - "k8s:sa:istio-ingressgateway-service-account"
  11. EOF

Istio Sidecar ClusterSPIFFEID

下面将创建一个 ClusterSPIFFEID, 它将自动注册任何带有 spiffe.io/spire-managed-identity: true 标签的 Pod, 这些 Pod 会使用 SPIRE 部署到 default 命名空间中。这些选择器用作简单示例; 有关更多详细信息,请参阅 SPIRE 控制器管理器文档

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: spire.spiffe.io/v1alpha1
  3. kind: ClusterSPIFFEID
  4. metadata:
  5. name: istio-sidecar-reg
  6. spec:
  7. spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
  8. podSelector:
  9. matchLabels:
  10. spiffe.io/spire-managed-identity: "true"
  11. workloadSelectorTemplates:
  12. - "k8s:ns:default"
  13. EOF

选项 2:手动注册

如果您希望手动创建 SPIRE 注册, 而不是使用推荐选项中提到的 SPIRE 控制器管理器, 请参阅有关手动注册的 SPIRE 文档

以下是基于选项 1 中的自动注册的等效手动注册。 以下步骤假设您已经按照 SPIRE 文档手动注册了您的 SPIRE 代理和节点证明, 并且您的 SPIRE 代理已使用 SPIFFE 身份 spiffe://example.org/ns/spire/sa/spire-agent 注册。

  1. 获取 spire-server Pod:

    1. $ SPIRE_SERVER_POD=$(kubectl get pod -l statefulset.kubernetes.io/pod-name=spire-server-0 -n spire-server -o jsonpath="{.items[0].metadata.name}")
  2. 为 Istio Ingress Gateway Pod 注册一个 Entry:

    1. $ kubectl exec -n spire "$SPIRE_SERVER_POD" -- \
    2. /opt/spire/bin/spire-server entry create \
    3. -spiffeID spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account \
    4. -parentID spiffe://example.org/ns/spire/sa/spire-agent \
    5. -selector k8s:sa:istio-ingressgateway-service-account \
    6. -selector k8s:ns:istio-system \
    7. -socketPath /run/spire/sockets/server.sock
    8. Entry ID : 6f2fe370-5261-4361-ac36-10aae8d91ff7
    9. SPIFFE ID : spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account
    10. Parent ID : spiffe://example.org/ns/spire/sa/spire-agent
    11. Revision : 0
    12. TTL : default
    13. Selector : k8s:ns:istio-system
    14. Selector : k8s:sa:istio-ingressgateway-service-account
  3. 注册一个通过 Istio Sidecar 注入工作负载的 Entry:

    1. $ kubectl exec -n spire "$SPIRE_SERVER_POD" -- \
    2. /opt/spire/bin/spire-server entry create \
    3. -spiffeID spiffe://example.org/ns/default/sa/curl \
    4. -parentID spiffe://example.org/ns/spire/sa/spire-agent \
    5. -selector k8s:ns:default \
    6. -selector k8s:pod-label:spiffe.io/spire-managed-identity:true \
    7. -socketPath /run/spire/sockets/server.sock

安装 Istio

  1. 下载 Istio 发行版

  2. 使用自定义补丁为 Ingress Gateway 和 istio-proxy 创建 Istio 配置。 Ingress Gateway 组件包含 spiffe.io/spire-managed-identity: "true" 标签。

    1. $ cat <<EOF > ./istio.yaml
    2. apiVersion: install.istio.io/v1alpha1
    3. kind: IstioOperator
    4. metadata:
    5. namespace: istio-system
    6. spec:
    7. profile: default
    8. meshConfig:
    9. trustDomain: example.org
    10. values:
    11. # 这用于自定义 Sidecar 模板。
    12. # 它添加了标签以指示 SPIRE 应该管理此 Pod 的身份,
    13. # 以及 CSI 驱动程序挂载。
    14. sidecarInjectorWebhook:
    15. templates:
    16. spire: |
    17. labels:
    18. spiffe.io/spire-managed-identity: "true"
    19. spec:
    20. containers:
    21. - name: istio-proxy
    22. volumeMounts:
    23. - name: workload-socket
    24. mountPath: /run/secrets/workload-spiffe-uds
    25. readOnly: true
    26. volumes:
    27. - name: workload-socket
    28. csi:
    29. driver: "csi.spiffe.io"
    30. readOnly: true
    31. components:
    32. ingressGateways:
    33. - name: istio-ingressgateway
    34. enabled: true
    35. label:
    36. istio: ingressgateway
    37. k8s:
    38. overlays:
    39. # 这用于定制 Ingress Gateway 模板。
    40. # 它添加了 CSI 驱动程序挂载,以及一个 init 容器来停止网关启动,
    41. # 直到 CSI 驱动程序挂载套接字。
    42. - apiVersion: apps/v1
    43. kind: Deployment
    44. name: istio-ingressgateway
    45. patches:
    46. - path: spec.template.spec.volumes.[name:workload-socket]
    47. value:
    48. name: workload-socket
    49. csi:
    50. driver: "csi.spiffe.io"
    51. readOnly: true
    52. - path: spec.template.spec.containers.[name:istio-proxy].volumeMounts.[name:workload-socket]
    53. value:
    54. name: workload-socket
    55. mountPath: "/run/secrets/workload-spiffe-uds"
    56. readOnly: true
    57. - path: spec.template.spec.initContainers
    58. value:
    59. - name: wait-for-spire-socket
    60. image: busybox:1.36
    61. volumeMounts:
    62. - name: workload-socket
    63. mountPath: /run/secrets/workload-spiffe-uds
    64. readOnly: true
    65. env:
    66. - name: CHECK_FILE
    67. value: /run/secrets/workload-spiffe-uds/socket
    68. command:
    69. - sh
    70. - "-c"
    71. - |-
    72. echo "$(date -Iseconds)" Waiting for: ${CHECK_FILE}
    73. while [[ ! -e ${CHECK_FILE} ]] ; do
    74. echo "$(date -Iseconds)" File does not exist: ${CHECK_FILE}
    75. sleep 15
    76. done
    77. ls -l ${CHECK_FILE}
    78. EOF
  3. 应用配置:

    1. $ istioctl install --skip-confirmation -f ./istio.yaml
  4. 检查 Ingress Gateway Pod 状态:

    1. $ kubectl get pods -n istio-system
    2. NAME READY STATUS RESTARTS AGE
    3. istio-ingressgateway-5b45864fd4-lgrxs 1/1 Running 0 17s
    4. istiod-989f54d9c-sg7sn 1/1 Running 0 23s

    Ingress Gateway Pod 已 Ready,因为 SPIRE 服务器上会自动为其创建相应的注册条目。 Envoy 能够从 SPIRE 获取加密身份。

    此配置还向网关添加了一个 initContainer,它将等待 SPIRE 创建 UNIX 域套接字, 然后再启动 istio-proxy。如果 SPIRE 代理尚未准备就绪,或者未正确配置相同的套接字路径, 则 Ingress Gateway initContainer 将永远等待。

  5. 部署示例工作负载:

    Zip

    1. $ 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 卷添加到您的工作负载的部署规范中。这两种方法都在下面的示例片段中进行了突出显示:

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: curl
    5. spec:
    6. replicas: 1
    7. selector:
    8. matchLabels:
    9. app: curl
    10. template:
    11. metadata:
    12. labels:
    13. app: curl
    14. # 注入自定义 Sidecar 模板
    15. annotations:
    16. inject.istio.io/templates: "sidecar,spire"
    17. spec:
    18. terminationGracePeriodSeconds: 0
    19. serviceAccountName: curl
    20. containers:
    21. - name: curl
    22. image: curlimages/curl
    23. command: ["/bin/sleep", "3650d"]
    24. imagePullPolicy: IfNotPresent
    25. volumeMounts:
    26. - name: tmp
    27. mountPath: /tmp
    28. securityContext:
    29. runAsUser: 1000
    30. volumes:
    31. - name: tmp
    32. emptyDir: {}
    33. # CSI 卷
    34. - name: workload-socket
    35. csi:
    36. driver: "csi.spiffe.io"
    37. readOnly: true

Istio 配置与 Ingress Gateway 和将要注入工作负载容器的 Sidecar 共享 spiffe-csi-driver, 从而授予它们访问 SPIRE 代理的 UNIX 域套接字的权限。

请参阅验证为工作负载创建的身份 以检查已颁发的身份。

验证工作负载的身份是否已创建

请使用以下命令确认是否已为工作负载创建了身份:

  1. $ kubectl exec -t "$SPIRE_SERVER_POD" -n spire-server -c spire-server -- ./bin/spire-server entry show
  2. Found 2 entries
  3. Entry ID : c8dfccdc-9762-4762-80d3-5434e5388ae7
  4. SPIFFE ID : spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account
  5. Parent ID : spiffe://example.org/spire/agent/k8s_psat/demo-cluster/bea19580-ae04-4679-a22e-472e18ca4687
  6. Revision : 0
  7. X509-SVID TTL : default
  8. JWT-SVID TTL : default
  9. Selector : k8s:pod-uid:88b71387-4641-4d9c-9a89-989c88f7509d
  10. Entry ID : af7b53dc-4cc9-40d3-aaeb-08abbddd8e54
  11. SPIFFE ID : spiffe://example.org/ns/default/sa/curl
  12. Parent ID : spiffe://example.org/spire/agent/k8s_psat/demo-cluster/bea19580-ae04-4679-a22e-472e18ca4687
  13. Revision : 0
  14. X509-SVID TTL : default
  15. JWT-SVID TTL : default
  16. Selector : k8s:pod-uid:ee490447-e502-46bd-8532-5a746b0871d6

检查 Ingress-gateway Pod 状态:

  1. $ kubectl get pods -n istio-system
  2. NAME READY STATUS RESTARTS AGE
  3. istio-ingressgateway-5b45864fd4-lgrxs 1/1 Running 0 60s
  4. istiod-989f54d9c-sg7sn 1/1 Running 0 45s

在为入口网关 Pod 注册条目后,Envoy 将收到 SPIRE 颁发的身份并将其用于所有 TLS 和 mTLS 通信。

检查工作负载身份是否由 SPIRE 颁发

  1. 获取 Pod 信息:

    1. $ CURL_POD=$(kubectl get pod -l app=curl -o jsonpath="{.items[0].metadata.name}")
  2. 使用 istioctl proxy-config secret 命令检索 curl 的 SVID 身份文档:

    1. $ istioctl proxy-config secret "$CURL_POD" -o json | jq -r \
    2. '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | base64 --decode > chain.pem
  3. 检查证书并确认 SPIRE 是颁发者:

    1. $ openssl x509 -in chain.pem -text | grep SPIRE
    2. 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_bundle_name用于 Envoy SDS 的默认 X.509 bundle 的验证上下文资源名称
default_all_bundles_name用于 Envoy SDS 的所有 bundle(包括联邦的)的验证上下文资源名称ROOTCA

这将允许 Envoy 直接从 SPIRE 获取联邦 bundle。

创建联邦注册条目

  • 如果使用 SPIRE Controller Manager,请通过将 ClusterSPIFFEID CRfederatesWith 字段设置为您希望 Pod 与之联邦的信任域来为工作负载创建联邦条目:

    1. apiVersion: spire.spiffe.io/v1alpha1
    2. kind: ClusterSPIFFEID
    3. metadata:
    4. name: federation
    5. spec:
    6. spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
    7. podSelector:
    8. matchLabels:
    9. spiffe.io/spire-managed-identity: "true"
    10. federatesWith: ["example.io", "example.ai"]
  • 要进行手动注册,请参阅为联邦创建注册条目

清理 SPIRE

通过卸载 Helm Chart 来删除 SPIRE:

  1. $ helm delete -n spire-server spire
  1. $ helm delete -n spire-server spire-crds