SigstoreによるKubernetesマニフェストの署名と検証

**Sigstore** はソフトウェアサプライチェーンのセキュリティ強化のためのオープンソースプロジェクトで、コンテナイメージだけでなく Kubernetes マニフェストの署名と検証もサポートしています。 ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=This%20kubectl%20subcommand%20plugin%20enables,confirmed%20on%20a%20k8s%20cluster))その一環で提供されている `sigstore/k8s-manifest-sigstore` プラグインを使うと、Kubernetesリソース定義(YAMLマニフェスト)にデジタル署名を行い、展開時やクラスタ上でその真正性と改ざんされていないことを検証できます。 ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=This%20kubectl%20subcommand%20plugin%20enables,confirmed%20on%20a%20k8s%20cluster))

## kubectl-sigstoreプラグインによるマニフェスト署名

`k8s-manifest-sigstore` は `kubectl` のサブコマンドプラグインです。あらかじめプラグインをインストールしておけば、以下のように `kubectl sigstore sign` コマンドでマニフェストに署名できます（プラグインは `kubectl-sigstore` という実行ファイルですが、PATH上に置くことで `kubectl sigstore` として利用可能） ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=The%20plugin%20is%20a%20standalone,sigstore)) ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=Usage%3A%20kubectl%20sigstore%20,command))。例えば:

```shell
# マニフェストの署名（例）
kubectl sigstore sign -f manifest.yaml -k cosign.key --tarball no -o manifest-signed.yaml
```

上記コマンドでは `manifest.yaml` に対し、`cosign.key`（秘密鍵）を用いて署名し、その結果を `manifest-signed.yaml` に出力しています ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=1%24%20kubectl,signed.yaml))。`--tarball no` オプションによりOCIイメージへのパッケージングを行わず、署名情報を直接YAMLに埋め込むモードを使用しています。 ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=1%24%20kubectl,signed.yaml)) ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20option%20of%20signing%20and,to%20store%20signed%20resource%20definitions))署名実行後、`INFO[XXXX] signed manifest generated at manifest-signed.yaml` のようなメッセージが表示され、指定した出力先に署名付きマニフェストが生成されます ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=1%24%20kubectl,signed.yaml))。

生成された署名付きマニフェスト（`manifest-signed.yaml`）には、`metadata.annotations` に以下のような情報が追加されています。

```yaml
metadata:
  annotations:
    cosign.sigstore.dev/message: <base64化されたマニフェスト本文>
    cosign.sigstore.dev/signature: <署名データ（Base64）>
```

このようにマニフェスト内に署名メタデータを埋め込むことで、マニフェスト単体で署名と内容を一緒に管理できます。**GitOpsワークフローで運用する場合**は、OCIレジストリを使う方法よりもこの埋め込み方式が適しています。 ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20option%20of%20signing%20and,to%20store%20signed%20resource%20definitions))実際、Kyvernoなどのポリシーエンジンでも「YAML署名」方式はGitリポジトリ中心の運用と相性が良いとされています ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20option%20of%20signing%20and,to%20store%20signed%20resource%20definitions))。

> 💡 **補足:** デフォルトではプラグインはマニフェストを一時的にtarball (アーカイブ)にまとめてOCIコンテナイメージとして登録し、そのイメージに対してCosign署名を行う方式（`--tarball=yes`）を取ります。しかしGitOps環境ではイメージ登録を伴うと煩雑になるため、`--tarball=no` を指定してYAML自体に署名情報を載せる方法が推奨されていま ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20option%20of%20signing%20and,to%20store%20signed%20resource%20definitions)) ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=However%2C%20the%20OCI%20approach%20may,use%20cases%2C%20beyond%20policy%20resources))】。後者の場合、OCIレジストリは不要です。

### 署名に使用する鍵 (キーレス署名の利用)

署名に使う鍵は、以下の2通りから選択できます。

- **既存の鍵ペアを使用する:** 事前に Cosign 等で作成した秘密鍵・公開鍵ペアを用意し、署名コマンドに `-k <秘密鍵ファイル>` オプションで指定します。上記例では `cosign.key` が秘密鍵、対応する公開鍵は後で検証に使用します。プラグイン内部では Cosign を利用して署名するため、パスフレーズ付き鍵の場合は実行時にパスワード入力を求められま ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=1%24%20kubectl,signed.yaml))】。

