Let’s EncryptとcertbotとDockerを使ってwebアプリをSSL化する

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サーバをたてることで対処することができた。

手順

大まかな手順は次の通り:

  1. 認証取得用の設定ファイルを書く
  2. 既存webアプリのリバースプロキシになってるwebサーバを停める
  3. certbot の Dockerコンテナを使って認証取得
  4. 既存の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

参考にしたページ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください