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 注册。

  1. 获取 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}")
    
  2. 为 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
    
  3. 为注入 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

  1. 下载 Istio 版本.

  2. 创建 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
    
  3. 应用配置

    $ istioctl install --skip-confirmation -f ./istio.yaml
    
  4. 检查 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 将永远等待。

  5. 部署示例工作负载

    压缩
    $ 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 发行

  1. 获取 Pod 信息

    $ CURL_POD=$(kubectl get pod -l app=curl -o jsonpath="{.items[0].metadata.name}")
    
  2. 使用 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
    
  3. 检查证书并验证 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 CRfederatesWith 字段设置为要与 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
这些信息是否有用?
您有什么改进建议吗?

感谢您的反馈!