署名検証ポリシーと資格情報を立てる
目標
cosignの署名とattestationを検証するKyvernoポリシーを書き、KyvernoとPodがそれぞれプライベートレジストリにアクセスするために必要な認証情報のリソースを、実際のクラスターに作成します。
なぜ重要なのか
署名検証ポリシーは、文法よりも運用条件のほうが難しいものです。署名があるか、その署名を問い合わせられるか、問い合わせた署名が信頼する主体のものか、がそれぞれ別の失敗モードで、症状はどれも「Podが起動しない」で同じです。特にattestors.countを忘れるとentriesのすべてが通過しなければならなくなるため、鍵を追加した途端にすべてが止まり、mutateDigestを有効にすると、タグを上書きしてPodを再作成していたパイプラインが静かに無力化されます。このラボでは、そうした落とし穴をマニフェストの上で先に踏んでみます。
ステップ
/root/kca-verify/ディレクトリを作成し、verify-keys.yamlにkindとしてClusterPolicy、metadata.nameとしてkca-verify-images、spec.rules[0].nameとしてverify-signatureを書いてください。verifyImages[0].imageReferencesに10.0.0.202/platform/*を入れ、attestors[0].entries[0].keys.publicKeysにBEGIN PUBLIC KEYで始まるPEM形式の公開鍵を入れてください。- 同じ
attestors[0]でcountを1にし、entriesを2つにしてください。2つ目の項目はkeylessで、urlはhttps://fulcio.sigstore.dev、rekor.urlはhttps://rekor.sigstore.dev、issuerはhttps://token.actions.githubusercontent.comにし、subjectには.github/workflows/を含むワークフローのパスを書いてください。 - 同じ
verifyImages[0]でrequired、verifyDigest、mutateDigestをすべてtrueにし、skipImageReferencesに10.0.0.202/platform/legacy-*を入れてください。そしてspec.webhookConfiguration.timeoutSecondsを20から30の間の値にし、非推奨(deprecated)のspec.webhookTimeoutSecondsは使わないでください。 /root/kca-verify/verify-attestations.yamlにkindとしてClusterPolicyを書き、verifyImages[0].attestationsに2つの項目を入れてください。1つはtypeがhttps://slsa.dev/provenance/v1で、conditions[0].all[0].keyでビルダーの身元を検査するもの、もう1つはtypeがhttps://cyclonedx.org/bom/v1.4で、conditions[0].all[0].keyでlog4j-coreコンポーネントのバージョンを検査するものです。/root/kca-verify/allowed-registries.yamlにkindとしてClusterPolicy、metadata.nameとしてkca-allowed-registriesを書いてください。spec.rules[0].validate.foreach[0].listはrequest.object.spec.[initContainers, containers][]にし、波括弧で囲まないでください。deny.conditions.all[0].operatorはAnyNotIn、valueのリストに10.0.0.202/platform/*を入れ、validate.failureActionはEnforceにしてください。- クラスターにネームスペース
kca-verifyを作成し、その中にSecretharbor-credを実際に作成してください。typeはkubernetes.io/dockerconfigjsonで、サーバーは10.0.0.202です。 - 同じネームスペースにServiceAccount
kca-deployerを作成してimagePullSecretsにharbor-credを付け、Deploymentkca-paymentsを実際に作成して、serviceAccountNameをkca-deployerにし、コンテナイメージを10.0.0.202/platform/payments@sha256:の後ろに64桁の16進数を付けた形で書いてください。
参考
kubectl create secret docker-registry harbor-cred --docker-server=10.0.0.202 --docker-username=robot --docker-password=... -n kca-verifyを使うと、typeとキーが自動的に合います。- 64桁のダイジェストは、
printf 'kca' | sha256sumのような方法で適当な値を作って使ってもかまいません。 - よくある間違い1: attestorsでentriesだけを増やしてcountを忘れること。すべてが通過しなければならない状態になります。
- よくある間違い2: foreachのlistを波括弧で囲むこと。listはJMESPath式そのものを受け取ります。
静的な鍵によるverifyImages
/root/kca-verify/ディレクトリを作成し、verify-keys.yamlにkindとしてClusterPolicy、metadata.nameとしてkca-verify-images、spec.rules[0].nameとしてverify-signatureを書いてください。verifyImages[0].imageReferencesに10.0.0.202/platform/*を入れ、attestors[0].entries[0].keys.publicKeysにBEGIN PUBLIC KEYで始まるPEM形式の公開鍵を入れてください。
verifyImagesはルールの中の配列で、各項目がimageReferencesとattestorsを持ちます。公開鍵はPEM文字列を複数行で入れます。
keylessの追加とcountの意味
同じattestors[0]でcountを1にし、entriesを2つにしてください。2つ目の項目はkeylessで、urlはhttps://fulcio.sigstore.dev、rekor.urlはhttps://rekor.sigstore.dev、issuerはhttps://token.actions.githubusercontent.comにし、subjectには.github/workflows/を含むワークフローのパスを書いてください。
entriesを増やしただけでは、すべての項目が通過しなければなりません。鍵の入れ替え期間を無停止で乗り切るには、いくつ通過すればよいかを明示する必要があります。
ダイジェストの固定と例外、そしてタイムアウト
同じverifyImages[0]でrequired、verifyDigest、mutateDigestをすべてtrueにし、skipImageReferencesに10.0.0.202/platform/legacy-*を入れてください。そしてspec.webhookConfiguration.timeoutSecondsを20から30の間の値にし、非推奨(deprecated)のspec.webhookTimeoutSecondsは使わないでください。
3つのブール値は、それぞれ検証の強制、ダイジェスト使用の強制、タグのダイジェストへの置き換えを意味します。例外はポリシーを複製せず、専用のフィールドに書いてください。レジストリへの往復が入るポリシーは、Webhookのタイムアウトのデフォルト値では足りません。
provenanceとSBOMの条件
/root/kca-verify/verify-attestations.yamlにkindとしてClusterPolicyを書き、verifyImages[0].attestationsに2つの項目を入れてください。1つはtypeがhttps://slsa.dev/provenance/v1で、conditions[0].all[0].keyでビルダーの身元を検査するもの、もう1つはtypeがhttps://cyclonedx.org/bom/v1.4で、conditions[0].all[0].keyでlog4j-coreコンポーネントのバージョンを検査するものです。
attestationsは、typeでどの陳述かを選び、conditionsでその内容を検査します。署名だけを見ても、誰がビルドしたかは検査していないことになります。
許可レジストリの検査
/root/kca-verify/allowed-registries.yamlにkindとしてClusterPolicy、metadata.nameとしてkca-allowed-registriesを書いてください。spec.rules[0].validate.foreach[0].listはrequest.object.spec.[initContainers, containers][]にし、波括弧で囲まないでください。deny.conditions.all[0].operatorはAnyNotIn、valueのリストに10.0.0.202/platform/*を入れ、validate.failureActionはEnforceにしてください。
foreachのlistは、波括弧で囲みません。初期化コンテナまで一緒に巡回する必要があり、許可リストの外にあるときに拒否する演算子を選んでください。
レジストリ認証情報のSecret
クラスターにネームスペースkca-verifyを作成し、その中にSecret harbor-credを実際に作成してください。typeはkubernetes.io/dockerconfigjsonで、サーバーは10.0.0.202です。
ここからは実際のクラスターです。レジストリ専用のSecretのタイプがあり、kubectlにはそれを作成するサブコマンドがあります。
ServiceAccountとダイジェスト固定のデプロイ
同じネームスペースにServiceAccount kca-deployerを作成してimagePullSecretsにharbor-credを付け、Deployment kca-paymentsを実際に作成して、serviceAccountNameをkca-deployerにし、コンテナイメージを10.0.0.202/platform/payments@sha256:の後ろに64桁の16進数を付けた形で書いてください。
ServiceAccountにimagePullSecretsを付けると、そのアカウントで起動するPodが引き継ぎます。イメージはタグではなくダイジェストで固定された形でなければならず、sha256は64桁です。