Docker デーモンのソケットを守る

デフォルトでは、ネットワークを通さない Unix ソケットで Docker を操作します。オプションで HTTP ソケットを使った通信も可能です。

Docker をネットワーク上で安全な方法で使う必要があるなら、TLS を有効にすることもできます。そのためには、 tlsverify フラグの指定と、 tlscacert フラグで信頼できる CA 証明書を Docker に示す必要があります。

デーモン・モードでは、その CA で署名された証明書を使うクライアントのみ接続可能にします。クライアント・モードでは、その CA で署名されたサーバのみ接続可能にします。

警告

TLS と CA の管理は高度なトピックです。プロダクションで使う前に、自分自身で OpenSSL、x509、TLS に慣れてください。

警告

各 TLS コマンドは Linux 上で作成された証明書のセットのみ利用可能です。Mac OS X は Docker デーモンが必要な OpenSSL のバージョンと互換性がありません。

OpenSSL で CA (サーバとクライアントの鍵)を作成

注釈

以下の例にある $HOST は、自分の Docker デーモンが動いている DNS ホスト名に置き換えてください。

まず、CA 秘密鍵と公開鍵を作成します。

$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
............................................................................................................................................................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:Sven@home.org.au

これで CA を手に入れました。サーバ鍵(server key)と証明書署名要求(CSR; certificate signing request)を作成しましょう。接続先の Docker ホスト名が Common Name (例:サーバの FQDN や自分自身の名前)に一致しているのを確認します。

注釈

以下の例にある $HOST は、自分の Docker デーモンが動いている DNS ホスト名に置き換えてください。

$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................++
.................................................................................................++
e is 65537 (0x10001)
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr

次に公開鍵を CA で署名しましょう。

TLS 接続は DNS 名と同様に、IP アドレスでも通信可能にできます。その場合は、証明書に情報を追加する必要があります。例えば、 10.10.10.20127.0.0.1 を使う場合は次のようにします。

 $ echo subjectAltName = IP:10.10.10.20,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-cert.pem -extfile extfile.cnf
 Signature ok
 subject=/CN=your.host.com
 Getting CA Private Key
 Enter pass phrase for ca-key.pem:

クライアント認証用に、クライアント鍵と証明書署名要求を作成します。

$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr

クライアント認証用の鍵を実装するには、追加設定ファイルを作成します。

$ 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
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

cert.pemserver-cert.pem を生成したら、証明書署名要求を安全に削除できます。

$ rm -v client.csr server.csr

デフォルトの umask は 022 のため、秘密鍵は自分と同じグループから読み書き可能です。

何らかのアクシデントから自分の鍵を守るため、書き込みパーミッションを削除します。自分だけしか読み込めないようにするには、ファイルモードを次のように変更します。

$ chmod -v 0400 ca-key.pem key.pem server-key.pem

証明書は誰でも読み込めても問題ありませんが、予期しないアクシデントによる影響を避けるため、書き込み権限を削除します。

$ chmod -v 0444 ca.pem server-cert.pem cert.pem

あとは Docker デーモンを、自分たちの CA を使って署名した信頼できるクライアントしか接続できないようにします。

$ docker daemon --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
  -H=0.0.0.0:2376

これは Docker に接続する時、証明書の認証を必要とするものです。認証には先ほどのクライアント鍵、証明書、信頼できる CA を使います。

$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
  -H=$HOST:2376 version

警告

上記の例では docker クライアントの実行に sudo が不要か、あるいは認証に使うユーザが docker グループに属しています。つまり、鍵を使ってDocker デーモンにアクセス可能にするとは、デーモンを動かしているマシンの root 権限を与えるのを意味します。これらの鍵は root パスワード同様に保護してください!

デフォルトで安全に

Docker クライアントの接続をデフォルトで安全にしたい場合は、自分のホームディレクトリ直下の .docker ディレクトリにファイルを移動できます。そして、環境変数 DOCKER_HOSTDOCKER_TLS_VERIFY を使います( 毎回 -H=tcp://$HOST;2376--tlsverify を実行する代わりになります )。

$ mkdir -pv ~/.docker
$ cp -v {ca,cert,key}.pem ~/.docker
$ export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1

こうしておけば、デフォルトで Docker は安全に接続しています。

$ docker ps

他のモード

双方向認証を有効にしたくない場合、他のフラグと組みあわせて Docker を実行できます。

デーモン・モード

  • tlsverifytlscacertlscerttlskey をセット:クライアントを認証する
  • tlstlscerttlskey :クライアントを認証しない

クライアント・モード

  • tls:サーバをベースとした公開/デフォルト CA プールで認証
  • tlsverifytlscacert :サーバをベースとした CA 認証
  • tlstlscerttlskey :クライアント認証を使い、サーバ側を指定した CA では認証しない
  • tlsverifytlscacerttlscerttlskey :クライアント証明書と、サーバ側で指定した CA で認証する

クライアントがクライアント証明書を送信したら、自分の鍵を ~/.docker/{ca,cert,key}.pem に移動します。あるいは、別の場所に保管し、環境変数 DOCKER_CERT_PATH でも指定できます。

$ export DOCKER_CERT_PATH=~/.docker/zone1/
$ docker --tlsverify ps

curl を使って Docker ポートに安全に接続

curl を API リクエストのテストに使うには、コマンドラインで3つの追加フラグが必要です。

$ curl https://$HOST:2376/images/json \
  --cert ~/.docker/cert.pem \
  --key ~/.docker/key.pem \
  --cacert ~/.docker/ca.pem