- **キーレス(keyless)署名を利用する:** あえて長期の秘密鍵を持たず、その場で一時的に発行される証明書を用いて署名する方法です。`kubectl sigstore sign` 実行時に `-k` オプションを指定しなければ**自動的にキーレス署名モード**になりま ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=,yes))】。キーレス署名では Sigstore の公開CAである **Fulcio** が発行する短期証明書を利用し、対応する公開鍵証明書と署名がTransparency Log (Rekor)に記録されます。キーレス署名を行うためには、実行環境に対応するアイデンティティ情報（OIDCトークンなど）を用意し、`COSIGN_EXPERIMENTAL=1` 環境変数を有効にする必要があります。（FulcioがGitHubやOIDCプロバイダ経由の本人確認を行うため ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=In%20addition%20to%20adding%20support,walk%20through%20a%20little%20demo))】。例えばGitHub Actions上で実行する場合や、開発者が自分のマシンでOIDCプロバイダにログインしている場合にキーレス署名が可能です。  

## ServiceAccount経由での鍵利用と署名

Kubernetesクラスタ自体が内部で使用する公開鍵・秘密鍵（例: Service Accountトークン署名用の鍵など）を**直接**取得してマニフェスト署名に使うことはできません。クラスタの証明書や鍵はKubernetesの内部用途に限定されており、アプリケーションから参照することは通常できないためです。しかし、**ServiceAccountを活用したキーレス署名**という形でクラスタの身元情報を署名に利用することが可能です。

Kubernetes (v1.20+) では **Service Account Token Volume Projection** 機能により、Pod内にそのPodが属するServiceAccountのOIDCトークンをマウントできま ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=Kubernetes%20has%20a%20feature%20called,cluster%20authentication%20%28AWS%2C%20GCP))】。EKSやGKEなど一部のマネージドKubernetesでは、このServiceAccountトークンに**外部で検証可能なOIDC issuer**を設定しており、SigstoreのFulcioはそれらを信頼する構成になっていま ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=In%20addition%20to%20adding%20support,walk%20through%20a%20little%20demo))】。つまり、**クラスター上のPodからServiceAccountトークンを使ってFulcioに証明書を発行してもらい、その証明書で署名する**ことができます。これにより、Podが**自分のServiceAccountに紐付いたアイデンティティ**でマニフェストに署名することが可能になります。実際、Fulcioが発行する証明書には署名者のServiceAccount識別子（例: `https://kubernetes.io/namespaces/default/serviceaccounts/<SA名>`）がSubjectとして含ま ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=%22Subject%22%3A%20%22https%3A%2F%2Fkubernetes.io%2Fnamespaces%2Fdefault%2Fserviceaccounts%2Fecr))】、後で検証時に「どのクラスターのどのServiceAccountが署名したか」を確認できます。

**鍵管理の観点からのポイント:**

- CI/CDのジョブをKubernetes上で動かす場合は、そのジョブ用のServiceAccountに適切なIAM権限(OIDCプロバイダ連携)を与え、上記のキーレス署名を利用すると安全です。長期の秘密鍵を配布せずに済み、署名用の証明書は短期間で無効になるためセキュアです ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=In%20addition%20to%20adding%20support,walk%20through%20a%20little%20demo))】

- 自前クラスタでOIDCプロバイダが無い場合や、キーレス署名を使えない場合は、署名用の秘密鍵をKubernetesの**Secret**リソースに格納し、ServiceAccountにそのSecretをマウントさせて利用する方法もあります。例えば`kubectl create secret generic cosign-key --from-file=cosign.key`でSecretに登録し、CI/CD Podでマウントして `kubectl sigstore sign -k /path/to/cosign.key ...` とする運用です。ただしSecret管理には十分注意し、最低限の権限のServiceAccountでのみ参照させるようにしてください。

## GitOps/CDパイプラインでのマニフェスト整合性検証

継続的デプロイ（CD）やGitOpsのパイプラインにおいて、デプロイ前にマニフェストの整合性を検証することは重要です。Sigstoreを使うことで、以下のような手順で検証を自動化できます。

