プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

nginxコンテナを自己署名認証局でHTTPS化する

前回の記事では自己署名証明書を使ってリバースプロキシとして動作するnginxコンテナをHTTPS化しました。

architecting.hateblo.jp

しかしブラウザがエラーや警告を色々出すのでHTTPS化した実感がありませんでした。

今回は自己署名認証局が署名した証明書を使ってHTTPS化してエラーや警告をなくしたいと思います。


自己署名証明書との違い

認証局(CA)を自分で立ててその認証局サーバ証明書に署名してもらいます。

オレオレCAですが一応、CAが署名しているのでオレオレ証明書ではなくなります。

これでnginxにアクセスしたときのエラーや警告がなくなるはずです。


CA証明書とサーバ証明書作成

構築していきましょう。

CA証明書とサーバ証明書はopensslコマンド連打で作成することができます。以下のページが参考になります。

https://qiita.com/makoto1899/items/ef15372d4cf4621a674e

もっと簡単かつ環境を汚さない方法はないかな、、、Dockerみたいな、、、と探したらopensslコマンドを連打してCA証明書とサーバ証明書を作成するDockerイメージがありました。

https://hub.docker.com/r/paulczar/omgwtfssl

怪しい名前ですがPull回数は100万回を超えています。GitHubにソースが公開されています。

https://github.com/paulczar/omgwtfssl

今回はこれを使うことにします。

omgwtfsslを使い、Dockerコマンド一発でCA証明書とサーバ証明書作成を作成します。

docker run --rm -e SSL_SUBJECT="localhost" -e SSL_DNS="localhost" -v /Users/john/certs:/certs  paulczar/omgwtfssl

-e SSL_SUBJECTはSubjectのCommon Name(CN)を指定します。

-e SSL_DNSはSubject Alternative Name(SAN)を指定します。Chromeの場合、これがないと以下のようにERR_CERT_COMMON_NAME_INVALIDエラーを出します。

f:id:hogehoge666:20210116025826p:plain

ChromeはSubjectのCNではなくSANをチェックします。CNはあってSANがないのにERR_CERT_COMMON_NAME_INVALIDエラーってエラー名がおかしくないか?と思うのですがSANを忘れるとこのエラーが出ます。fetch APIはCNを見てるっぽいし、ややこしいですね。

-vで指定した/Users/john/certsに色々作成されます。

$ ls /Users/john/certs
ca-key.pem  ca.pem      ca.srl      cert.pem    key.csr     key.pem     openssl.cnf secret.yaml

この先の作業で使うのは以下の3つです。


証明書をチェック

サーバ証明書の内容をチェックします。署名がtest-caになっています。自己署名証明書ではないことがわかります。

60日でExpireします。-e SSL_EXPIREオプションで変更できるそうです。

SANもlocalhostになっています。DNSが2つある理由はわかりません。

# openssl x509 -text -noout -in cert.pem 
        Issuer: CN = test-ca
        Validity
            Not Before: Jan 15 15:47:42 2021 GMT
            Not After : Mar 16 15:47:42 2021 GMT
        Subject: CN = localhost
<略>
            X509v3 Subject Alternative Name: 
                DNS:localhost, DNS:localhost

nginx設定変更

前回、下記Dockerコマンドで作成したnginxコンテナを引き続き使用します。nginxコンテナがまだない場合は下記Dockerコマンドで作成して下さい。

docker run -it --name nginx -p 443:443 nginx /bin/bash

作成されたサーバ証明書とサーバ秘密鍵をnginxコンテナにコピーします。

docker cp /Users/john/certs/cert.pem <コンテナID>:/etc/nginx/conf.d/ssl/
docker cp /Users/john/certs/key.pem <コンテナID>:/etc/nginx/conf.d/ssl/

/etc/nginx/conf.d/default.confを下記の通り変更します。

vimが入っていない場合はapt-getでインストールして下さい。

前回からの変更はサーバ証明書秘密鍵を新しく作成したものに変えただけです。

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate /etc/nginx/conf.d/ssl/cert.pem;   <<< 変更1
    ssl_certificate_key /etc/nginx/conf.d/ssl/key.pem;   <<< 変更2

    access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /api {
        proxy_pass   http://todoapi-app:5000;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

nginxを再起動します。今回はpass phraseは要求されません。


CA証明書のインポート

ブラウザがnginxのサーバ証明書を検証するにはCA証明書(ca.pem)をブラウザにインポートする必要があります。

インポートの方法はOSやブラウザによって異なります。

Firefoxだと「オプション」 / 「プライバシーとセキュリティー」→「証明書」→「証明書を表示」からインポートするそうです。

WindowsChromeは「インターネットオプション」→「コンテンツ」→「証明書」→「信頼されたルート機関タブ」からインポートするそうです。

ここではMacChromeを使います。MacChromeはキーチェーンアクセスからインポートします。

  • Macのキーチェーンアクセスを起動します。
  • 画面左側「キーチェーン」で「ログイン」が選択されていることを確認します。
  • 画面右側の証明書や鍵の一覧が表示されているところに生成したca.pemをドラッグ&ドロップします。
  • 下記のようにtest-caという名前で登録されるので一覧からtest-caを見つけてダブルクリックします。 f:id:hogehoge666:20210116031015p:plain
  • 「信頼」→「この証明書を使用するとき」で「常に信頼」を選びます。 f:id:hogehoge666:20210116031027p:plain
  • 閉じます。

動作確認

ブラウザでhttps://localhostへアクセスします。

エラーはありません。

アドレスバーはこの接続が安全であることを示しています。

f:id:hogehoge666:20210116032928p:plain

安全なHTTPS接続を確立することができました。


結論

ようやく安全なHTTPセッションができました。

しかもomgwtfsslを使ったお陰で証明書はコマンド1発で出来てしまいました。すごい便利です。

あえてpaulczar/omgwtfsslの難点を上げるとすれば、このコンテナが作ったCA証明書をキーチェーンアクセスに登録して本当に安全なのかという点です。

安心が欲しい人は自分でopenssl連打で証明書を作成した方が安心かもしれません。

omgwtfsslを利用する場合でも利用前にGitHubでソースを確認して納得した上で使った方がよいでしょう。