Androidはワンツーパンチ 三歩進んで二歩下がる

Android卵プログラマーの記録ブログ

【7】SteveLTN/HTTPS-PORTALを使ってLet's Encryptで全自動SSL対応を行う(GCP(GCE),Rails,Nginxリバースプロキシ,Docker)

> 2019/03/31追記 Let's Encryptがらみでトラブル発生してしまいました。
ここの情報がまちがっていたのでご参考になさった方は
8.3. FORCE_RENEW: 'true'について
の部分を確認していただけますとありがたいです。
申し訳ありませんがよろしくお願いいたします。

これはHerokuからGCP無料枠にRailsアプリを引越ししたときにやったことシリーズの【6】の続きです。
前回は GCE に 作成した VM インスタンス上でhttp://独自ドメイン:3000という形で Rails アプリを動かしてみました。
今回は最終目標であるhttps://www.独自ドメインでアクセスできるようにするところのメモです。
今回までの内容で引越し準備はほぼ終わりです。

1. 公式サイト

Let's Encrypt - Free SSL/TLS Certificates
NGINX | High Performance Load Balancer, Web Server, & Reverse Proxy
SteveLTN/https-portal: A fully automated HTTPS server powered by Nginx, Let's Encrypt and Docker.
steveltn/https-portal - Docker Hub
Rate Limits - Let's Encrypt - Free SSL/TLS Certificates

2. 前提

SteveLTN さんの作ったHTTPS-PORTALという Docker コンテナを使って面倒な SSL 対応を全自動で行いますが、前提として次のような条件が揃っている必要があります。

  • 80・443 番ポートが開いてて公開されている。
  • Docker エンジンがインストールされている。(Docker Composeを使用するのが強く推奨されている。 )
  • 独自ドメイン取得済であり、サーバーのアドレスと紐付けが済んでいる。

3. 目標

4. 注意点

Let's Encrypt は公正な使用のためレート制限があります。
・1 時間に 5 回の検証失敗
・登録ドメインごとに 1 週間に 50 の証明書
そのため、開発中で何度も Docker イメージをビルドする時は STAGE を production 以外にしましょう。
制限を超えてしまうと 1 週間後にレート制限が期限切れになるまで待つ必要があります。
参考:Rate Limits - Let's Encrypt - Free SSL/TLS Certificates

5. 参考サイト様

本当に参考になりました。ありがとうございます!

docker で全自動 Let's encrypt - Qiita
A Brief Introduction to HTTPS-PORTAL – SteveLTN
全自動 Let's Encrypt Docker コンテナ "https-portal" を使ってウェブサイトを HTTPS 化する - Qiita

6. RAILS_ENV を production にする

HTTPS-PORTAL を導入するにあたって、RAILS_ENV を production にしました。

/config/environments/production.rb に https 強制の指定があるので、ここからは HTTPS-PORTAL 必須です。

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  config.force_ssl = true

※ development(local) 環境でも一応 HTTPS-PORTAL のコンテナを導入してますが、SSL テストはしていません。
このように導入し、普通にhttp://localhost:3000/でアクセスしています。
興味ある方はこちらにローカル環境でのテスト方法が書いてあります。

※ 作業対象のプロジェクトは小さな個人開発アプリなので、development と production 環境しかありません。

7. HTTPS-PORTAL とは何かを知る

HTTPS-PORTAL は、Nginx、Let's Encrypt、および Docker を搭載した完全自動化された HTTPS サーバーです。
既存の Web アプリケーションを HTTPS 化することができます。

Let's Encrypt のクライアント certbot の実行、
SSL 証明書の取得、
適切な場所に設置、
失効する証明書の更新を自動化、
Nginx の設定

といった面倒な処理を全自動で行ってくれます。
本当に楽で助かりました。
作者さんありがとうございます!

