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