Let’s Encrypt はフリーで利用できる認証局だ。
今回はこの Let’s Encrypt を利用して、既存の web アプリを SSL 化(つまり、https://〜でアクセスできるように)してみた。
Let’s Encrypt を利用するには certbot というツールをインストールして、サーバ証明書の発行や更新ができる。だけど、webアプリやwebサーバは Docker のコンテナ上で動かしているので、せっかくだから certbot も Docker でできないかと調べてみたら、ちゃんとやり方があった。certbot は Docker Hub にイメージが公開されている。
以下、ウチのwebアプリはこのやり方でできたよ、という記録。
前提
- 既存のwebアプリ(http://webapp.panicblanket.com/)をSSL化する
- webアプリ、webサーバ(Nginx)は Docker コンテナ上で動いている
- Docker と docker-compose がインストール済み
付け加えると、webアプリの /
(ルート)は他のページへリダイレクトされている。ここが最初よくわからなかったところだ。
certbot を使うには、対象のサーバの /
に Let’s Encrypt からアクセスできるようにしておく必要がある。というのも certbot はサーバ認証のために一時的なファイルを作って、Let’s Encrypt からアクセスできるのを確認することでサーバの存在を確認しているらしいからだ。だけど、webアプリでは他のページにリダイレクトしているので、うまく行かないんじゃないかと思っていた。
結論からいうと、webアプリのリバースプロキシになっているwebサーバをいったん停めて、認証取得用に別のwebサーバをたてることで対処することができた。
手順
大まかな手順は次の通り:
- 認証取得用の設定ファイルを書く
- 既存webアプリのリバースプロキシになってるwebサーバを停める
- certbot の Dockerコンテナを使って認証取得
- 既存のwebサーバの設定ファイルをSSL対応にして再起動
認証取得用の設定ファイル
Docker、というか docker-compose を使うので docker-compose.yml ファイルを書く。
ersion: "3" services: nginx: image: nginx:1.19.2-alpine ports: - 80:80 volumes: - ./conf.d:/etc/nginx/conf.d - ./html:/var/www/html - /etc/letsencrypt:/etc/letsencrypt - /var/lib/letsencrypt:/var/lib/letsencrypt certbot: image: certbot/certbot:latest volumes: - ./html:/var/www/html - /etc/letsencrypt:/etc/letsencrypt - /var/lib/letsencrypt:/var/lib/letsencrypt command: ["--version"]
Nginx と certbot のイメージを利用する。この Nginx は認証取得のための一時的なものだ。
で、その Nginx の設定ファイルはこう:
server { listen 80; listen [::]:80; server_name webapp.panicblanket.com; root /var/www/html; }
80番ポートで待ち受けるだけ。このファイルを、docker-compose.yml ファイルからみて ./conf.d/webapp.panicblanket.com.conf に保存する。ドキュメントルートはディレクトリを作っておけば、何もなくて構わない。
認証取得
取得する前に既存のwebサーバを停めておくこと。
次に、認証用のwebサーバだけ起動する。
$ docker-compose up -d nginx
webサーバが起動したら、certbot を使って認証取得する。
$ docker-compose run --rm certbot certonly --webroot -w /var/www/html -d webapp.panicblanket.com
これで証明書が /etc/letsencrypt 以下に保存される。
確認できたら Nginx も停める。
$ docker-compose down
webアプリとサーバの設定、再起動
証明書が取得できたので、それを利用して https://〜 でアクセスできるように設定ファイルを書き換える。
まずは、Nginx のバーチャルホストの設定ファイル。
upstream webapp { server webapp:9000; } server { listen 80; listen [::]:80; server_name webapp.panicblanket.com; location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name webapp.panicblanket.com; ssl_certificate /etc/letsencrypt/live/webapp.panicblanket.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/webapp.panicblanket.com/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_protocols TLSv1.3 TLSv1.2; ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256'; ssl_prefer_server_ciphers off; access_log /var/log/nginx/webapp.panicblanket.com.access.log main; error_log /var/log/nginx/webapp.panicblanket.com.error.log warn; keepalive_timeout 60; proxy_connect_timeout 60; proxy_read_timeout 60; proxy_send_timeout 60; location / { proxy_set_header Host $host; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://webapp; } }
80番ポートにアクセスしてきたらすべて https://~ にリダイレクトしている。
あと、docker-compose.yml(の該当部分)。
httpserver: image: nginx:1.18.0 container_name: httpserver restart: always ports: - 80:80 - 443:443 volumes: - /home/takatoh/docker-environment/nginx/etc:/etc/nginx/conf.d - /home/takatoh/docker-environment/nginx/log:/var/log/nginx - /etc/letsencrypt:/etc/letsencrypt
443番ポートと /etc/letsencrypt を追加。
これでコンテナを再起動すればOK。
$ docker-compose restart webapp httpserver