0
1

More than 3 years have passed since last update.

AWS EC2 に Docker Compose で nginx + certbot (Let's Encript) + Redmine + MySQL 入れてみた(3/5 Nginx, Certbot 導入と SSL 証明書の取得・更新)

Last updated at Posted at 2021-08-31

↓ 1/5 はじめに

↓ 2/5 Docker, Docker Compose など基盤構築

前回記事の続きです。
この先使用する Compose ファイル(docker-compose.yml)については、前記事の最後を参照してください。

そして、ここから面倒な作業が続きます。
以下の順番で設定・操作を nginx, certbot を起動・再起動を繰り返すことで、SSL 環境を作ります。

  1. DHE ciphers 利用のための準備(https 対応の際に必要)
  2. Nginx 起動 ⇒ Webサイトの設定ファイル設置 ⇒ 再起動(http のみ)
  3. certbot 起動(SSL証明書取得)
  4. Compose ファイル、Nginx 設定ファイルの差替(https 対応)
  5. nginx 再起動(https 対応)
  6. certbot 再起動(SSL証明書更新用)

DHE ciphers 利用のための準備(https 対応の際に必要)

SSL で利用される暗号化方式には種類がいくつかあり、その1つに DHE ciphers があります。
SSL 対応版のconf に差し替える前に、DHE ciphers が使えるようにしておく必要があるため、必要なファイルをここで準備しておきます1 2

$ mkdir -p data/ssl/private
$ openssl dhparam 2048 -out data/ssl/private/dhparam.pem

この dhparam.pem は SSL有効化の際に差し替える nginx の conf ファイル内、ssl_dhparam パラメータで紐付けています。
そのためこのファイルは、本来この時点(SSL証明書取得前の時点)では必要ありません。
しかしながら Compose ファイルの中でこのファイルをマウントしている関係上、先にここで準備する必要があります3

Nginx 起動 ⇒ Webサイトの設定ファイル設置 ⇒ 再起動(http のみ)

Nginx 設定ファイルの保存場所(ボリューム)を作成・構築するため、最初は何も考えずに Nginx を起動します。

$ docker-compose up -d nginx

次に Certbot による SSL 証明書取得からはじめます。
そのため Nginx も、http だけ応答する状態から始めるので、そのための設定を準備します。

次に Webドメイン用の Nginx 設定ファイル「vhost-www.example.jp.conf」を作成し、サーバにアップロードします4

vhost-www.example.jp.conf
server {
    listen 80;
    server_name www.example.jp;

    root /usr/share/nginx/html;
}

記載の通り、まずは 80ポートのみで一旦アップロード。
SSL 証明書取得後に 443 ポートに関する記述を追加した設定ファイルに差し替えます。

ちなみにドキュメントルートの場所についても注意してください5

次に、この設定ファイルをホストからコンテナにコピーします。
(「docker-compose cp」コマンドはありません。コピーは「docker cp」コマンドを使います。)

$ docker cp vhost-www.example.jp.conf nginx:/etc/nginx/conf.d/vhost-www.example.jp.conf

この状態で Nginx 最初の再起動し、設定ファイルの内容を反映します。

$ docker-compose stop nginx
$ docker-compose up -d nginx

一旦ブラウザからアクセスして、Nginx のブランクページが表示されるか確認します。

http://www.example.jp

最近のブラウザは、URL だけで確認しようとすると https:// 優先(SSL 有)でアクセスします。 この時点では SSL はまだ設定していないので、必ず http:// (SSL 無)で確認してください。

certbot 起動(SSL証明書取得)

次に certbot を起動し、証明書を取得します。
その際、最初は Compose ファイルに記述されている ENTRYPOINT を無視させる必要があります。
そのため、以下の通り docker-compose コマンドに --entrypoint オプションを指定して「空白」を記述します。

$ docker-compose run --rm --entrypoint '' certbot certbot certonly --webroot -w /usr/share/nginx/html -d www.example.jp --agree-tos -m foobar@example.jp --test-cert

これによって ENTRYPOINT の動作が上書き(オーバーライド)されます。
さらに替わりとして、SSL 証明書を取得するために「certbot certonly」コマンドを記述しています[^9]。

補足:certbot コマンドの非対話オプションについて

ここで使用した certbot コマンドは、-d, --agree-tos, -m などのオプションオプションを用いることで、オプション未使用の場合は表示される「対話形式」を極力省略しています。
しかしながら、、、

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: 

…という対話形式だけは、省略する方法(オプション)が分かりませんでした。
よって、ここだけは対応形式で応答する必要があります。

要約すると「Certbot 開発団体である EFF に、コマンド内で登録するメールアドレスを共有してもよいか」という確認メッセージです。 「Y」(Yes) とすると、コマンド内で登録したメールアドレスが EFF に共有され、EFF からニュースやキャンペーンなどお知らせ等を配信してくれる模様。 各自の判断で Y/N いずれかを選択してよいと思いますが、個人的には必要ないので、「N」(No) を入力して Enter。

本当はこの対話もコマンドオプションで省略したかったのですが、対応するオプション等がよく分からなかった。 判明した時点で、記述を訂正する予定です。

Compose ファイル、Nginx 設定ファイルの差替(https 対応)

前項の certbot コマンドに問題がなければ、SSL 証明書が取得できているはずです。
(正常かエラーかは、メッセージの内容を確認してください。)

問題なければ https(SSL)対応の記述をCompose ファイル、Nginx 設定ファイルそれぞれに反映します。

docker-compose.yml(抜粋)
    :<snip>
  nginx:
      :<snip>
    ports:
      - "80:80"
      - "443:443" # 443 ポートが EXPOSE されてないので、ports で開ける
          :<snip>

(docker-compose.yml は、443 ポート開放のコメント解除のみ。)

vhost-www.example.conf
server {
    listen        80;
    server_name   www.example.jp;

    # certbot renew (SSL証明書の更新用)
    location /.well-known/acme-challenge/ {
        root /usr/share/nginx/html;
    }

    # それ以外は https に一律強制リダイレクト
    return 302    https://www.example.jp$request_uri;
}
server {
    listen        443 ssl;
    server_name   www.example.jp;

    ssl_certificate          /etc/letsencrypt/live/www.example.jp/fullchain.pem;
    ssl_certificate_key      /etc/letsencrypt/live/www.example.jp/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    ssl_trusted_certificate  /etc/letsencrypt/live/www.example.jp/chain.pem;
    ssl_dhparam              /ssl/private/dhparam.pem;

#    add_header Strict-Transport-Security "max-age=63072000" always;

    ssl_session_cache shared:SSL:10m;

    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    location / {
        root /usr/share/nginx/html;
    }
}

(conf のほうは、80ポートの「certbot renew」用の記述と、https 強制リダイレクト、443 ポートに関する記述を追加。)

Nginx 設定ファイルについては、コンテナ(ボリューム)内に再設置します。

$ docker cp vhost-www.example.jp.conf nginx:/etc/nginx/conf.d/vhost-www.example.jp.conf

http ⇒ https 強制リダイレクトは 302(一時的)リダイレクトを使用していますが、環境構築が終了し、運用が安定した時点で 301(恒久的)リダイレクトに変更したほうが良いと思います。 302 の場合は「ブラウザ ⇒ サーバ」に都度アクセスしたうえでリダイレクトを判断されますが、301 の場合、ブラウザ内にキャッシュが残っているかぎり、サーバにはアクセスせず、キャッシュの内容からリダイレクトを取得し遷移します。これは無用なアクセストラフィックの軽減に繋がります。 ただしこれは、すべてのブラウザがこの挙動をするとは限りません。 また、誤って 301 リダイレクトを指定した場合、ブラウザ側のキャッシュを削除しないかぎり、正しい遷移ができなくなる可能性があるので、注意が必要です。

「add_header Strict-Transport-Security」は、HSTS(HTTP Strict Transport Security)に関する設定です。 こちらもセキュリティ上は、設定することが望ましいです。 しかしながら誤って設定した場合、こちらもブラウザ側で設定を解除する必要が生じる可能性があります。 そのため環境構築が終了し、運用が安定した時点でコメント状態を解除したほうが良いと思います。

nginx 再起動(https 対応)

Nginx を再起動して SSL を有効化します。

$ docker-compose stop nginx
$ docker-compose up -d nginx
$ docker-compose logs nginx

上記3つめのコマンドは、Nginx のログ確認のため。エラーが出てなければ OK。
この状態で https, http 両方で Nginx のブランクページが表示されるかを確認します。

http://www.example.jp
https://www.example.jp

さらに https の場合は、証明書が適切に入っているか(鍵マークになっているか)と、証明書の中身も確認しましょう。

ここで一旦、EC2 のスナップショットを取得。(2番目) この先は Redmine, MySQL の環境構築を行うので、破壊時の戻り先にしておく。

certbot 再起動(SSL証明書更新用)

certbot も docker-compose.yml の entrypoint の内容を反映させるため、再起動します。

$ docker-compose stop certbot
$ docker-compose up -d certbot
$ docker-compose logs certbot

これによって、Composer ファイル内の記述されている COMMAND が実行され、定期的に「certbot renew」が実行されるようになります。

ちなみに3つめのコマンドは、certbot renew のログ確認のためです。 エラーが出てなければ問題ありませんが、nginx, certbot ともに、設定作業後しばらく(初回の SSL 更新が行われるまで)は、「docker-compose logs」コマンドで定期的に動作を確認したほうが良いと思います。

次の記事に続きます。

↓ 4/5 MySQL, Redmine の構築

↓ 5/5 引用・謝辞

参考・引用


  1. nginx 1.11 以降で DHE ciphers を使えるようにしたい場合、自分で DH パラメータファイルを準備する必要がある。そのため、 dhparam.pem をここで生成する。ちなみにDHE ciphers が使えない場合は、別の暗号化方式が利用できれば事足りると思う。しかしながら DHE ciphers を利用した通信ができなくなるぶん、利用可能な暗号化の選択肢も狭まることに繋がる。その悪影響を考慮すると、適切に設定しておくに越したことはないと思う。 

  2. このファイルは、暗号化通信のセキュリティ観点から、ホスト毎に設定すべき性質のファイル。コンテナ(ボリューム)内には格納しないのが適切かと思う。 

  3. あとで Compose ファイルを書き換えるのもあり。 

  4. Nginx コンテナ運用のベストプラクティスに沿って行う場合、(1) Nginx 設定テンプレートファイル(vhost-www.example.jp.conf.tmplate)を作成し、(2) バインドマウントした templates に設置、(3)Nginx を起動すると設置したテンプレートファイルから自動的に設定ファイル(vhost-www.example.jp.conf)が構築される、という流れに沿って行うべき。でも今回は、テンプレート用に一時的なバインドマウントを施す手間を省略するため、このような手順とした。 

  5. ドキュメントルートは /var/www/html が割と有名ですが、nginx のドキュメントルートは、公式も含めて /usr/share/nginx/html が一般的な模様。 

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1