Dockerイメージとオレオレ証明書組み込みレシピ

# SSL 自己署名証明書と Docker レシピ その１

今年も AdventCalendar の季節ですね **㊗**　日立グループ からも元気に参加します！
OSSソリューションセンタ 神山 が先陣を切らせて頂きたく。

## とある社内インフラ屋の困りごと

私はとある社内システムの設計、構築、運用、etc …ゆりかごから墓場まで面倒を見ています。
最近はコンテナが大好物です。

そんな私をたまに苦しめるのが、会社やチーム内独自の 自己署名証明書 (*self*-*signed certificate*、通称 オレオレ証明書) という存在です。理想を言えば、然るべき認証局に署名してもらうべきです。
しかし、イントラネットや開発中の環境だと、どうしてもそれが難しいことがあります。

特に Docker や Kubernetes を使い始めると、この”オレオレ証明書”の扱いが非常に面倒になっています (新しいイメージ使おうと思うとすぐに証明書エラー…)。巷では、「sslVerify = false」みたいな設定がはびこるわけですがセキュリティ上リスクが大きいです。

そこで今回は、(Docker) コンテナ利用を前提に、オレオレ証明書をうまく扱うための方法を書き並べてみました。

## コンテナでオレオレ証明書を使う

コンテナでオレオレ証明書を正しく使う方法ですが…

* イメージビルド時に証明書もインストールする
* 実行時に証明書をマウントする

このような方法があるかと思います。今回は、それぞれ見ていきます。

### イメージビルド時に証明書もインストールする

いくつかのOSについて、ビルド時に 証明書を入れていきます。

```
Dockerfile
self-signed.cert
```

上記のようなファイル構成を想定しています。
**self-signed.cert** がオレオレ認証局のCA証明書です。 PEM形式を想定しています。DER系式など、異なる形式の場合は事前に変換が必要です([参考](https://www.atmarkit.co.jp/ait/articles/1602/05/news039.html))。このファイルをコンテナ内に登録し、コンテナ内部でこの証明書を”信用できる”証明書として登録していきます。

今回はそれぞれベースイメージや公式イメージを元にしました。
執筆時点(2020年11月)で安定版のタグを利用しています。

#### Ubuntu (Debian)

```dockerfile
FROM ubuntu:20.04

COPY self-signed.cert /usr/local/share/ca-certificates/extra/self-signed.crt
RUN apt-get update -y && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* && update-ca-certificates
```

執筆時点の`ca-certificates`は、`20201027ubuntu0.20.04.1` でした。

1. 所定の場所(**/usr/local/share/ca-certificates/extra/**)に証明書を配置
2. **ca-certificates** パッケージをインストール
3. **update-ca-certificates** を実行

これで完了です。上記の例では、ごみファイルを残さないように、ということで最後 /var/lib/apt/lists 以下を削除しています。
debian(debian:10) でも同様の手順となります。

#### Alpine Linux

```dockerfile
FROM alpine:3.12

COPY self-signed.cert /usr/local/share/ca-certificates/self-signed.cert
RUN apk add --no-cache ca-certificates && update-ca-certificates
```

執筆時点の`ca-certificates`は、`ca-certificates-20191127-r4` でした。シンプルに、

1. 所定の場所(**/usr/local/share/ca-certificates/**)に証明書を配置
2. **ca-certificates** パッケージをインストール
3. **update-ca-certificates** を実行

でOKです。

#### CentOS

```dockerfile
FROM centos:7

COPY self-signed.cert /usr/share/pki/ca-trust-source/anchors/self-signed.crt
RUN yum install -y ca-certificates && update-ca-trust && yum clean all
```

centos:latest (執筆時点では centos:8 )は、dnfコマンドがうまく動かなかったので、妥協して centos:7 としました。執筆時点の`ca-certificates`は、`ca-certificates-2020.2.41-70.0.el7_8.noarch` でした。

こちらも、

1. 所定の場所(**/usr/share/pki/ca-trust-source/anchors/**)に証明書を配置
2. **ca-certificates** パッケージをインストール
3. **update-ca-trust** を実行

でOKです。

#### Java

```
FROM openjdk:15

COPY self-signed.cert /tmp/self-signed.cert
RUN keytool -import -trustcacerts \
     -file /tmp/self-signed.cert \
     -noprompt -storepass changeit \
     -cacerts \
     -alias self-signed
```

執筆時点では OpenJDK:15 (openjdk version "15.0.1" 2020-10-20) でした。
基本的な流れは

1.  適当な場所に証明書を配置
2.  **keytool** コマンドで取り込み
    * それぞれのオプションの説明はほかに譲ります
    * OpenJDK14 あたりから、-keystore で cacerts キーストアの場所を指定するのは warning 扱いになりました。 -cacerts だけで済むのでこちらの方が楽ですね
    * -alias オプションでわかりやすい名前を付けると良いです

処理によってはOS側の証明書を参照してくれますが、過去に Java VM 側でも証明書を覚えておかないとうまく動かないことがあったので載せてみました。

### 実行時に証明書をマウントする

イメージを自前でビルドするとバージョン管理が面倒になることがあります。そういう場合は、実行時にマウントするという手があります。ここは Debian/Ubuntu の場合について書きます。

#### Debian/Ubuntu

まずは、**update-ca-certificates** コマンドの結果出力されるファイルを保存します。本記事のイメージビルド時に証明書をインストールする方法でイメージ をビルド(ここではイメージ名を certs )します。

そして、

```bash
docker build -t certs .
docker create --name certs certs
docker cp certs:/etc/ssl/certs/ca-certificates.crt certs.crt
docker rm certs
```

コンテナからファイルを取り出します。既に動作させてるUbuntu環境からファイルをコピーしても良いのですが、上記のように極力クリーンな環境からコピーしたほうが間違いが少ないです。こうすることで、後々Ubuntuのバージョンがあがり、`ca-certificates.crt` の形式が変わった場合も、同じ手順で更新できます。

ここまで出来たら、後は

```bash
docker run -v `pwd`/certs.crt:/etc/ssl/certs/ca-certificates.crt -it ubuntu bash
```

このように、同ディレクトリにマウントしてコンテナを起動することで、オレオレ証明書を認識した状態でコンテナを立ち上げることができます。上記では Ubuntu を起動していますが、Ubuntuベースのイメージであれば、同じパスにマウントすることで、期待通りに動作します。

## さいごに

今回は、OSのベースイメージ毎に、SSL証明書の登録の仕方をまとめてみました。

実際は、JenkinsやRedmineなどのイメージに対して証明書を埋め込みたいケースもあるかと思います。そういったケースにどう対応するか…は その２ として公開したいと思います。