📦

sigstoreでコンテナに署名する(with key)

2022/06/17に公開

要約

  • cosignというsigstoreの提供するツールを使ってコンテナに署名して同じレジストリに保存する事が出来る
  • sigstoreではkeylessで署名する為の仕組みを構築しているが、cosignにはkeyを管理する方法もあるのでここではこれを説明する

cosignのインストール

環境に応じていくつかインストール方法があります
https://docs.sigstore.dev/cosign/installation

  • 自分でビルド
    go install github.com/sigstore/cosign/cmd/cosign@latest
    
  • Binaryを取得 (Release 1.6.0)
    wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign-linux-amd64"
    mv cosign-linux-amd64 /usr/local/bin/cosign
    chmod +x /usr/local/bin/cosign
    
  • RPM (Release 1.6.0)
    wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign-1.6.0.x86_64.rpm"
    rpm -ivh cosign-1.6.0.x86_64.rpm
    
  • DEB (Release 1.6.0)
    wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign_1.6.0_amd64.deb"
    dpkg -i "cosign_1.6.0_amd64.deb
    
  • Arch Linux
    pacman -S cosign
    
  • Alpine Linux
    apk add cosign sget
    

なおYubiKey等のハードウェアトークンを使う機能はデフォルトで有効になっていないため自分でビルドする必要があります

go build -tags=pivkey,pkcs11key ./cmd/cosign

鍵の生成

sigstoreではkeyless署名を実現するために色々な試みが行われていますが、鍵を管理する方式がデフォルトです。

cosign generate-key-pair

で秘密鍵のパスワードを入力した後、現在のディレクトリにcosign.key(秘密鍵)とcosign.pub(公開鍵)を生成されます。

また手元に秘密鍵をおかずにGitLab CIの変数として保存しておくことも出来ます:

export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxx  # apiの権限が必要
cosign generate-key-pair gitlab://termoshtt/sigstore-testing

Generated variables in GitLab CI
既に変数が存在しているとエラーになります(上書きはされない)。

署名

まず署名に使うコンテナを用意します。前回で説明したttl.shを使います:

echo "FROM alpine" > Dockerfile
IMAGE_NAME=$(uuidgen)
docker build -t ttl.sh/${IMAGE_NAME}:1h .
docker push ttl.sh/${IMAGE_NAME}:1h

このコンテナに署名するには秘密鍵としてcosign.keyファイルを使う場合は:

cosign sign --key cosign.key ttl.sh/${IMAGE_NAME}:1h

GitLabに保存した鍵を使う場合は--keygenerate-key-pairと同じようにgitlab://<user>/<repo>を指定します:

cosign sign --key gitlab://termoshtt/sigstore-testing ttl.sh/${IMAGE_NAME}:1h

GitHubでも同じ様にGitHub Actionsの変数として生成することは出来ますが、GitHubにはActionsの変数を取得するAPIが存在していないためActions以外の環境ではその鍵を使って署名出来ません。

--keyを省略するとCOSIGN_PRIVATE_KEY環境変数から鍵を読み取り、COSIGN_PASSWD環境変数からパスワードを読み込みます。GitHub/GitLab上に鍵を作成した場合は自動的にこの変数に保存されるので指定する必要はありません。

cosign signは署名した内容を同じレジストリの別タグに保存します。タグはコンテナイメージのdigestを使って決めます。digestはImage IDとは別に振られるハッシュ値なので注意です。
https://docs.docker.jp/engine/reference/commandline/images.html#digest

cosign triangulate ttl.sh/${IMAGE_NAME}:1h

で書き込まれるコンテナのイメージ名(タグ込み)が表示されます。triangulate(三角測量)なのはコンテナのイメージとdigestから署名のタグを定めているからなのでしょうか? このイメージはtarになっていないのでdocker pullすることは出来ません。例えばcraneなどを使ってメタデータを表示できます::

crane manifest $(cosign triangulate ttl.sh/${IMAGE_NAME}:1h) | jq

検証

コンテナの署名を検証するには公開鍵を指定します:

cosign verify --key cosign.pub ttl.sh/${IMAGE_NAME}:1h

これは指定された公開鍵に対して1つでも有効な署名があったら正常終了します。というのも、コンテナには複数の鍵で署名できます。例えば上で説明した通りローカルに生成した鍵とGitLabに生成した鍵でそれぞれ署名した場合、同じタグに2つの署名が保存されます(これはコンテナのレイヤーとして処理されます)。verifyコマンドはこれの内1つでも有効な署名があるかどうかを検証します。

非対称鍵を用いた署名における一般的な事項ですが、ここで検証しているのはあくまで公開鍵が正しいコンテナ配布者により公開されたものという事が前提で、この鍵が正しいものかは別途確認しなければいけない事項です。

GitHubで編集を提案

Discussion