使用 Kubernetes CSR 整合自訂 CA

此功能需要 Kubernetes 版本 >= 1.18。

此任務示範如何使用與 Kubernetes CSR API 整合的自訂憑證授權單位來佈建工作負載憑證。不同的工作負載可以從不同的憑證簽署者取得其簽署的憑證。每個憑證簽署者實際上都是不同的 CA。預期從同一個憑證簽署者發出的憑證的工作負載可以彼此進行 mTLS 通訊,而由不同簽署者簽署的工作負載則無法。此功能利用了 Chiron,這是一個與 Istiod 連接的輕量級元件,使用 Kubernetes CSR API 簽署憑證。

在此範例中,我們使用 開源的 cert-manager。從 1.4 版本開始,Cert-manager 添加了 對 Kubernetes CertificateSigningRequests 的實驗性支援

在 Kubernetes 叢集中部署自訂 CA 控制器

  1. 請依照 安裝文件 部署 cert-manager。

    $ helm repo add jetstack https://charts.jetstack.io
    $ helm repo update
    $ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set featureGates="ExperimentalCertificateSigningRequestControllers=true" --set installCRDs=true
    
  2. 為 cert-manager 建立三個自我簽署的叢集發行者 istio-systemfoobar。注意:也可以使用命名空間發行者和其他類型的發行者。

    $ cat <<EOF > ./selfsigned-issuer.yaml
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: selfsigned-bar-issuer
    spec:
      selfSigned: {}
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: bar-ca
      namespace: cert-manager
    spec:
      isCA: true
      commonName: bar
      secretName: bar-ca-selfsigned
      issuerRef:
        name: selfsigned-bar-issuer
        kind: ClusterIssuer
        group: cert-manager.io
    ---
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: bar
    spec:
      ca:
        secretName: bar-ca-selfsigned
    ---
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: selfsigned-foo-issuer
    spec:
      selfSigned: {}
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: foo-ca
      namespace: cert-manager
    spec:
      isCA: true
      commonName: foo
      secretName: foo-ca-selfsigned
      issuerRef:
        name: selfsigned-foo-issuer
        kind: ClusterIssuer
        group: cert-manager.io
    ---
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: foo
    spec:
      ca:
        secretName: foo-ca-selfsigned
    ---
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: selfsigned-istio-issuer
    spec:
      selfSigned: {}
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: istio-ca
      namespace: cert-manager
    spec:
      isCA: true
      commonName: istio-system
      secretName: istio-ca-selfsigned
      issuerRef:
        name: selfsigned-istio-issuer
        kind: ClusterIssuer
        group: cert-manager.io
    ---
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: istio-system
    spec:
      ca:
        secretName: istio-ca-selfsigned
    EOF
    $ kubectl apply -f ./selfsigned-issuer.yaml
    

驗證是否為每個叢集簽發者建立密鑰

$ kubectl get secret -n cert-manager -l controller.cert-manager.io/fao=true
NAME                  TYPE                DATA   AGE
bar-ca-selfsigned     kubernetes.io/tls   3      3m36s
foo-ca-selfsigned     kubernetes.io/tls   3      3m36s
istio-ca-selfsigned   kubernetes.io/tls   3      3m38s

匯出每個叢集簽發者的根憑證

$ export ISTIOCA=$(kubectl get clusterissuers istio-system -o jsonpath='{.spec.ca.secretName}' | xargs kubectl get secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 -d | sed 's/^/        /')
$ export FOOCA=$(kubectl get clusterissuers foo -o jsonpath='{.spec.ca.secretName}' | xargs kubectl get secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 -d | sed 's/^/        /')
$ export BARCA=$(kubectl get clusterissuers bar -o jsonpath='{.spec.ca.secretName}' | xargs kubectl get secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 -d | sed 's/^/        /')

