新機能:プロジェクトの最新情報については、TwitterMastodonでご確認ください。

csi-driver-spiffe

csi-driver-spiffe は、Kubernetes 用の Container Storage Interface (CSI) ドライバプラグインであり、cert-manager と連携して動作するように設計されています。

Kubernetes Pod のマウントに、SPIFFE SVID(X.509 証明書キーペアの形で)を透過的に配信します。

最終的な結果は、Kubernetes で実行されているすべての Pod が、最小限の設定で、信頼ドメインから SPIFFE ID ドキュメントを安全に要求できるようになることです。

これらのドキュメントは、次の特性を持っています。

  • 自動更新 ✔️
  • 秘密鍵はノードの仮想メモリから決して出ていかない ✔️
  • 各 Pod のドキュメントは一意である ✔️
  • ドキュメントは Pod と同じライフサイクルを共有し、Pod の終了時に破棄される ✔️
  • 信頼ドメイン内で mTLS を有効にする ✔️
...
volumeMounts:
- mountPath: "/var/run/secrets/spiffe.io"
name: spiffe
volumes:
- name: spiffe
csi:
driver: spiffe.csi.cert-manager.io
readOnly: true

コンポーネント

このプロジェクトは、ドライバと承認者の2つのコンポーネントに分かれています。

CSI ドライバ

CSI ドライバはクラスタ上で DaemonSet として実行され、証明書と秘密鍵を生成、要求し、管理するノード上の Pod にマウントする役割を担っています。CSI ドライバは tmpfs ディレクトリを作成および管理します。

CSI ボリュームが設定された Pod が作成されると、ドライバはローカルに秘密鍵を生成し、Pod と同じ名前空間に cert-manager CertificateRequest を作成します。

ドライバは CSI トークンリクエスト を使用します。これは、作成されている Pod のトークンがドライバに渡されることを意味します。

このトークンの詳細は、Pod の ID を表す SPIFFE ID の作成に使用され、トークンは SVID の実際の CertificateRequest の作成に使用されます。

証明書が設定された cert-manager 発行者によって署名されると、ドライバは秘密鍵と証明書を Pod のボリュームにマウントし、証明書の有効期限に基づいて証明書と秘密鍵を更新するために監視します。

承認者

個別の cert-manager 承認者 Deployment は、csi-driver-spiffe CertificateRequest の承認と拒否の管理を担当します。

承認者は、リクエストが次の条件を満たしていることを確認します。

  1. 許容されるキーの使用(鍵暗号化、デジタル署名、クライアント認証、サーバ認証)
  2. 強制された期間(デフォルトは1時間)と一致する要求された期間
  3. SAN または単一の URI SAN を除く、その他の識別可能な属性がないこと
  4. CertificateRequest を作成した ServiceAccount の SPIFFE ID である URI SAN
  5. 起動時に設定されたものと一致する SPIFFE ID 信頼ドメイン

承認者は、spiffe.csi.cert-manager.io/identity アノテーションを持つ CertificateRequest のみを考慮します。これは、csi-driver-spiffe によって作成されるすべてのリクエストに追加されます。

インストール

インストールガイド に csi-driver-spiffe のインストール方法が記載されています。

セキュリティに関する考慮事項

csi-driver-spiffe は、機密性の高い資格情報を扱うため、秘密にしておく必要があります。Kubernetes CSI ボリュームを使用する設計により、CSI ボリュームをマウントする Pod のみにアクセスを制限することが簡単になりますが、csi-driver-spiffe によって作成された秘密鍵を公開しないよう注意する必要があります。

csi-driver-spiffe は、常に発行のために使用している Pod のトークンを使用して、作成する SVID の CertificateRequest リソースを作成します。つまり

  1. 発行中、csi-driver-spiffe は Pod が実行できるあらゆる操作を実行できます。侵害された csi-driver-spiffe Pod はこれらの権限を悪用する可能性があることに注意してください。ただし、通常の動作では、CertificateRequest 権限のみが使用されます。
  2. 重要な点:csi-driver-spiffe を使用するすべての Pod は、CertificateRequest リソースを作成する権限を持っている必要があります。

Pod が CertificateRequest を作成する権限を持つ必要があるという要件は、重要なセキュリティ上の意味を持ちます。これらの権限を持つ Pod が侵害されると、任意の CertificateRequest リソースを作成でき、クラスタ内の任意の発行者を参照する可能性があります。

csi-driver-spiffe はクラスタ内で承認を使用する必要があるため、このリスクは承認によって軽減されます。

ただし、クラスタ内のその他の承認方法(approver-policy など)は、csi-driver-spiffe 承認者と重複せず、他の発行者へのアクセスを制限するように注意深く設定することが重要です。

