#chiroito ’s blog

Java を中心とした趣味の技術について

TLS を使用して Docker へ接続する

概要

Docker はネットワークを使わない方法も平文でのネットワーク通信も可能です。ここでは Docker を安全に使うため TLS を使用して信頼できるクライアントから信頼できるサーバへのみ接続できるようにします。 Docker Engine の起動引数に認証局の証明書およびサーバの証明書と鍵を設定します。クライアントは認証局の証明書およびクライアントの証明書と鍵を設定して接続します。

環境

鍵の作成

証明書や鍵があれば別途作る必要はありません。証明書や鍵が無い場合にはこれらを作成します。認証局、Docker Engine(サーバ)、Docker クライアントの分をそれぞれ作成します。 作成されるファイル名は Docker Machine のデフォルト値で作成します。
作成中に様々な入力を求められますが、以下の 2 つ以外は何も入力しないでも大丈夫です。

Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:

各コマンドの出力は参考のページなどでご確認下さい。

認証局用の作業

俗に言うオレオレ認証局(CA)の秘密鍵と公開鍵を作成します
$ openssl genrsa -aes256 -out ca-key.pem 4096
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

Docker Engine用の作業

サーバの鍵(server-key.pem)と証明書署名要求(server.csr)を作成します
$ openssl genrsa -out server-key.pem 4096
$ openssl req -sha256 -new -key server-key.pem -out server.csr

サーバの証明書(server.pem)を作成します
$ echo subjectAltName = IP:192.168.56.100,IP:127.0.0.1 > extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server.pem -extfile extfile.cnf

Docker クライアント用の作業

クライアントの鍵(key.pem)と証明書署名要求(client.csr)を作成
$ openssl genrsa -out key.pem 4096
$ openssl req -new -key key.pem -out client.csr

クライアントの証明書(cert.pem)を作成
$ echo extendedKeyUsage = clientAuth > extfile.cnf
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf

署名と鍵の整理

証明書署名要求は不要なため削除
$ rm -v client.csr server.csr

秘密鍵は所持者のみが読めるようにします
$ chmod -v 0400 ca-key.pem server-key.pem key.pem

証明書は全てのユーザが読めるようにします
$ chmod -v 0444 ca.pem server.pem cert.pem

Docker Engine の設定

CAの証明書と、Docker Engine の証明書と鍵を使用するように--tlsverify --tlscacert=ca.pem --tlscert=server.pem --tlskey=server-key.pemを追加。
所持する全ての IP で tcp の 2376 番ポートでリッスンするように ExecStart に-H tcp://0.0.0.0:2376を追加
これらの設定を起動引数に設定します。
$ vim /usr/lib/systemd/system/docker.service

[Service]
# ExecStart=/usr/bin/dockerd
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=ca.pem --tlscert=server.pem --tlskey=server-key.pem -H tcp://0.0.0.0:2376

設定を反映し docker を再起動します
$ systemctl daemon-reload
$ systemctl restart docker

TCP の 2376 で動いていることを確認
$ netstat -tln | grep 2376

tcp6       0      0 :::2376                 :::*                    LISTEN

Docker クライアント

作成した証明書と鍵を配置して、TLS を使用する設定をして Docker Engine へ接続します。

証明書と鍵の配置

ホームディレクトリに .docker ディレクトリを作成し、このディレクトリに認証局の公開鍵(ca.pem)とクライアントの証明書(cert.pem)と鍵(key.pem)をコピーします。

$ dir /b

ca.pem
cert.pem
key.pem

このディレクトリは DOCKER_CERT_PATH 環境変数でも変更できます。
$ export DOCKER_CERT_PATH=~/.docker/dev/

実行時に TLS を使うことを指定する

実行時に --tlsverify を指定します。
$ docker -H tcp://192.168.56.100:2376 --tlsverify run hello-world

Hello from Docker!
(略)

環境変数TLS を使うことを指定する方法

TLS の使用は DOCKER_HOST 環境変数で指定します。
set DOCKER_TLS_VERIFY=1

環境変数で指定した場合には実行時に --tlsverify 指定する必要はありません。
$ docker -H tcp://192.168.56.100:2376 run hello-world

Hello from Docker!
(略)

ダメな例

証明書や鍵をディレクトリに置いていない(読めない)と以下のエラーが発生します。

Could not read CA certificate "C:\\Users\\user\\.docker\\ca.pem": open C:\Users\user\.docker\ca.pem: The system cannot find the file specified.

環境変数でも実行時にも指定してない場合は以下のエラーが発生します。

Get http://192.168.56.100:2376/v1.24/containers/json: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?

参考: docs.docker.com