web-dev-qa-db-ja.com

秘密鍵を安全にDockerコンテナーに渡す

Dockerコンテナーで実行されるアプリに必要な秘密の値を渡したいのですが。この特定のコンテナは短命です-起動してコマンドを実行し、その後終了します。

方法1:コンテナーを開始するときに、コマンドラインを介して値を環境変数として渡します(Dockerは、コンテナーを開始するためのコマンドライン引数としてこれをサポートしています)。 dockerコンテナーを起動したホストマシンのプロセスリスト(キーとすべて)にコマンドが表示されるため、これは悪い感じです。

方法2:env変数ファイルを介して、env変数として値を渡します。これはプロセスリストの問題を解決しますが、docker infoホストから実行中のコンテナで、そのコンテナに設定されているすべての環境変数のリストを表示します。これにより、Dockerは安全ではないホスト上のディスクのどこかにこれらを格納していると思います(実行中のコンテナーに新しい環境変数を追加してもこのリストは更新されないため、リアルタイムで直接読み取ってはなりません)。

一般的に、環境変数は秘密データを安全に(一時的にでも)保管するのに十分ではないと思いますが、この考えを裏付ける十分な知識がありません。

秘密のデータをコンテナに渡す安全な方法は何ですか?

34
Anthony Kraft

これを行うには、環境変数、特に方法2が最善の方法です。デフォルトでは、Dockerはroot以外のユーザーによる実行を許可していません。ソケットへのアクセスは禁止されています。方法2は、攻撃者がrootアクセス権を持っている(そしてDockerコンテナーを覗き見できる)場合、箱から出してすぐに状態が悪くなるので、かなり安全です。

一般的に2つのDockerセキュリティノート。デフォルトでは暗号化や認証は行われないため、APIの有効化には十分注意してください。文書化した証明書とTLSを使用する方法がありますが、注意して進めてください。

また、可能であれば、サーバーでSELinuxを有効にします。新しいバージョンでは、実際にDockerコンテナーを確認して、コンテナーごとにセキュリティコンテキストを自動的に構築できます。これにより、コンテナーの侵害がホストに簡単に戻らないようにします。デフォルトでは、Dockerはrootユーザーとして実行され、USERディレクティブを使用しても、VMとは異なり、カーネルと直接インターフェイスします。これにより、Dockerコンテナーが侵害されるとすぐに、ホストがローカル権限の悪用にさらされます。

10

Dockerの人たちは最近、このためのネイティブソリューションを導入しました: https://blog.docker.com/2017/02/docker-secrets-management/

使用パターンは次のとおりです。

$ echo "This is a secret" | docker secret create my_secret_data -
$ docker service  create --name="redis" --secret="my_secret_data" redis:Alpine

暗号化されていないシークレットは、/run/secrets/<secret_name>にあるメモリ内ファイルシステムのコンテナーにマウントされます。

これはswarm内でのみアクセス可能ですが

ここで完全なドキュメントを見つけることができます: https://docs.docker.com/engine/swarm/secrets/

6
grandrew

簡潔な答え

docker buildには --secret AP​​Iバージョン1.39+のオプション があります。

長い答え

APIバージョン1.39+はdocker 18.09.0+を意味します

リリースノートの 18.09. の「Docker Engine EEおよびCEの新機能」セクションで、次のように述べています。

  • APIバージョンを1.39 moby/moby#37640に更新しました

ガイドの「Dockerの拡張機能の作成」ページに少し古い説明があります。

新しいDockerビルドの秘密情報--secretオプションを見つけましたが、ここの説明は古くなっています。それは言う

このDockerfileは、シークレットにアクセスできることを示すためだけのものです。ご覧のとおり、ビルドの出力に出力された秘密が表示されます。ビルドされた最終イメージには秘密ファイルがありません

しかし、実際には秘密はビルド出力に出力されません。安全のために守られていると思います。

ビルドキットの「Dockerfileフロントエンド実験的構文」ページに最新の説明があります。

そして次のページを見つけました。

docker build --secretの使い方

以下の手順に従います。

  1. 必要なバージョンのdockerを使用していることを確認してください。
$ docker version
Client: Docker Engine - Community
 Version:           19.03.2
 API version:       1.40
 Go version:        go1.12.8
 Git commit:        6a30dfc
 Built:             Thu Aug 29 05:29:11 2019
 OS/Arch:           linux/AMD64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.2
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.8
  Git commit:       6a30dfc
  Built:            Thu Aug 29 05:27:45 2019
  OS/Arch:          linux/AMD64
  Experimental:     false
 containerd:
  Version:          1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
  1. DOCKER_BUILDKIT環境変数を1に設定します
$ export DOCKER_BUILDKIT=1
  1. シークレットファイルを作成します。
$ echo "It's a secret" > mysecret.txt
  1. Dockerfileを作成します。
$ cat <<EOF > Dockerfile
# syntax = docker/dockerfile:experimental
FROM Alpine
RUN --mount=type=secret,id=mysecret,target=/foobar cat /foobar | tee /output
EOF

Dockerfileの最初の行に# syntax = docker/dockerfile:experimentalがあることを確認してください。上記の例はデモ用です。秘密の内容は実際の使用では保存しないでください。

  1. docker build--secretオプション付きで実行します。
$ docker build -t secret-example --secret id=mysecret,src=mysecret.txt .
[+] Building 2.3s (8/8) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 176B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => resolve image config for docker.io/docker/dockerfile:experimental
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:888f21826273409b5ef5ff9ceb90c64a8f8ec7760da30d1ffbe6c3e2d323a7bd
 => [internal] load metadata for docker.io/library/Alpine:latest
 => CACHED [1/2] FROM docker.io/library/Alpine
 => [2/2] RUN --mount=type=secret,id=mysecret,target=/foobar cat /foobar | tee /output
 => exporting to image
 => => exporting layers
 => => writing image sha256:22c44473107b6d1f92095c6400613a7e82c9835f5baaa85853a114e4bb5d8744
 => => naming to docker.io/library/secret-example

mysecret.txtの内容は、ビルド出力でも出力されないことに注意してください。

シークレットが正しく使用されていることを確認します。これもデモ用です。

$ docker run -t secret-example cat /output
It's a secret

/foobarの内容は保存されていませんが、ビルドされたイメージに空のファイルが残っています。

$ docker run -t secret-example ls -l /foobar
-rwxr-xr-x    1 root     root             0 Sep 16 19:16 /foobar
2
hnakamur

docker secretswarmモードでのみ機能します。ローカルモードの場合、パスワードなどの単純な秘密を渡すために、stdinから変数にパスワードを読み取ることができます。問題は、コンテナ内のパイプを読み取るときにハングするdetachモードにあります。回避策は次のとおりです。

cid=$(docker run -d -i Alpine sh -c 'read A; echo "[$A]"; exec some-server')
docker exec -i $cid sh -c 'cat > /proc/1/fd/0' <<< _a_secret_

まず、-iオプションを指定してdockerデーモンを作成します。コマンドread Aは、/proc/1/fd/0からの入力を待機してハングします。次に、2番目のdockerコマンドを実行して、stdinからシークレットを読み取り、最後のハングしているプロセスにリダイレクトします。シークレットは1回だけ読み取られ、検査されません。

1
James Z.M. Gao