たとえば、任意の Pod が ACME(Let's Encrypt)発行者を使用して発行することを許可する CertificateRequestPolicy リソースを使用すると、その Pod が csi-driver-spiffe を使用し、CertificateRequest リソースを作成する権限を持っている場合、侵害された Pod が公開で信頼できる証明書を発行できる可能性があります。

csi-driver-spiffe を安全に使用するには、クラスタで使用可能な承認方法を考慮し、Pod が使用することを意図していない発行者を対象としないように、これらの承認者を注意深く設定する必要があります。

使用方法

ドライバが正常にインストールされると、Pod はキーと SPIFFE 証明書の要求とマウントを開始できます。CertificateRequest の作成時に Pod の ServiceAccount が impersonate されるため、ボリュームを使用する予定のすべての ServiceAccount にその権限を与える必要があります。

ダミー Deployment を使用した例のマニフェスト

kubectl apply -f https://raw.githubusercontent.com/cert-manager/csi-driver-spiffe/ed646ccf28b1ecdf63f628bf16f1d350a9b850c1/deploy/example/example-app.yaml
kubectl exec -n sandbox \
$(kubectl get pod -n sandbox -l app=my-csi-app -o jsonpath='{.items[0].metadata.name}') \
-- \
cat /var/run/secrets/spiffe.io/tls.crt | \
openssl x509 --noout --text | \
grep "Issuer:"
# expected output: Issuer: CN = csi-driver-spiffe-ca
kubectl exec -n sandbox \
$(kubectl get pod -n sandbox -l app=my-csi-app -o jsonpath='{.items[0].metadata.name}') \
-- \
cat /var/run/secrets/spiffe.io/tls.crt | \
openssl x509 --noout --text | \
grep "URI:"
# expected output: URI:spiffe://foo.bar/ns/sandbox/sa/example-app

実行時設定

csi-driver-spiffe が実行時設定を有効にしてインストールされた場合、発行者設定について名前付き ConfigMap を監視します。その ConfigMap が存在し、有効な発行者参照が含まれている場合、その発行者が作成されたすべての CertificateRequest リソースに使用されます。

ConfigMap が削除された場合、または有効な発行者参照が含まれていない場合、無視されます。インストール時にデフォルトの発行者が指定されている場合は、そのデフォルトがフォールバックとして使用されます。有効な実行時設定が提供されず、デフォルトの発行者が指定されていない場合、有効な発行者が設定されるまで、発行(および Pod の作成)は失敗します。

実行時設定 ConfigMap の名前は、インストール時に app.runtimeIssuanceConfigMap Helm 値で設定されます。有効な ConfigMap には、issuer-nameissuer-kind、および issuer-group キーが含まれている必要があります。

my-issuer-name という名前の ClusterIssuer の ConfigMap を作成する例を以下に示します。

kubectl create configmap spiffe-issuer -n cert-manager \
--from-literal=issuer-name=my-issuer-name \
--from-literal=issuer-kind=ClusterIssuer \
--from-literal=issuer-group=cert-manager.io

FS グループ

特定のユーザーまたはグループで Pod を実行する場合、Unix ベースのファイルシステムのアクセス許可のために、デフォルトではボリュームを読み取ることができません。マウントボリュームのファイルグループは、次のボリューム属性を使用して指定できます。

...
securityContext:
runAsUser: 123
runAsGroup: 456
volumes:
- name: spiffe
csi:
driver: spiffe.csi.cert-manager.io
readOnly: true
volumeAttributes:
spiffe.csi.cert-manager.io/fs-group: "456"
kubectl apply -f https://raw.githubusercontent.com/cert-manager/csi-driver-spiffe/ed646ccf28b1ecdf63f628bf16f1d350a9b850c1/deploy/example/fs-group-app.yaml
kubectl exec -n sandbox $(kubectl get pod -n sandbox -l app=my-csi-app-fs-group -o jsonpath='{.items[0].metadata.name}') -- cat /var/run/secrets/spiffe.io/tls.crt | openssl x509 --noout --text | grep URI:
# expected output: URI:spiffe://foo.bar/ns/sandbox/sa/fs-group-app

ルート CA バンドル

⚠️ この機能は、trust-manager と比較して、機能が大幅に限定されており、使用および更新がはるかに困難です。trust-manager の使用をお勧めします。

デフォルトでは、CSI ドライバーはPodの秘密鍵と署名済み証明書のみをマウントします。csi-driver-spiffe は、オプションで、すべてのPodボリュームに書き込まれるボリュームから静的に定義されたCAバンドルをマウントするように構成することもできます。

CSI ドライバーがこのバンドルの変更(上書き、更新など)を検出すると、新しいバンドルは既存のすべてのボリュームに書き込まれます。

次の例では、Trust Domain ClusterIssuerで使用されるCA証明書をマウントします。

helm upgrade -i -n cert-manager cert-manager-csi-driver-spiffe jetstack/cert-manager-csi-driver-spiffe --wait \
--set "app.logLevel=1" \
--set "app.trustDomain=my.trust.domain" \
\
--set "app.runtimeIssuanceConfigMap=spiffe-issuer"
--set "app.issuer.name=csi-driver-spiffe-ca" \
--set "app.issuer.kind=ClusterIssuer" \
--set "app.issuer.group=cert-manager.io" \
\
--set "app.driver.volumes[0].name=root-cas" \
--set "app.driver.volumes[0].secret.secretName=csi-driver-spiffe-ca" \
--set "app.driver.volumeMounts[0].name=root-cas" \
--set "app.driver.volumeMounts[0].mountPath=/var/run/secrets/cert-manager-csi-driver-spiffe" \
--set "app.driver.sourceCABundle=/var/run/secrets/cert-manager-csi-driver-spiffe/ca.crt"
kubectl rollout restart deployment -n sandbox my-csi-app
kubectl exec -it -n sandbox $(kubectl get pod -n sandbox -l app=my-csi-app -o jsonpath='{.items[0].metadata.name}') -- ls /var/run/secrets/spiffe.io/
# expected output: ca.crt tls.crt tls.key