Ubuntu 19.04でDockerデーモンにTLSで接続できるようにする

### 更新履歴
* 2020/12/10
    * クライアント用の証明書要求を作成する手順を書き忘れていました。
    * DockerデーモンへのTLS接続を許可する手順で、説明に食い違いがありました。
    * Ubuntu 20.04 LTSでも問題なく動作しました(当たり前)

##対象とする環境

Dockerのインストールが済んでいる、Ubuntu 19.04を想定しています。

Ubuntu 18.04 LTS以降であれば、systemdを使っているので同じ手順で大丈夫だとは思います。

##手順

[公式ドキュメント][1]に沿って進めます。

実行にroot権限が必要なコマンドは文頭に\#を、そうでなければ\$をつけています。

複数のファイルを生成するので、適当なディレクトリを作成して移動しておきます。
ここではホームディレクトリにdocker-authというディレクトリを作ることにします。

```
$ mkdir ~/docker-auth && cd ~/docker-auth
```

###自己署名用の秘密鍵を作成する

```
$ openssl genrsa -aes256 -out ca-key.pem 4096
```

パスフレーズを求められます。このパスフレーズはDockerのTLS接続時に使うものではないので、長くて複雑なものを用いて構いません。

###自己署名用の証明書要求を作成する

```
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
```

daysで設定する整数値は証明書要求ファイルの有効日数です。ここでは1年間にしています。必要に応じて調整してください。

パスフレーズの入力を求められるので、先ほど秘密鍵で設定したパスフレーズを入力してください。

その後所在地の入力等を求められるので適当に入力します。Common NameにはDockerソケットの公開元であるサーバーのFQDNを入れてください。

###サーバー用の秘密鍵を作成する

```
$ openssl genrsa -out server-key.pem 4096
```

サーバー側の処理であり、パスフレーズの入力ができない為、こちらはパスフレーズを設定しません。

###サーバー用の証明書要求を作成する

```
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
```

\$HOSTはサーバーのFQDNと置き換えてください。

###サーバー用の自己署名証明書を作成する

```
$ echo subjectAltName = DNS:$HOST,IP:$HOST_IP,IP:127.0.0.1 >> extfile.cnf
$ 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
```

\$HOSTはこちらでもサーバーのFQDNと置き換えます。

\$HOST_IPはサーバーへの接続に使うIPアドレスを設定します。自分はサーバーを自宅に置いている為、LAN内からでも接続できるようにプライベートIPを指定しました。

こちらも有効期間は1年間としています。必要に応じて調整してください。

パスフレーズには最初に設定したものを入力してください。

###クライアント用の秘密鍵を作成する

```
$ openssl genrsa -out key.pem 4096
```

こちらも利便性の為にパスフレーズを設定しません。

###クライアント用の証明書要求を作成する

```
$ 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
```

こちらも有効期間は1年間としています。必要に応じて調整してください。

パスフレーズには最初に設定したものを入力してください。

###不要なファイルを削除し、権限を設定する。

```
$ rm -v client.csr server.csr extfile.cnf extfile-client.cnf
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
```

証明書要求ファイルと拡張設定ファイルは証明書の作成が済めば不要なのでここで削除しておきます。

残ったファイルはどれも大切に保管する必要があるので、適切な権限を設定しておきます。

###DockerデーモンへのTLS接続を許可する

```
# mkdir /etc/docker/auth
# cp {ca,server-cert,server-key}.pem /etc/docker/auth
```

Dockerデーモンが用いる証明書要求、サーバー用自己署名証明書、サーバー用秘密鍵を適当な位置に移動しておきます。

Ubuntu19.04ではデーモンの起動にsystemdを用いているため、~~/lib/systemd/system/docker.serviceにある設定ファイルを編集します。~~

**追記)
systemdのユニットファイルを直接編集する設定方法を紹介していましたが、こちらの方法ではdockerのアップデートを行うと自動で修正されてしまいます。
systemdの設定を編集する際には差分ファイルを作るのが正しい手法のようなのでそちらに倣いましょう。**

### Dockerデーモンの起動オプションの編集

systemdではユニットファイルの差分読み込みという機能があります。

```
# systemctl edit docker.service
```

こちらのコマンドを実行すると自動でエディタが立ち上がるので、下記内容で保存してくだいさい。

```override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/auth/ca.pem --tlscert=/etc/docker/auth/server-cert.pem --tlskey=/etc/docker/auth/server-key.pem -H tcp://0.0.0.0:2376 -H fd:// --containerd=/run/containerd/containerd.sock
```

上記ファイルの実体は/etc/systemd/system/docker.service.d/override.confとなります。

あとはDockerデーモンを再起動すればTLS接続が有効になっています。

ufw等を使っている場合は適宜ポート開放を行い、クライアントからdockerコマンドで接続出来ることを確認してください。

OS別のDockerクライアントインストール方法は[別記事][2]にて取り扱います。

## Dockerデーモンの起動オプションの補足

/etc/docker/daemon.jsonというファイルがDockerデーモンの設定ファイルとなっていました。

起動オプションをdockerdの引数ではなく、設定ファイルとして書ける仕組みですが、面倒な事にdockerdの引数とdaemon.jsonのオプションが競合すると起動に失敗する仕様となっています。[^1]

下記内容が今回の設定と対応していますが、こちらで設定する場合でもどっちみちsystemdを編集して、-Hオプションを消さないとならないので、あくまでおまけとして覚えておく方がいいと思います。

```json:/etc/docker/daemon.json
{
	"tlsverify": true,
	"tlscacert": "/etc/docker/auth/ca.pem",
	"tlscert": "/etc/docker/auth/server-cert.pem",
	"tlskey": "/etc/docker/auth/server-key.pem",
	~~ hosts が -H と競合する~~
	"hosts": ["tcp://0.0.0.0:2376", "fd://"]
}
```


##参考文献

[Protect the Docker daemon socket][1]

[^1]: [dockerd - Daemon configuration file][3]

[1]:https://docs.docker.com/engine/security/https/
[2]:https://qiita.com/negineri/items/7947eda8a0ad6b8668a1
[3]:https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file