如何在 Istio 中集成 SPRIRE?

語言: CN / TW / HK

安裝 Istio

GitHub 上下載 Istio 安裝包,解壓後,建議使用 istioctl 安裝 Istio。

istioctl install --set profile=demo

如果你安裝了低於 1.14 版本的 Istio,請先升級 Istio。

安裝 SPIRE

我們使用 Istio 提供的快速安裝方式:

kubectl apply -f samples/security/spire/spire-quickstart.yaml

這將安裝以下組件:

namespace/spire created
csidriver.storage.k8s.io/csi.spiffe.io created
customresourcedefinition.apiextensions.k8s.io/spiffeids.spiffeid.spiffe.io created
clusterrolebinding.rbac.authorization.k8s.io/k8s-workload-registrar-role-binding created
clusterrole.rbac.authorization.k8s.io/k8s-workload-registrar-role created
configmap/k8s-workload-registrar created
serviceaccount/spire-server created
configmap/trust-bundle created
clusterrole.rbac.authorization.k8s.io/spire-server-trust-role created
clusterrolebinding.rbac.authorization.k8s.io/spire-server-trust-role-binding created
configmap/spire-server created
statefulset.apps/spire-server created
service/spire-server created
serviceaccount/spire-agent created
clusterrole.rbac.authorization.k8s.io/spire-agent-cluster-role created
clusterrolebinding.rbac.authorization.k8s.io/spire-agent-cluster-role-binding created
configmap/spire-agent created
daemonset.apps/spire-agent created

需要給打 patch,這是為了讓所有 sidecar 和 Ingress 都可以共享 SPIRE agent 的 UNIX Domain Socket。

istioctl install --skip-confirmation -f - <<EOF
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  profile: default
  meshConfig:
    trustDomain: example.org
  values:
    global:
    # This is used to customize the sidecar template
    sidecarInjectorWebhook:
      templates:
        spire: |
          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"          
  components:
    ingressGateways:
      - name: istio-ingressgateway
        enabled: true
        label:
          istio: ingressgateway
        k8s:
          overlays:
            - 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"
                - path: spec.template.spec.containers.[name:istio-proxy].volumeMounts.[name:workload-socket]
                  value:
                    name: workload-socket
                    mountPath: "/run/secrets/workload-spiffe-uds"
                    readOnly: true
EOF

安裝好 Istio 和 SPIRE 後,我們就可以註冊負載了。

自動註冊 Kubernetes 負載

在快速安裝 SPIRE 的時候,我們已經安裝了 SPRIE Kubernetes Workload Registrar ,也就是説我們已經開啟自動負載註冊。現在檢查一下 SPIRE 是否給負載頒發了身份證明。

kubectl exec -i -t spire-server-0 -n spire -c spire-server -- /bin/sh -c "bin/spire-server entry show -socketPath /run/spire/sockets/server.sock"

你將看到例如下面這樣的結果:

# Node

Entry ID         : 3f17c8be-1379-4b7c-9a01-90805165d59f
SPIFFE ID        : spiffe://example.org/k8s-workload-registrar/demo-cluster/node/gke-jimmy-cluster-default-pool-d5041909-3atb
Parent ID        : spiffe://example.org/spire/server
Revision         : 0
TTL              : default
Selector         : k8s_psat:agent_node_uid:cbab5123-b32f-49d0-89f2-0a7e4d2b0edd
Selector         : k8s_psat:cluster:demo-cluster

# Ingress gateway

Entry ID         : ffc76b2e-e602-4ad3-8069-993ffbf4440e
SPIFFE ID        : spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account
Parent ID        : spiffe://example.org/k8s-workload-registrar/demo-cluster/node/gke-jimmy-cluster-default-pool-d5041909-0ucb
Revision         : 1
TTL              : default
Selector         : k8s:node-name:gke-jimmy-cluster-default-pool-d5041909-0ucb
Selector         : k8s:ns:istio-system
Selector         : k8s:pod-uid:be32b10a-b5a4-4716-adaf-eab778f58c13
DNS name         : istio-ingressgateway-6989cbc776-87qb6
DNS name         : istio-ingressgateway.istio-system.svc

# SPIRE Server

