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
イメージにあるlatest
と3.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_TRUST
を1
に設定します。
これを行っておけば、サインされていないタグつきイメージは、操作対象になりません。
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