1. **署名の検証**: デプロイ対象のマニフェストに対して `kubectl sigstore verify` コマンドを実行し、署名が正当であることを確認します。例えば、鍵ペア方式で署名されている場合は公開鍵を指定して検証します。

    ```shell
    # 署名付きマニフェストの検証（公開鍵を使用する例）
    kubectl sigstore verify -f manifest-signed.yaml -k cosign.pub
    ```

    上記では `cosign.pub`（公開鍵）に対してマニフェストの署名を検証しています。何もエラーが出なければ署名は有効（改ざんされていない）です。署名者の公開鍵が信頼できるものであることを事前に確認しておく必要があります。

    キーレス署名の場合、`-k` オプションは不要です（指定しなければプラグインがSigstoreの公開証明書を用いて検証しま ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=,less%20verification))】）。例えば:

    ```shell
    # キーレス署名されたマニフェストの検証
    kubectl sigstore verify -f manifest-signed.yaml
    ```

    キーレスの場合、検証時にTransparency Log (Rekor)を参照して署名の存在証明を確認したり、Fulcioのルート証明書で署名者の証明書を検証する処理が行われます。必要に応じて `--config` オプションで検証ポリシー（許容する証明書のIssuerやSubjectなど）を指定することもできま ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=Flags%3A%20,less))】。

2. **継続的インテグレーションへの組み込み**: 上記の検証コマンドをCI/CDパイプラインのデプロイ前ステップに組み込みます。例えば、GitOpsツール（Argo CDやFluxなど）であれば、クラスタにリソースを適用する直前にスクリプトで `kubectl sigstore verify` を実行し、失敗したらデプロイを停止するといったフローです。GitOpsツール自体にフックを仕込めない場合でも、デプロイ用マニフェストを適用する前に別のジョブで検証する仕組みを設けられます。

3. **クラスタ内での強制** (オプション): パイプライン内での検証に加えて、クラスタ側でも署名検証を**強制**することで二重の防御とすることができます。たとえば **Kyverno** ポリシーを使用すると、署名付きマニフェストしか受け付けないルールを設定できます ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20declaration%20can%20then%20be,signed%20using%20a%20static%20key)) ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=16%20%20%20%20,23%20MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEStoX3dPCFYFD2uPgTjZOf1I5UFTa))】下記は**例として**Kyvernoのポリシーで特定のリソース（Secret）に対し署名検証を要求するものです。

    ```yaml
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: validate-secrets
    spec:
      validationFailureAction: Enforce
      rules:
        - name: require-signed-secret
          match:
            any:
            - resources:
                kinds: ["Secret"]
          validate:
            manifests:
              attestors:
                - entries:
                    - keys:
                        publicKeys: |-
                          -----BEGIN PUBLIC KEY-----
                          (ここに署名者の公開鍵を貼り付け)
                          -----END PUBLIC KEY-----
    ```
    上記のようにKyvernoのポリシーで公開鍵を登録しておくと、Kubernetes APIサーバへの`kubectl apply`要求時にAdmission Webhookによってマニフェスト内の署名が検証されま ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=16%20%20%20%20,23%20MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEStoX3dPCFYFD2uPgTjZOf1I5UFTa))】。署名が不正だったり改ざんが検知された場合、そのリソースの作成や更新は拒否されます。実際、署名済みマニフェストの内容を改変して適用しようとすると以下のようにエラーとなります（署名時と異なる内容が検出された旨が表示される ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=4%20policy%20Secret%2Fdefault%2Fmysecret%20for%20resource,europe))】。

    ```shell
    $ kubectl apply -f secret-signed.yaml 
    Error from server: ... admission webhook "validate.kyverno.svc-fail" denied the request:
      policy validate-secrets: manifest verification failed; verifiedCount 0; requiredCount 1;
      message .attestors[0].entries[0].keys: failed to verify signature. diff found;
      {"items":[{"key":"metadata.labels.location","values":{"after":"asia","before":"europe"}}]}
    ```

    このように検証ポリシーを導入しておけば、パイプライン外から誤って改変されたマニフェストが適用される事故も防げます。

> 💡 **ワンステップ検証と適用:** `kubectl-sigstore` プラグインには、検証に成功した場合のみリソースを作成する `apply-after-verify` サブコマンドも用意されてい ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=Create%20resource%20with%20a%20k8s,manifest%20file%20after%20verifying%20signature))0】。例えば `kubectl sigstore apply-after-verify -f manifest-signed.yaml` と実行すれば、署名が有効なときだけそのマニフェストを`kubectl apply`する、という処理を1コマンドで行えます。簡易な環境であればこのコマンドを直接CIパイプラインで使うことも可能です。

## Sigstoreを用いたマニフェスト署名のベストプラクティス

最後に、Kubernetesマニフェストの署名/検証におけるベストプラクティスをまとめます。

