Skip to content

コンテナー イメージの署名と共同署名ツールによる検証

⚡ 必要条件 nerdctl >= 0.15

cosign は、コンテナイメージに署名して検証できるツールです。 公開鍵と秘密鍵のペア、または公開鍵を使わないキーレスサポートがあります。

Keylessは、自動的にエフェメラルキーとfulcio ルート CAの証明書を使用します。署名は、署名日の証明を自動的に提供できるRekor Transparency Logに格納されます。

Cosignは、署名中にプロンプトを使用して以下のステートメントを確認します。Nerdctlは、Cosignコマンドに--yesを追加して、追加のプロンプトを抑止します。 Cosign による署名で Nerdctl push を使用すると、ユーザーはステートメントに同意したことになります。

この署名された成果物には、個人を特定できる情報が含まれている可能性があることに注意してください。
認証に使用するアカウントに関連付けられたメールアドレスが含まれる場合があります。
情報は成果物に署名するために使用され、公開されている透明性ログに保存され、後で削除することはできません。

「y」と入力すると、この情報を透明性ログに永続的に保存することに同意したことになります。

コンテナーの署名と検証機能は、cosign を使用して nerdctlpush コマンドとpull コマンドで有効にできます 内部的には、 コンテナイメージをプッシュする際に--signフラグを指定し、 コンテナー イメージをプルする際に--verify フラグを指定しています。

環境準備

# Create a sample Dockerfile
$ cat <<EOF | tee Dockerfile.dummy
FROM alpine:latest
CMD [ "echo", "Hello World" ]
EOF

ベースイメージ (この場合は alpine:latest) は、ビルドされたコンテナーイメージの検証は行われません。 コンテナー イメージ自体は、署名後にのみ検証します。

# Build the image
$ nerdctl build -t devopps/hello-world -f Dockerfile.dummy .

# Generate a key-pair: cosign.key and cosign.pub
$ cosign generate-key-pair

# Export your COSIGN_PASSWORD to prevent CLI prompting
$ export COSIGN_PASSWORD=$COSIGN_PASSWORD

プッシュ中にコンテナー イメージに署名します。

# Sign the image with Keyless mode
$ nerdctl push --sign=cosign devopps/hello-world

# Sign the image and store the signature in the registry
$ nerdctl push --sign=cosign --cosign-key cosign.key devopps/hello-world

プル中にコンテナー イメージを確認します。

注意:--verifyフラグを渡した場合、一致する署名がない場合、イメージはプルされません。

