「古いMastodonを今更アップデートした話」の子記事です。
ここで解決したもののうち**「古いDockerイメージのコンテナからlet's encryptのSSL証明書を持つページにアクセスできない問題」**に焦点を絞って記載しています。
備忘録です。
#問題
古いDockerイメージを使ってコンテナからlet's encryptのSSL証明書を持つページにアクセスすると、何故か「証明書が信頼できない」旨のエラーが出てしまう。
証明書チェーンを作り直してもだめ。
ssl_client: ****: certificate verification failed: certificate has expired
他の機関から発行された証明書のページには正常にSSL通信が可能でした。
#環境
Docker環境のMastodonでの話です。
- Docker
- ruby:2.5.0-alpine3.7
#原因
**「let's encryptルート証明書期限切れ」と「Openssl1.0.2の仕様」**の2つが合わさって起こった問題です。
これにより、問題のあったコンテナではいくら正しい適切な証明書であろうともlet's encryptの証明書だけ信頼しないという状態となっていました。
- let's encrypt ルート証明書期限切れ
2021/9/30をもって、それまでlet's encryptで使用されていたルート証明書"DST Root CA X3"が期限切れとなりました。それ以降はISRGというものが使用されています。
この「let's encryptが失効したルート証明書を持つ」ことが原因のひとつです。
- Openssl1.0.2の仕様
今回問題の起きた環境ではOpenssl1.0.2を使い、アクセスしたサイトの持つ証明書が信頼できるかどうか判断しています。
Openssl1.0.2の仕様ではサイトの持つ証明書が"信頼できるルート証明書"と"信頼できないルート証明書"2つに紐づけられている場合、"信頼できないルート証明書"を優先します。
つまり"信頼できないルート証明書"を持つものは絶対に信頼しないということです。
これがふたつめの原因です。
Openssl2.0以降にはこの仕様は無く、信頼できるルート証明書があれば信頼してくれます。
#対応策
Openssl1.0.2のままで内部のCA証明書を入れ替えることで対策しました。
コンテナ作成時にもともと持っているCA証明書は期限切れの"信頼できないルート証明書"に紐づいてしまっているため、これを削除して最新のCA証明書に入れ替えました。
またもともと持っている"DST Root CA X3"に関するファイルも削除して、念のためISRGの証明書を同じ場所に保存しておきました。
具体的には下記内容をDockerfileに記載することで上手くいきました。
mastodonで動かす際にはCOPY処理が書かれている部分の前後に書いてました。
なんか一部不要な記述がありそうですが、とりあえずこれで動きました。
RUN apk -U upgrade
RUN rm -f /etc/ssl/certs/2e5ac55d.0
RUN rm -f /etc/ssl/certs/790a7190.0
RUN rm -f /etc/ssl/certs/ca-cert-DST_ACES_CA_X6.pem
RUN rm -f /etc/ssl/certs/ca-cert-DST_Root_CA_X3.pem
RUN rm -f /usr/share/ca-certificates/mozilla/DST_ACES_CA_X6.crt
RUN rm -f /usr/share/ca-certificates/mozilla/DST_Root_CA_X3.crt
COPY isrgrootx1.pem /usr/local/share/ca-certificates/isrgrootx1.pem
COPY cacert-2021-09-30.pem /etc/ssl/cert.pem
RUN update-ca-certificates
- ISRG証明書・CA証明書ファイルのコピー
mastodonのフォルダ(Dockerfileなどと同じフォルダ)に「isrgrootx1.pem」「cacert-2021-09-30.pem」を用意します。 - isrgrootx1.pem : このファイルを保存して名前と拡張子を書き換えました。
- cacert-2021-09-30.pem : ここから適宜最新のものを選んできました。Mozilaさんのところから使わせてもらいました。
#補足
コンテナ内で"DST Root CA X3"関連のファイルを削除する際、はじめはそのパスが分からず苦労してました
(1)Opensslのトラストストアのパスを確認して、(2)その中で"DST Root CA X3"関連を探すという手順が必要だったんですが、結局Dockerfileに
RUN cat /usr/lib/ssl/openssl.cnf
RUN ls /etc/ssl/certs/
みたいなの書いて、失敗前提でdocker buildして確認したりしてました。
泥臭いというか頭悪い。
もっとスマートなやり方があると思うんですが、手軽さに負けました。
#まとめ
let's encryptの証明書を持つサイトだけSSL通信できなかったのは、「ルート証明書期限切れ」と「Openssl1.0.2の"信頼しないルート証明書を優先する"という仕様」が原因。
Opensslトラストストアから信頼されないルート証明書関連の情報をすべて消してやることで解決。
#おわりに
CA証明書とかトラストストアとか言葉が正しい意味通りに使えているのか自信がない・・・
今回Mozilaさんから持ってきたcacertって、ほんとにここでいう「CA証明書」で合ってるのか?
中間CA証明書ってopensslトラストストア内でいうとどのファイルなんだ・・・?
理解が足りてないから知らべてまとめます。