Kubernetes CSR を使用したカスタム CA 統合

この機能には、Kubernetesバージョン1.18以上が必要です。

このタスクでは、Kubernetes CSR APIと統合するカスタム証明書認証局を使用して、ワークロード証明書をプロビジョニングする方法を示します。異なるワークロードは、異なる証明書署名者から証明書に署名させることができます。各証明書署名者は、事実上異なるCAです。同じ証明書署名者から発行された証明書を持つワークロードは、互いにmTLSで通信できますが、異なる署名者によって署名されたワークロードは通信できません。この機能は、Kubernetes CSR APIを使用して証明書に署名するIstiodにリンクされた軽量コンポーネントであるChironを活用します。

この例では、オープンソースのcert-managerを使用します。Cert-managerは、バージョン1.4以降で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用に、3つの自己署名クラスタ発行者 `istio-system`、`foo`、`bar` を作成します。注:名前空間発行者や他のタイプの発行者も使用できます。

    $ 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/^/        /')

デフォルトの cert-signer 情報を使用して 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. `bar` と `foo` の名前空間を作成します。

    $ kubectl create ns bar
    $ kubectl create ns foo
    
  3. `bar` 名前空間のワークロードの証明書署名者を定義するために、`bar` 名前空間に `proxyconfig-bar.yaml` をデプロイします。

    $ 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` 名前空間のワークロードの証明書署名者を定義するために、`foo` 名前空間に `proxyconfig-foo.yaml` をデプロイします。

    $ 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. `foo` と `bar` の名前空間に、`httpbin` と `curl` のサンプルアプリケーションをデプロイします。

    $ 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` ポッドの名前に設定します。

    $ export CURL_POD_FOO=$(kubectl get pod -n foo -l app=curl -o jsonpath={.items..metadata.name})
    
  2. `foo` 名前空間のサービス `curl` と `httpbin` 間のネットワーク接続を確認します。

    $ 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` 名前空間のサービス `curl` と `bar` 名前空間の `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が `CertificateSigningRequest` リソースを監視し、それらに対して動作するKubernetesコントローラーを実装する必要があります。

  • より優れたマルチテナンシー - 異なるワークロードに異なる証明書署名者を指定することにより、異なるテナントのワークロードの証明書を異なるCAで署名できます。

この情報は役に立ちましたか?
改善のための提案はありますか?

フィードバックありがとうございます!