- **鍵管理とキーレス署名:** 長期運用する秘密鍵の管理には細心の注意が必要です。可能であればSigstoreのキーレス署名を活用し、短期の証明書ベースで署名することで秘密鍵の漏洩リスクを低減することが推奨されてい ([Latest Integrity Shield protects Red Hat Advanced Cluster Management for Kubernetes policies](https://www.redhat.com/en/blog/latest-integrity-shield-protects-red-hat-advanced-cluster-management-for-kubernetes-policies#:~:text=First%2C%20there%20is%20a%20major,signatures%2C%20including%20support%20for%C2%A0keyless%20signing))0】。どうしても鍵ペアを使う場合は、KMSやVaultに保管し定期的なローテーションを検討してください。

- **署名の埋め込み:** 前述の通り、マニフェストYAML自体に署名を埋め込む形式はGitOpsとの親和性が高く、署名付きマニフェストファイルをそのままGit管理でき ([KDP/proposals/yaml_signing_and_verification.md at main · kyverno/KDP · GitHub](https://github.com/kyverno/KDP/blob/main/proposals/yaml_signing_and_verification.md#:~:text=This%20option%20of%20signing%20and,to%20store%20signed%20resource%20definitions))3】。OCIイメージとしてマニフェストをパッケージする方法もありますが、GitOps中心の運用ではシンプルにYAML署名を利用する方が管理しやすいでしょう。

- **CI/CDとAdmissionで二重チェック:** 開発プロセスではCI/CDパイプライン上で署名検証を自動化し、署名が有効でないマニフェストはデプロイしないようにします。また、クラスタ側でもKyvernoやSigstore Policy Controllerなどの仕組みで署名検証ポリシーを導入し、署名のないリソースや検証に失敗したリソースが適用されないようにすると万全で ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=4%20policy%20Secret%2Fdefault%2Fmysecret%20for%20resource,europe))3】

- **署名者の信頼性確保:** 検証時に使用する公開鍵や証明書の信頼性を社内で管理しましょう。例えば組織内の開発者ごとにキーを発行し、その公開鍵一覧を検証ポリシー（Kyvernoの`publicKeys`リスト等）に登録しておくことで、許可された署名者以外の署名は通さないようにできます。またキーレス署名の場合でも、Fulcio発行証明書のIssuerやSubjectに基づいて特定の原本（例: 特定のGitHubリポジトリのCI、特定クラスタのServiceAccountなど）からの署名だけを許可する設定も検討してください。

- **透明性ログの活用:** SigstoreのTransparency Log (**Rekor**) に署名記録を残すことで、後から誰がいつどの内容を署名したかを追跡でき、万一鍵が漏洩した場合でも過去の署名を監査できます。Cosignおよび`kubectl-sigstore`プラグインはデフォルトでRekorに記録を残す動作をします。オフライン環境など特別な場合を除き、透明性ログへの記録は有効にしておくことを推奨します。

以上の手順とベストプラクティスに従うことで、`sigstore/k8s-manifest-sigstore` を使ったKubernetesマニフェストの署名と検証を安全かつ効果的に導入できます。開発者がマニフェストに署名し、CDパイプラインでその署名を検証、クラスタ側でも不正なリソースを拒否することで、GitOpsの運用におけるマニフェスト改ざんリスクを大幅に低減できるでしょう。

**参考資料・リンク:** 発展的な話題や実装例については、公式のSigstoreドキュメントやブログ記事も参照してくださ ([GitHub - sigstore/k8s-manifest-sigstore: kubectl plugin for signing Kubernetes manifest YAML files with sigstore](https://github.com/sigstore/k8s-manifest-sigstore#:~:text=This%20kubectl%20subcommand%20plugin%20enables,confirmed%20on%20a%20k8s%20cluster)) ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=In%20addition%20to%20adding%20support,walk%20through%20a%20little%20demo))4】たとえば、Sigstore公式ブログにはKubernetes上でのキーレス署名デモが紹介されてい ([Zero-friction “keyless signing” with Kubernetes](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-kubernetes#:~:text=In%20addition%20to%20adding%20support,walk%20through%20a%20little%20demo))4】。また、Kyvernoのドキュメントにはマニフェスト署名検証ポリシーの具体例が掲載されてい ([Validate Rules | Kyverno](https://release-1-11-0.kyverno.io/docs/writing-policies/validate/#:~:text=16%20%20%20%20,23%20MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEStoX3dPCFYFD2uPgTjZOf1I5UFTa))0】。SigstoreとKubernetesの統合は日々進化している領域なので、最新のプロジェクト情報やコミュニティのベストプラクティスもウォッチすると良いでしょう。