Docker デーモンソケットの保護

読む時間の目安: 5 分

デフォルトで Docker は、インターネットを介さない UNIX ソケットを通じて実行されます。 SSH や TLS(HTTPS)ソケットを用いた通信を行うこともできます。

SSH を用いた Docker デーモンソケットの保護

メモ

説明に用いるUSERNAMEは、リモートマシン上において Docker ソケットにアクセスする権限を持っていなければなりません。 root ではないユーザーにより Docker ソケットへアクセスする方法に関しては root ユーザー以外で Docker を管理する を参照してください。

以下の例では docker context を生成し、リモートホストhost1.example.com上のdockerdデーモンに対して SSH を使って接続します。 その際にはリモートホスト上のユーザーdocker-userによりアクセスします。

$ docker context create \
    --docker host=ssh://docker-user@host1.example.com \
    --description="Remote engine" \
    my-remote-engine

my-remote-engine
Successfully created context "my-remote-engine"

コンテキストを生成したら、docker context useを実行してdocker CLI がこれを用いるようにします。 そしてリモートエンジンにアクセスします。

$ docker context use my-remote-engine
my-remote-engine
Current context is now "my-remote-engine"

$ docker info
<リモートエンジンに関する情報の出力>

defaultコンテキストを用いると、デフォルトの(ローカルの)デーモンに切り替わります。

$ docker context use default
default
Current context is now "default"

あるいは環境変数DOCKER_HOSTを用いることで、docker CLI によりリモートホストへの SSH 接続設定を一時的に切り替えることもできます。 この場合はコンテキストを生成する必要がなく、接続切り替えを簡単にできるので便利です。

$ export DOCKER_HOST=ssh://docker-user@host1.example.com
$ docker info
<リモートエンジンに関する情報の出力>

SSH に関するヒント

SSH の利用を最大限活用するには、以下のようにして~/.ssh/configを設定します。 これは、何度も呼び出されるdocker CLI コマンドに対して、SSH 接続を再利用するものです。

ControlMaster     auto
ControlPath       ~/.ssh/control-%C
ControlPersist    yes

TLS (HTTPS) を使った Docker デーモンソケットの保護

Docker がネットワークから接続される際に SSH でなく HTTP を用いて安全性を確保するには、tlsverifyフラグを指定して TLS(HTTPS)を有効にし、tlscacertフラグを使って信頼された CA 証明書を指定します。

デーモンモードにおいては、CA によって署名された証明書を用いて認証されたクライアントからのみ、接続を許可します。 クライアントモードでは、その CA によって署名された証明書を利用するサーバーに対してのみ、接続を可能にします。

高度なトピック

TLS 利用と CA 管理は高度なトピックです。 これを本番環境に利用する場合は、OpenSSL、x509、TLS についてよく理解してから行ってください。

OpenSSL を用いた CA、サーバー鍵、クライアント鍵の生成

メモ: 以下に示す例において$HOSTと示されている箇所はすべて、利用している Docker デーモンホストの DNS 名に置き換えてください。

まず Docker デーモンが起動するホストマシン において、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 を生成したので、次にサーバー鍵と証明書署名要求(certificate signing request; CSR)を生成します。 「Common Name」欄には、Docker に接続するホストの名前となっていることを確認してください。

メモ

以下に示す例において$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 アドレスを使っても行われるため、証明書の生成時には IP アドレスが必要になります。 たとえば10.10.10.20127.0.0.1を使って接続を許可するには、以下のようにします。

$ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf

Docker デーモンの拡張属性は、サーバー認証に対してのみ利用するものとして設定します。

$ echo extendedKeyUsage = serverAuth >> 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:

認証プラグイン は、相互 TLS からの認証を補完する、きめ細かな制御を可能にします。 上記のドキュメント内の説明内容に加えて、Docker デーモン上で動作する認証プラグインは、Docker クライアントに接続するための認証情報を受け取ります。

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

メモ: ここから続く手順を簡単にするために、以下の手順は Docker デーモンが稼動するホストマシン上で行ってもかまいません。

$ 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-client.cnf

そこで署名された証明書を生成します。

$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile-client.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

cert.pemserver-cert.pemを生成したら、証明書署名要求と拡張設定ファイルの 2 つは、安全に削除することができます。

$ rm -v client.csr server.csr extfile.cnf extfile-client.cnf

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 に信頼された証明書を利用するクライアントのみとすることができました。

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

Docker に接続して証明書を確認します。 クライアント鍵、証明書、信頼された CA を指定してください。

クライアントマシン上での実行

ここでの手順は Docker クライアントマシン上で行います。 したがって CA 証明書、サーバー証明書、クライアント証明書は、そのマシン上にコピーしておく必要があります。

メモ: 以下に示す例において$HOSTと示されている箇所はすべて、利用している Docker デーモンホストの DNS 名に置き換えてください。

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

メモ

Docker over TLS は TCP ポート 2376 上を使って動作させる必要があります。

警告 上の例に示したように、証明書認証操作を行う際の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 を実行することができます。

デーモンモード

  • tlsverifytlscacerttlscerttlskeyの各設定は、クライアント認証を行います。
  • 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
docker, docs, article, example, ssh, https, daemon, tls, ca, certificate