Entry ID         : 54444848-95ec-4b4d-a7c5-902a4049c96a
SPIFFE ID        : spiffe://example.org/ns/spire/sa/spire-server
Parent ID        : spiffe://example.org/k8s-workload-registrar/demo-cluster/node/gke-jimmy-cluster-default-pool-d5041909-0ucb
Revision         : 1
TTL              : default
Selector         : k8s:node-name:gke-jimmy-cluster-default-pool-d5041909-0ucb
Selector         : k8s:ns:spire
Selector         : k8s:pod-uid:4917defc-9b5a-42d8-9b98-c0e0a48c0313
DNS name         : spire-server-0
DNS name         : spire-server.spire.svc

每個 node 的 parent ID 是 spiffe://example.org/ns/spire/sa/spire-server ,而 spiffe://example.org/ns/spire/sa/spire-server 的 parent 是 spiffe://example.org/k8s-workload-registrar/demo-cluster/node/gke-jimmy-cluster-default-pool-d5041909-0ucb

也就是説工作負載的層級是這樣的:

spiffe://example.org/spire/server
spiffe://example.org/k8s-workload-registrar/demo-cluster/node/
spiffe://example.org/{namespace}/spire/sa/{service_acount}

SPIRE 通過標籤選擇器選擇工作負載,為它們創建 SPIFFE ID。

工作負載的 SPIFEE ID 格式為 spiffe://<trust.domain>/ns/<namespace>/sa/<service-account>

部署應用

下面我們部署一個應用,然後檢查下 SPIRE 為該應提供的身份。

使用下面的命令部署 Istio 提供的示例應用 sleep

istioctl kube-inject --filename samples/security/spire/sleep-spire.yaml | kubectl apply -f -

因為我們在安裝 Istio 的時候打了補丁,做了 SPIRE 自動注入,然後所有 pod 都會共享 SPIRE Agent 中的 UNIX Domain Socket: /run/secrets/workload-spiffe-uds/socket 。獲取正在運行的 sleep pod 的 YAML,我們可以看到其中有 volume 配置如下:

  volumes:
    - name: workload-socket
      csi:
        driver: csi.spiffe.io

以及 volumeMounts 如下:

      volumeMounts:
        - name: workload-socket
          readOnly: true
          mountPath: /run/secrets/workload-spiffe-uds
        - name: workload-socket
          mountPath: /var/run/secrets/workload-spiffe-uds
        - name: workload-certs
          mountPath: /var/run/secrets/workload-spiffe-credentials
        - name: istiod-ca-cert

這都證實了 SPRIE Agent 與工作負載 pod 共享 UNIX Domain Socket。

我們再查詢下 Kubernetes 集羣中的 SPIRE 條目,你將可以看到 sleep 的條目已註冊。

Entry ID         : bd4457af-c55c-4d8c-aee2-4d477a79b465
SPIFFE ID        : spiffe://example.org/ns/default/sa/sleep
Parent ID        : spiffe://example.org/k8s-workload-registrar/demo-cluster/node/gke-jimmy-cluster-default-pool-d5041909-3atb
Revision         : 1
TTL              : default
Selector         : k8s:node-name:gke-jimmy-cluster-default-pool-d5041909-3atb
Selector         : k8s:ns:default
Selector         : k8s:pod-uid:81f116ce-6538-4492-a78a-98b163b58310
DNS name         : sleep-9df96d88-tvl49
DNS name         : sleep.default.svc

再檢查下 sleep 的 SVID。

istioctl proxy-config secret $SLEEP_POD -o json | jq -r \
'.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | base64 --decode > chain.pem

上面這條命令獲取 sleep pod 中 Envoy 的 secret 並解析其中的 TLS 證書,將其 base64 解碼後保存到 chain.pem 文件中。

openssl x509 -in chain.pem -text | grep SPIRE

使用 OpenSSL 解析其中的發行商,將得到如下的結果。

Subject: C=US, O=SPIRE, CN=sleep-9df96d88-tvl49

SPIFFE 還支持聯邦,你可以為 SPIRE Agent SDS 配置 bundle,還需要為 pod 增加註解 spiffe.io/federatesWith: "<trust.domain>" ,然後 Envoy 就會向 SPRIE Server 獲取 bundle 了,詳細步驟請參考 Istio 官方文檔

以上就是在 Istio 中集成 SPIRE 的全過程。

參考