Docker のコンテントトラスト

読む時間の目安: 3 分

ネットワークシステム内でデータ転送を行う際には、信頼(trust)できるものであるかどうかが一番大切なことです。 特にインターネットのような信頼に欠けるシステム上で通信を行う場合、システムが扱うデータの整合性とその発信者情報を確実にすることが極めて重要です。 Docker Engine では、公開リポジトリあるいはプライベートリポジトリに対して、イメージ(データ)をプッシュしプルすることができます。 コンテントトラスト(content trust)はあらゆるチャネルを通じて、レジストリから取得するデータの整合性と公開者情報を、ともに検証できる仕組みを提供します。

Docker コンテントトラストについて

Docker コンテントトラスト (Docker Content Trust; DCT) は、リモート Docker レジストリとの間で送受信されるデータに対して、デジタル証明書を利用する機能を提供します。 この証明書があることによって、所定のイメージタグに対して、クライアントサイドつまり実行時での整合性および公開者情報の検証を可能としています。

DCT を通じてイメージ公開者は、そのイメージを証明することができます。 そしてイメージ利用者は、プルを行うイメージが証明されていることがわかります。 公開者は個人の場合もあり組織の場合もあります。 リリース工程の一部としてこの証明作業を、手動で行っていたり、自動化されたソフトウェアサプライチェーンによって行っているかもしれません。

イメージタグと DCT

1 つのイメージには、以下の識別子が記録されています。

[REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

イメージREPOSITORYは複数のタグを持つことができます。 たとえばmongoイメージにあるlatest3.1.2というのは、どちらもタグです。 イメージ公開者は、イメージをビルドするたびに、何度もイメージとタグの組み合わせを作り出します。

DCT はイメージのTAG部分に関連づけられます。 各イメージリポジトリには、公開者がイメージタグにサインするための鍵が複数あります。 どのタグに対してサインを行うかを、イメージ公開者は自由に取り決めています。

イメージリポジトリには 1 つのイメージに対して、サインされたタグを持つものが 1 つだけあり、それ以外のタグはサインされていません。 Mongo イメージリポジトリ を例にして説明します。 3.1.6がサインされているタグであったとすると、latestはサインされていません。 どのイメージタグにサインし、サインしないかは、イメージ公開者が取り決める責任があります。 以下の図においては、サインされているイメージがあり、それ以外はサインされていません。

サインされたタグ

公開者としては、どのタグにサインするかを取り決めることができます。 サインされているものとサインされていないものが、結果的に同じ名称になったとしても、同一の内容としては扱われません。 たとえば公開者がタグづけしたイメージsomeimage:latestをプッシュして、これにサインしたとします。 後にその公開者は、サインをしていないsomeimage:latestというイメージをプッシュすることができます。 2 度めに行ったプッシュによって、サインをしていなかった直前のlatestがプッシュしたものに置き換えられますが、サインしているlatestには何ら影響しません。 公開者がサインするタグを選べるということは、イメージをサインせずに何回も繰り返しプッシュした上で、最後に公式イメージにサインするというやり方が可能になります。

イメージ利用者は DCT を通じて、利用するイメージがサインされているかどうかがわかります。 利用者が DCT を有効にしている場合、イメージをプルしビルドし実行するのは、信頼できる(trusted)イメージのみとなります。 DCT を有効にすることは、レジストリに「フィルター」をかけるようなものです。 利用者にとって、サインされたイメージタグだけが「見える」ものになります。 あまり利用することがない、未サインのイメージタグは「見えない」わけです。

信頼ビュー

利用者が DCT を有効にしていなければ、Docker イメージの操作方法は何も変わりません。 イメージがサインされていてもいなくても、すべてのイメージを見ることができます。

Docker コンテントトラストの鍵

イメージタグへのトラストつまり信頼ができるかどうかは、証明書鍵を利用して管理されます。 DCT を利用する操作を行った初回において、鍵一式が生成されます。 鍵一式には、以下のような種類の鍵があります。

  • オフライン鍵(offline key)。イメージタグ用の DCT ルート鍵です。
  • タグにサインをするリポジトリ鍵(repository key)あるいはタグ用鍵(tagging key)。
  • サーバーによって管理される、たとえばタイムスタンプ鍵(timestamp key)。 リポジトリに対して、最新のセキュリティ保証を行います。

以下の図では、いろいろな種類の鍵とその関係を表わしています。

コンテントトラストの構成

警告

ルート鍵を失ってしまうと復旧が 極めて困難 になります。 これを復旧するためには Docker サポート が調整を行う必要があり、リポジトリ状態をリセットしなければなりません。 またこのリポジトリを通じてサイン済タグを利用していたすべてのユーザーに対して、ルート鍵を失なう以前のイメージは 手動で復旧操作を行う 必要があります。

ルート鍵はどこか安全なところにバックアップをとっておいてください。 このルート鍵は新たなリポジトリを生成する重要なものなので、オフラインのデータとして保存しておくことをお勧めします。 セキュアなリポジトリの維持、鍵のバックアップに関しては DCT における鍵の管理 を参照してください。

DCT によるイメージへのサイン

Docker CLI においてはdocker trustコマンドを使って、コンテナーイメージにサインしてプッシュすることができます。 これは Notary 機能の上に実現されています。 詳細は Notary GitHub リポジトリ を参照してください。

イメージへのサインを行うためには、(Docker Hub のような)Notary サーバーが連携した Docker Registry が必要です。 独自のサーバー環境を構築するのであれば、その手順が こちら にあります。

Docker イメージにサインするには、委任鍵ペア(delegation key pair)が必要です。 この鍵はローカルでdocker trust key generateの実行によって生成するか、認証局によって生成されます。

はじめにローカルの Docker トラストレジストリに、委任鍵ペアを追加します。 (デフォルトでこの鍵は~/.docker/trust/に保存します。) docker trust key generateによって委任鍵ペアを生成している場合、秘密鍵(private key)も自動的にローカルの保存場所に生成されています。 個別に鍵をインポートしている場合は、docker trust key loadコマンドを実行することが必要です。

$ docker trust key generate jeff
Generating key for jeff...
Enter passphrase for new jeff key with ID 9deed25:
Repeat passphrase for new jeff key with ID 9deed25:
Successfully generated and loaded private key. Corresponding public key available: /home/ubuntu/Documents/mytrustdir/jeff.pub

すでに鍵がある場合は以下のようにします。

$ docker trust key load key.pem --name jeff
Loading key from "key.pem"...
Enter passphrase for new jeff key with ID 8ae710e:
Repeat passphrase for new jeff key with ID 8ae710e:
Successfully imported key from key.pem

次に委任鍵ペアの公開鍵を Notary サーバーへ追加します。 これは Notary の Global Unique Name (GUN) と呼ばれるイメージリポジトリとして特化したものです。 委任鍵ペアをリポジトリに追加する初回は、このコマンド実行において、ローカル Notary サーバーの正規のルート鍵を使って、リポジトリが初期化されます。 リポジトリの初期化と委任鍵の役割に関しては、コンテントトラストの委任鍵 を参照してください。

$ docker trust signer add --key cert.pem jeff registry.example.com/admin/demo
Adding signer "jeff" to registry.example.com/admin/demo...
Enter passphrase for new repository key with ID 10b5e94:

最後に委任鍵ペアの秘密鍵を使って、指定するタグに対してサインを行い、これをレジストリにプッシュします。

$ docker trust sign registry.example.com/admin/demo:1
Signing and pushing trust data for local image registry.example.com/admin/demo:1, may overwrite remote trust data
The push refers to repository [registry.example.com/admin/demo]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

あるいは鍵ペアがインポート済であれば、DCT 環境変数を設定しておくことで、docker pushコマンドによりイメージをプッシュすることもできます。

$ export DOCKER_CONTENT_TRUST=1

$ docker push registry.example.com/admin/demo:1
The push refers to repository [registry.example.com/admin/demo:1]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

リモートにあるトラストデータやリポジトリを参照するにはdocker trust inspectコマンドを実行します。

$ docker trust inspect --pretty registry.example.com/admin/demo:1

Signatures for registry.example.com/admin/demo:1

SIGNED TAG          DIGEST                                                             SIGNERS
1                   3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e   jeff

List of signers and their keys for registry.example.com/admin/demo:1

SIGNER              KEYS
jeff                8ae710e3ba82

Administrative keys for registry.example.com/admin/demo:1

  Repository Key:	10b5e94c916a0977471cc08fa56c1a5679819b2005ba6a257aa78ce76d3a1e27
  Root Key:	84ca6e4416416d78c4597e754f38517bea95ab427e5f95871f90d460573071fc

リモートにあるトラストデータはdocker trust revokeコマンドにより削除することができます。

$ docker trust revoke registry.example.com/admin/demo:1
Enter passphrase for signer key with ID 8ae710e:
Successfully deleted signature for registry.example.com/admin/demo:1

Docker コンテントトラストのクライントでの利用

Docker クライアントにおいて、コンテントトラストはデフォルトで無効になっています。 これを有効にするには、環境変数DOCKER_CONTENT_TRUST1に設定します。 これを行っておけば、サインされていないタグつきイメージは、操作対象になりません。

Docker クライアントにおいて DCT が有効である場合、タグつきのイメージを操作するdockerコマンドには、コンテント署名あるいは明示的なコンテントハッシュがなければなりません。 DCT の操作が可能なコマンドは以下です。

  • push
  • build
  • create
  • pull
  • run

たとえば DCT を有効にしている場合、docker pull someimage:latestというコマンドはsomeimage:latestがサインされている場合のみ処理が成功します。 ただしコンテントハッシュが明示的に指定されている場合は、このコマンドの処理は常に成功します。

$ docker pull registry.example.com/user/image:1
Error: remote trust data does not exist for registry.example.com/user/image: registry.example.com does not have trust data for registry.example.com/user/image

$ docker pull registry.example.com/user/image@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1: Pulling from user/image
ff3a5c916c92: Pull complete
a59a168caba3: Pull complete
Digest: sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
Status: Downloaded newer image for registry.example.com/user/image@sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
content, trust, security, docker, documentation