注意:v キーレスフローを機能させるには、--cosign-certificate-identity または --cosign-certificate-identity-regexp のいずれかと、--cosign-certificate-oidc-issuer または --cosign-certificate-oidc-issuer-regexp を設定する必要があります。--verify=cosign で有効に扱かわれる、Fulcio 証明書で想定される OIDC 発行者 (例: https://token.actions.githubusercontent.com または https://oauth2.sigstore.dev/auth)。

# Verify the image with Keyless mode
$ nerdctl pull --verify=cosign --certificate-identity=name@example.com --certificate-oidc-issuer=https://accounts.example.com devopps/hello-world
INFO[0004] cosign:
INFO[0004] cosign: [{"critical":{"identity":...}]
docker.io/devopps/nginx-new:latest:                                               resolved       |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:0910d404e58dd320c3c0c7ea31bf5fbfe7544b26905c5eccaf87c3af7bcf9b88: done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:1de1c4fb5122ac8650e349e018fba189c51300cf8800d619e92e595d6ddda40e:   done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 1.4 s                                                                    total:  1.3 Ki (928.0 B/s)

# You can not verify the image if it is not signed
$ nerdctl pull --verify=cosign --cosign-key cosign.pub devopps/hello-world-bad
INFO[0003] cosign: Error: no matching signatures:
INFO[0003] cosign: failed to verify signature
INFO[0003] cosign: main.go:46: error during command execution: no matching signatures:
INFO[0003] cosign: failed to verify signature

Compose で署名

Compose での Cosign のサポートも実験的であり、Compose の拡張機能に基づいて実装されています。

CoSign は、nerdctl compose up|run|push|pull でサポートされています。Compose yaml に次のフィールドを追加することで、Compose で cosign を使用できます。これらのフィールドは_サービスごと_であり、検証のみ、署名のみ (またはその両方) を有効にすることができます。

# 署名/検証するサービスの下にのみ cosign 関連フィールドを配置します。
services:
  svc0:
    build: .
    image: ${REGISTRY}/svc0_image # replace with your registry
    #「x-nerdctl-verify」と「x-nerdctl-cosign-public-key」は検証用です
    # 'nerdctl compose up|run|pull' に必要
    x-nerdctl-verify: cosign
    x-nerdctl-cosign-public-key: /path/to/cosign.pub
    #「x-nerdctl-sign」と「x-nerdctl-cosign-private-key」は署名用です
    #「nerdctl compose push」に必要
    x-nerdctl-sign: cosign
    x-nerdctl-cosign-private-key: /path/to/cosign.key
    ポート:
    - 8080:80
  svc1:
    build: .
    image: ${REGISTRY}/svc1_image # replace with your registry
    ポート:
    - 8081:80

Following the cosign tutorial above, first set up environment and prepare cosign key pair:

# Generate a key-pair: cosign.key and cosign.pub
$ cosign generate-key-pair

# Export your COSIGN_PASSWORD to prevent CLI prompting
$ export COSIGN_PASSWORD=$COSIGN_PASSWORD

We'll use the following Dockerfile and docker-compose.yaml:

$ cat Dockerfile
FROM nginx:1.19-alpine
RUN uname -m > /usr/share/nginx/html/index.html

$ cat docker-compose.yml
services:
  svc0:
    build: .
    image: ${REGISTRY}/svc1_image # replace with your registry
    x-nerdctl-verify: cosign
    x-nerdctl-cosign-public-key: ./cosign.pub
    x-nerdctl-sign: cosign
    x-nerdctl-cosign-private-key: ./cosign.key
    ポート:
    - 8080:80
  svc1:
    build: .
    image: ${REGISTRY}/svc1_image # replace with your registry
    ポート:
    - 8081:80

キーレス モードの場合、docker-compose.yaml は次のようになります。

$ cat docker-compose.yml
services:
  svc0:
    build: .
    image: ${REGISTRY}/svc1_image # replace with your registry
    x-nerdctl-verify: cosign
    x-nerdctl-sign: cosign
    x-nerdctl-cosign-certificate-identity: name@example.com # or x-nerdctl-cosign-certificate-identity-regexp
    x-nerdctl-cosign-certificate-oidc-issuer: https://accounts.example.com # or x-nerdctl-cosign-certificate-oidc-issuer-regexp
    ポート:
    - 8080:80
  svc1:
    build: .
    image: ${REGISTRY}/svc1_image # replace with your registry
    ポート:
    - 8081:80

The env "COSIGN_PASSWORD="$COSIGN_PASSWORD"" part in the below commands is a walkaround to use rootful nerdctl and make the env variable visible to root (in sudo). (1)rootlessを使用している場合、または(2)COSIGN_PASSWORDがrootに表示される場合は、この部分は必要ありません。

まず、2つのサービスをbuildしてpushしましょう。

$ sudo nerdctl compose build
INFO[0000] Building image xxxxx/svc0_image
...
INFO[0000] Building image xxxxx/svc1_image
[+] Building 0.2s (6/6) FINISHED

$ sudo env "COSIGN_PASSWORD="$COSIGN_PASSWORD"" nerdctl compose --experimental=true push
INFO[0000] Pushing image xxxxx/svc1_image
...
INFO[0000] Pushing image xxxxx/svc0_image
INFO[0000] pushing as a reduced-platform image (application/vnd.docker.distribution.manifest.v2+json, sha256:4329abc3143b1545835de17e1302c8313a9417798b836022f4c8c8dc8b10a3e9)
INFO[0000] cosign: WARNING: Image reference xxxxx/svc0_image uses a tag, not a digest, to identify the image to sign.
INFO[0000] cosign:
INFO[0000] cosign: This can lead you to sign a different image than the intended one. Please use a
INFO[0000] cosign: digest (example.com/ubuntu@sha256:abc123...) rather than tag
INFO[0000] cosign: (example.com/ubuntu:latest) for the input to cosign. The ability to refer to
INFO[0000] cosign: images by tag will be removed in a future release.
INFO[0000] cosign: Pushing signature to: xxxxx/svc0_image

次に、サービスをpullしてupできます(runはupと似ています)。

# ensure built images are removed and pull is performed.
$ sudo nerdctl compose down
$ sudo env "COSIGN_PASSWORD="$COSIGN_PASSWORD"" nerdctl compose --experimental=true pull
$ sudo env "COSIGN_PASSWORD="$COSIGN_PASSWORD"" nerdctl compose --experimental=true up
$ sudo env "COSIGN_PASSWORD="$COSIGN_PASSWORD"" nerdctl compose --experimental=true run svc0 -- echo "hello"
# clean up compose resources.
$ sudo nerdctl compose down

ログをチェックして、svc0 が cosign によって検証され (cosign ログがある)、svc1 が検証されていないことを確認します。また、docker-compose.yaml の公開キーをランダムな値に変更して、検証の失敗によってコンテナーの pull|up|run が停止されることを確認することもできます。