ざっと公式を眺めたところ上記の他にこのような特徴があります。

  • リダイレクトを簡単に書くことが出来る
  • 自動コンテナ検出機能
  • Docker コンテナーではなくホストコンピューターで直接実行される Web アプリケーションも実行可能
  • マルチドメイン対応
  • Web アプリケーションにリクエストを転送する代わりに静的サイトを直接動かせる
  • 任意のホストコンピューターのディレクトリを/var/lib/https-portal volume としてマウント出来る → 他のアプリと証明書を共有可能
  • ベーシック認証を簡単に設定できる
  • IP アドレスでアクセス制限できる
  • 環境変数で Nginx の設定ができる
  • デフォルトの Nginx 設定を上書き可能
  • RSA 秘密鍵の長さを手動で設定できる(デフォルトでは 2048 ビット長)

8. HTTPS-PORTAL を stage:staging で動かしてオレオレ証明書の画面を表示する

このプロジェクトではdousa-kensho.tkというドメインを取得済みであり、
DNS サーバーの設定も済んでいます。(シリーズの【6】参照)
サブドメインwwwで設定してあります。

8.1. docker-compose.yml ファイル

docker-compose.yml ファイルはこんな感じです。

version: '3'
services:
  # HTTPS Reverse Proxy
  https-portal:
    image: steveltn/https-portal:1
    ports:
      - '80:80'
      - '443:443'
    links:
      - web
    restart: always
    environment:
      # STAGE: 'production'
      DOMAINS: 'dousa-kensho.tk => www.dousa-kensho.tk, www.dousa-kensho.tk -> http://web:3000'
      # FORCE_RENEW: 'true'
  db:
    image: postgres:11.1-alpine
    ports:
      - '5432'
    volumes:
      - db-data:/var/lib/postgresql/data
  web:
    build: .
    command: bash -c "rm -f ./tmp/pids/server.pid; bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/usr/src/app
      - bundle-data:/bundle
    environment:
      RAILS_ENV: production
    ports:
      - "3000:3000"
    depends_on:
      - db

volumes:
  bundle-data:
  db-data:

8.2. stage について

environment:の下の
STAGE:を省略するとデフォルトで staging と見なされます。
staging では、Let's Encrypt を使ったオレオレ証明書が作成されます。

8.3. FORCE_RENEW: 'true'について

environment:の下の
FORCE_RENEW: 'true'というオプションは、証明書の更新を強制的に行うというものですが、
コメントアウトしないと staging はうまく動かずオレオレ証明書の画面が表示されませんでした。
本番のときだけ有効にすべきであるようです。

【2019/03/31追記】
FORCE_RENEW: 'true'にしてはいけないものでした。
読んでしまった方申し訳ありません。
こちらを有効にして本番を動かすと毎日証明書を更新してしまうというものでした。
おかげでレートリミットに引っかかりサイトが1週間止まってしまいました。
その時のログにはacme-v02のAPIからこのようなレスポンスが返ってきていました。

Response Code: 429
Response: {
  "type": "urn:ietf:params:acme:error:rateLimited",
  "detail": "Error creating new order :: too many certificates already issued for exact set of domains: www.pockettoushituryou.com: see https://letsencrypt.org/docs/rate-limits/",
  "status": 429
}

このミスには我ながら呆れて物も言えません。
全体的に理解不足というのと英語の繋がりの理解がうまくいってなかったと凹んでおります。

8.4. オリジナルとサブドメインの両方をアクセス可能にする方法

https://dousa-kensho.tkhttps://www.dousa-kensho.tkでどちらでもアクセスしたいのですが、リダイレクトの指定との組み合わせでできました。

[Question] How can I https the origin and the subdomain? · Issue #112 · SteveLTN/https-portal · GitHub

f:id:sakura_bird1:20190311005805p:plain:w600

リダイレクトの場合は通常の->と違い=>を使います。

DOMAINS: 'dousa-kensho.tk => www.dousa-kensho.tk, www.dousa-kensho.tk -> http://web:3000'