使用預設憑證簽署者資訊部署 Istio

  1. 使用以下配置使用 istioctl 在叢集上部署 Istio。ISTIO_META_CERT_SIGNER 是工作負載的預設憑證簽署者。

    $ cat <<EOF > ./istio.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      values:
        pilot:
          env:
            EXTERNAL_CA: ISTIOD_RA_KUBERNETES_API
      meshConfig:
        defaultConfig:
          proxyMetadata:
            ISTIO_META_CERT_SIGNER: istio-system
        caCertificates:
        - pem: |
    $ISTIOCA
          certSigners:
          - clusterissuers.cert-manager.io/istio-system
        - pem: |
    $FOOCA
          certSigners:
          - clusterissuers.cert-manager.io/foo
        - pem: |
    $BARCA
          certSigners:
          - clusterissuers.cert-manager.io/bar
      components:
        pilot:
          k8s:
            env:
            - name: CERT_SIGNER_DOMAIN
              value: clusterissuers.cert-manager.io
            - name: PILOT_CERT_PROVIDER
              value: k8s.io/clusterissuers.cert-manager.io/istio-system
            overlays:
              - kind: ClusterRole
                name: istiod-clusterrole-istio-system
                patches:
                  - path: rules[-1]
                    value: |
                      apiGroups:
                      - certificates.k8s.io
                      resourceNames:
                      - clusterissuers.cert-manager.io/foo
                      - clusterissuers.cert-manager.io/bar
                      - clusterissuers.cert-manager.io/istio-system
                      resources:
                      - signers
                      verbs:
                      - approve
    EOF
    $ istioctl install --skip-confirmation -f ./istio.yaml
    
  2. 建立 barfoo 命名空間。

    $ kubectl create ns bar
    $ kubectl create ns foo
    
  3. bar 命名空間中部署 proxyconfig-bar.yaml,以定義 bar 命名空間中工作負載的憑證簽署者。

    $ cat <<EOF > ./proxyconfig-bar.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: ProxyConfig
    metadata:
      name: barpc
      namespace: bar
    spec:
      environmentVariables:
        ISTIO_META_CERT_SIGNER: bar
    EOF
    $ kubectl apply  -f ./proxyconfig-bar.yaml
    
  4. foo 命名空間中部署 proxyconfig-foo.yaml,以定義 foo 命名空間中工作負載的憑證簽署者。

    $ cat <<EOF > ./proxyconfig-foo.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: ProxyConfig
    metadata:
      name: foopc
      namespace: foo
    spec:
      environmentVariables:
        ISTIO_META_CERT_SIGNER: foo
    EOF
    $ kubectl apply  -f ./proxyconfig-foo.yaml
    
  5. foobar 命名空間中部署 httpbincurl 範例應用程式。

    $ kubectl label ns foo istio-injection=enabled
    $ kubectl label ns bar istio-injection=enabled
    $ kubectl apply -f samples/httpbin/httpbin.yaml -n foo
    $ kubectl apply -f samples/curl/curl.yaml -n foo
    $ kubectl apply -f samples/httpbin/httpbin.yaml -n bar
    

驗證同一命名空間內 httpbincurl 之間的網路連線

當工作負載部署完成時,它們會發送帶有相關簽署者資訊的 CSR 請求。Istiod 將 CSR 請求轉發到自訂 CA 進行簽署。自訂 CA 將使用正確的叢集發行者簽回憑證。foo 命名空間下的工作負載將使用 foo 叢集發行者,而 bar 命名空間下的工作負載將使用 bar 叢集發行者。為了驗證它們確實是由正確的叢集發行者簽署的,我們可以驗證同一個命名空間下的工作負載可以進行通訊,而不同命名空間下的工作負載則無法通訊。

  1. CURL_POD_FOO 環境變數設定為 curl Pod 的名稱。

    $ export CURL_POD_FOO=$(kubectl get pod -n foo -l app=curl -o jsonpath={.items..metadata.name})
    
  2. 檢查 foo 命名空間中服務 curlhttpbin 之間的網路連線能力。

    $ kubectl exec "$CURL_POD_FOO" -n foo -c curl -- curl http://httpbin.foo:8000/html
    <!DOCTYPE html>
    <html>
      <head>
      </head>
      <body>
          <h1>Herman Melville - Moby-Dick</h1>
    
          <div>
            <p>
              Availing himself of the mild...
            </p>
          </div>
      </body>
    
  3. 檢查 foo 命名空間中服務 curlbar 命名空間中 httpbin 之間的網路連線能力。

    $ kubectl exec "$CURL_POD_FOO" -n foo -c curl -- curl http://httpbin.bar:8000/html
    upstream connect error or disconnect/reset before headers. reset reason: connection failure, transport failure reason: TLS error: 268435581:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED
    

清除

  • 移除命名空間並解除安裝 Istio 和 cert-manager

    $ kubectl delete ns foo
    $ kubectl delete ns bar
    $ istioctl uninstall --purge -y
    $ helm delete -n cert-manager cert-manager
    $ kubectl delete ns istio-system cert-manager
    $ unset ISTIOCA FOOCA BARCA
    $ rm -rf istio.yaml proxyconfig-foo.yaml proxyconfig-bar.yaml selfsigned-issuer.yaml
    

使用此功能的原因

  • 自訂 CA 整合 - 通過在 Kubernetes CSR 請求中指定簽署者名稱,此功能允許 Istio 使用 Kubernetes CSR API 介面與自訂憑證授權單位整合。這確實需要自訂 CA 實作一個 Kubernetes 控制器來監控 CertificateSigningRequest 資源並對其採取行動。

  • 更好的多租戶 - 通過為不同的工作負載指定不同的憑證簽署者,不同租戶的工作負載憑證可以由不同的 CA 簽署。

此資訊是否有用?
您有任何改進建議嗎?

感謝您的回饋!