まとめ
マルチステージビルドで実行用のバイナリをscratchに載せる場合、
ビルドと同じタイミングで信頼したい証明書を用意して、
CA証明書のリストを更新したものをアプリと一緒にコピーすればよさそう。
環境
- Docker version 20.10.17, build 100c701
- go version go1.18.1 darwin/arm64
- nginx:1.23.1
やったこと
オレオレ証明書を作成してnginxをHTTPSで建てる
まずは検証用にオレオレ証明書を作成する。openssl
が使えるならどんなやり方でもいいが、自分はDocker
上で作ってホストに持ってくるのがすき。
# ホストOSのボリュームをマウントする
$ docker container run --rm -it -v="$PWD:/workdir" -w="/workdir" --entrypoint=/bin/bash nginx:1.23.1
root@e08bb1ebe3da:/workdir# openssl version
OpenSSL 1.1.1n 15 Mar 2022
# 秘密鍵の作成
root@e08bb1ebe3da:/workdir# openssl genpkey -algorithm RSA -out server-private-key.pem
...................+++++
......................................................................................................+++++
# オレオレ証明書の作成
root@e08bb1ebe3da:/workdir# openssl req -x509 -key server-private-key.pem -out server-cert.pem -addext 'subjectAltName = DNS:hogehoge.uzimihsr.com'
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]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:hogehoge.uzimihsr.com
Email Address []:
root@e08bb1ebe3da:/workdir#
exit
# ホスト上に鍵と証明書が置かれている
$ ls
server-cert.pem server-private-key.pem
作成した秘密鍵とオレオレ証明書を使ってnginxをHTTPSで立ててみる。
# ディレクトリの状態
$ tree .
.
├── docker-compose.yaml
├── https.conf
├── server-cert.pem
└── server-private-key.pem
0 directories, 4 files
# nginxを起動
$ docker compose up -d
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
# CA証明書を指定して叩くとHTTPSで通信できている
$ curl -I https://hogehoge.uzimihsr.com --cacert ./server-cert.pem --resolve "hogehoge.uzimihsr.com:443:127.0.0.1"
HTTP/1.1 200 OK
...
HTTPリクエストを送るGoアプリの作成
先ほど立てたnginx(HTTPS)に対してHTTPリクエストを送る簡単なアプリを実装する。
内容としては引数で指定されたURLに対してHTTP GETしてそのステータスコードを表示する(200系以外は異常終了する)だけのもの。
$ go run main.go https://example.com
2022/09/28 23:45:32 target: https://example.com, insecure: false
2022/09/28 23:45:33 status: 200 OK
これをマルチステージビルドでscratchに乗せて動かしてみる。
起動したコンテナを確認すると、オレオレ証明書を信頼できずにエラーとなっている。
$ tree .
.
├── Dockerfile
├── docker-compose.yaml
├── go.mod
├── https.conf
├── main.go
├── server-cert.pem
└── server-private-key.pem
0 directories, 7 files
$ cat go.mod
module http-status-checker
go 1.18
$ docker compose up -d
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
http-status-checker "./app https://hogeh…" http-status-checker exited (1)
$ docker compose logs http-status-checker
http-status-checker | 2022/09/28 12:17:41 target: https://hogehoge.uzimihsr.com/, insecure: false
http-status-checker | 2022/09/28 12:17:41 [ERROR] HTTP(S) request failed: Get "https://hogehoge.uzimihsr.com/": x509: certificate signed by unknown authority
オレオレ証明書を信頼する
こんなとき、実装にもあるようにtls.Config.InsecureSkipVerifyを指定することで証明書エラーを回避することは可能だが、
どうしてもオレオレ証明書を信頼する設定で動かしたくなった。
どうしたものかと悩んだが、単純にupdate-ca-certificatesで信頼した証明書をコピーする方法に落ち着いた。
Dockerfile
の設定を次のように書き換える。
変更点は次の部分。
信頼したい証明書を/usr/local/share/ca-certificates/
配下に.crt
という名前でコピーして、update-ca-certificates
を実行している。
これにより、ビルド用コンテナ内の/etc/ssl/certs/ca-certificates.crt
が更新されて対象の証明書を信頼できるようになる。
COPY server-cert.pem /usr/local/share/ca-certificates/server-cert.crt
RUN update-ca-certificates
再度実行してみると、今度はオレオレ証明書が信頼できていてエラーが発生しなくなった。
$ docker compose up -d --remove-orphans --build
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
http-status-checker "./app https://hogeh…" http-status-checker exited (0)
$ docker compose logs http-status-checker
http-status-checker | 2022/09/28 13:44:43 target: https://hogehoge.uzimihsr.com/, insecure: false
http-status-checker | 2022/09/28 13:44:43 status: 200 OK
今回はgolangのimageがdebian系だったのでこの手順で試したが、
Red Hat系なら同様に.crt
ファイルを/usr/share/pki/ca-trust-source/anchors/
配下にコピーして、
update-ca-trustすると/etc/pki/tls/certs/ca-bundle.crt
が更新されたはず。(試してない)
おわり
久しぶりにGo+scratchでimageを作ったときに詰まったのでおさらいした。
こんなことしてる暇があったらdistrolessに移行しろと言われればそれはそう…