手元で試してませんが、他の人の書き込みを見るとこの書き方でも出来るようです。

DOMAINS: 'dousa-kensho.tk -> www.dousa-kensho.tk, www.dousa-kensho.tk -> http://web:3000'

8.5. コンテナを起動

docker-compose up -dで起動します

8.6. staging でオレオレ証明書の画面が表示されるのを確認する

こんな感じの画面が表示されたら OK です。

f:id:sakura_bird1:20190311010543p:plain:w400

次はちゃんとサイトの画面を表示します。

9. HTTPS-PORTAL を stage:production で動かしてhttps://www.独自ドメインでサイトを表示する

9.1. docker-compose.yml ファイル

先程と違うとこだけ。
STAGEFORCE_RENEWをアンコメントします。

【2019/03/31追記】
FORCE_RENEW: 'true'にしてはいけないものでした。 8.3. FORCE_RENEW: 'true'について の部分を確認していただけますとありがたいです。

    environment:
      STAGE: 'production'
      DOMAINS: 'dousa-kensho.tk => www.dousa-kensho.tk, www.dousa-kensho.tk -> http://web:3000'
      # FORCE_RENEW: 'true'

コンテナを起動します。

証明書取得は結構時間がかかりました。
私が動かした時は 10 分ぐらいで終わりました。

9.2. HTTPS-PORTAL のログ

ログはこんな感じです。Starting crond ...と表示されたらサイトを表示出来るようになってると思います。

sakura@instance-1:~/Rails-PocketCarbo$ docker logs 8ee219b2cd02
[fix-attrs.d] applying owners & permissions fixes...
[fix-attrs.d] 00-runscripts: applying...
[fix-attrs.d] 00-runscripts: exited 0.
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-welcome: executing...
========================================
HTTPS-PORTAL v1.5.1
========================================
[cont-init.d] 00-welcome: exited 0.
[cont-init.d] 10-persist-env: executing...
[cont-init.d] 10-persist-env: exited 0.
[cont-init.d] 20-setup: executing...
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
.....................................................................................................................................................................中略
Generating RSA private key, 4096 bit long modulus
...............++++
.......................................++++
e is 65537 (0x010001)
2019/02/28 17:49:17 [notice] 151#151: signal process started
Generating RSA private key, 2048 bit long modulus
.................................................+++++
..............................+++++
e is 65537 (0x010001)
Signing certificates from https://acme-v02.api.letsencrypt.org/directory ...
Parsing account key...
Parsing CSR...
Found domains: dousa-kensho.tk
Getting directory...
Directory found!
Registering account...
Registered!
Creating new order...
Order created!
Verifying dousa-kensho.tk...
dousa-kensho.tk verified!
Signing certificate...
Certificate signed!
2019/02/28 17:49:23 [notice] 170#170: signal process started
Signed key for dousa-kensho.tk
2019/02/28 17:49:23 [notice] 171#171: signal process started
[cont-init.d] 20-setup: exited 0.
[cont-init.d] 30-set-docker-gen-status: executing...
[cont-init.d] 30-set-docker-gen-status: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
Starting crond ...

9.3. production でhttps://独自ドメインで画面が表示されるのを確認する

うまく表示できました。リダイレクトもされます。

f:id:sakura_bird1:20190311012954p:plain:w600

SSL 対応については以上です。

10. その他

このシリーズの【6】の続きです。
このページだけ読んだ方は読む必要はありません。

ここまでテスト出来たので Google Cloud DNS で行ったネームサーバーの設定を削除します。
余計な課金を防ぐためです。

残りの作業は

  • お名前 com での DNS 設定(A レコードを追加して GCE とドメインを紐付け+ Heroku の設定削除)
  • docker-compose.yml のドメインを本番稼働中のものに差し替える
    あたりがうまくいったら引越し完了です。(Heroku解約もある)
    長々としたエントリーになってしまいましたが、お付き合いありがとうございました!

11. 関連リンク