AppleのApp Transport Securityが延期になったとはいえ、SEO的にも信頼性的にも「とりあえず https にしとくか」みたいな時代になってきましたね 🤔
ということで、最近話題の Let's Encrypt を使って Jekyll + Heroku で構成されているRailsガイドを HTTPS に対応してみました。
具体的な実装は下記のプルリクエスト (以下、PR) で公開しています。
Enable SSL in Production #277
https://github.com/yasslab/railsguides.jp/pull/277
上記PRから汎用的なコミットをpickupして、1つずつ解説していこうと思います。
- Let's Encryptの使い方や、SSL証明書の取得手順などについては総合ポータルをご参照ください。
- 本番環境で動作確認するのは危険なので、HerokuのFork機能などでテスト用の環境を作り、そこで動作確認しましょう。
TL;DR
- acme_challenge gem で証明書取得が便利
- rack-rewrite gem で https にリダイレクト
- rack-contrib gem でヘッダーを適宜修正
1. Add acme_challenge gem to be certified
Let's EncryptでSSL証明書を取得するためには、特定のURLにアクセスしたら特定の結果を返す必要があります (ACME CHALLENGEと呼ばれています)。環境変数を使えば良さそうですが、「Jekyllで環境変数を扱うにはどうしたら良いのか?🤔」という点で結構はまっていました
_plugin
を使った実装とか色々と試してみましたが、結論としては acme_challenge gem が便利でした。
Simple rack middeware for responding to ACME challenge like LetsEncrypt uses.
https://github.com/soffes/acme_challenge
上記のREADMEに詳細がありますが、Jekyll + Heroku の場合は config.ru
の最初で呼び出すだけです (これだけ!)。
# config.ru
require 'acme_challenge'
use AcmeChallenge, ENV['ACME_CHALLENGE'] if ENV['ACME_CHALLENGE']
あとはローカル環境とプロダクション環境の環境変数 (ACME_CHALLENGE
) に、Let's Encrypt で取得したキーを入力します。キーの取得方法は総合ポータルの「Let's Encrypt の使い方」を参照してください。
なお、環境変数に代入する値は戻り値の方です。
Make sure your web server displays the following content at
http://railsguides.jp/.well-known/acme-challenge/FOOBAR before continuing:
FOOBAR.HOGEHOGE
※👆ここに表示される値を ACME_CHALLENGE に代入
If you don't have HTTP server configured, you can run the following
command on the target server (as root):
(中略)
Press ENTER to continue
本番環境でのセットアップが終わったら、総合ポータルの手順に従ってSSL証明書を取得しましょう。
2. Enforce SSL by rack-rewrite gem
1.
での手続きが無事に終わると、*.pem
が入手できるはずです。まずはそのファイルを Heroku にアップロードします。
$ heroku certs:add \
/etc/letsencrypt/live/railsguides.jp/fullchain.pem \
/etc/letsencrypt/live/railsguides.jp/privkey.pem
これで http と https の両方にアクセスできるようになりますが、せっかくなので https に統一させたいです。そのための実装がこちら。
Enforce SSL by rack-rewrite gem
https://github.com/yasslab/railsguides.jp/pull/277/commits/0cc483958d33e1781d607f3649ee424058222391
上のコミットでは、rack-rewrite gem を使って config.ru
に次のコードを追記しています (細かな記法は省略)。
require 'rack/rewrite'
use Rack::Rewrite do
if ENV['RACK_ENV'] == 'production'
r301 %r{.*}, 'https://railsguides.jp$&', :scheme => 'http'
end
end
これをデプロイすれば、ブックマークなどから http://railsguides.jp/ に来る人も https に切り替わるようになります。ただし、後述の問題がまだ残っているので、この時点ではまだ本番環境にデプロイしない方が良いです (テスト用の環境だけにデプロイする)
3. Implement custom headers with rack-contrib gem
2.
で https でも表示できるようになりましたが、実際に https でアクセスしてみると、Facebook の Like ボタンや Twitter の Tweet ボタンなど、他サイトのドメインから埋め込んだコードに対してエラーまたは警告が表示されてしまいます。
ここで表示されるエラーおよび警告は、そのサイトでどんなコードを使っているかによって変わります。したがって解決方法もその内容によるところが大きいですが、1つの汎用的な解決策としては、Content-Security-Policy (CSP) に許容するドメインを適宜追加していく方法があります。
例えばRailsガイドの場合は、はてなブックマークやGoogleアナリティクスなどをサイト内で使っているため、次のようなコードを config.ru
に追記しています。
require 'rack/contrib/try_static'
use Rack::TryStatic,
urls: %w[/],
root: '_site',
try: ['.html', 'index.html', '/index.html'],
header_rules: [
[:all, {
'Strict-Transport-Security' => 'max-age=31536000; preload',
'X-Xss-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'Content-Security-Policy' => "default-src 'self' 'unsafe-inline' 'unsafe-eval' *.dropboxusercontent.com *.google-analytics.com *.facebook.net *.facebook.com *.twitter.com ghbtns.com *.hatena.ne.jp *.st-hatena.com *.google.com;"
}],
# (後略)
コミットの詳細: Implement custom headers with rack-contrib gem
(rack/jekyll
や run Rake::Jekyll.new
を置き換えるようにして追記している点に注意してください )
CSPをどのように対応するかはサイトごとに異なると思うので、必要に応じて適宜コードを修正してください。目安としては、(Chromeの場合) アイコンが鍵マーク🔒に変わったかどうか、Console上にエラーや警告が出ないかどうかなどがあります。
他の目安としては、SSL Server Testなどの外部サービスを使ったチェックもあります。どこまで対応するかはケースバイケースだと思うので、その塩梅については各ケースに応じて判断してもらえればと思います
まとめ (再掲)
- acme_challenge gem で証明書取得が便利
- rack-rewrite gem で https にリダイレクト
- rack-contrib gem でヘッダーを適宜修正
https 対応のご参考になれば幸いです 😆
参考記事
宣伝
Railsガイドのソースコードはオープンソースとして yasslab/railsguides.jp から公開され、YassLab社によって運営されています。
コンテンツ内の誤字・脱字はもとより、コードとして改善できそうなところがあれば、お気軽に Issue または PR を送っていただけると嬉しいです!