Araoの技術ブログ

見習いエンジニアのAraoが、学んだことなどを書いたりするブログです。

Let's Encryptで取得した証明書をHerokuに反映させてみた

Let's Encryptで取得したSSL証明書を、Herokuで公開しているアプリケーションに反映させた話です。
Let's Encryptで取得した証明書は90日後までには更新しないといけないようなので、次回更新時に手順がわからず途方に暮れないように、作業手順を記事にしてみます。
SSL証明書などの知識に乏しいため、説明が不十分な点などあるかと思いますが、ご了承ください。
なお、作業環境は基本的にMacのターミナルです。

今回対象としたのは、既にHerokuで公開されているアプリケーションです。個人のドメインが設定されており、my_app.herokuapp.comだけでなく、my_domainにアクセスすることでも、アプリケーションを利用できます。
ちなみにそのアプリケーションはSinatraを利用しています。関連情報を検索するとRuby on Railsがよくヒットしますが、やることはそこまで大きく変わらないと思うので、気にせず進めていきましょう。

今回の記事の大まかな流れを示します。
1. Let's Encryptのクライアントソフトウェア、Certbotを導入する
2. Let's EncryptでSSL証明書を取得する
3. 取得したSSL証明書を個人のドメインに反映させる
こんな感じです。それではさっそく1番から進めます。

まずは1番、Let's Encryptのクライアントソフトウェア、Certbotを導入する方法を説明します。

参考:Let's Encrypt 総合ポータル Let's Encrypt の使い方

僕はこちらを参考に進めました。
初めに、gitのインストールが必要になりますが、僕は既にインストールされていたため、それは飛ばします。
次に、git cloneしてCertbotクライアントをダウンロードします。gitを使ったことのある方にはおなじみですね。
ダウンロードしたら、cloneしたディレクトリに移動して、Certbotを実行できる環境が整っているかテストします。基本的には書いてある通りに進めればいいのですが、僕はこんな警告が出ました。

$ cd certbot/
$ ./certbot-auto

WARNING: Mac OS X support is very experimental at present...
if you would like to work on improving it, please ensure you have backups
and then run this script again with the --debug flag!

どうやらMac OS Xのサポートはとても実験的(?)なようです。それでも動作させたいなら、バックアップを確認して、--debugフラグをつけて再びこのスクリプトを実行するように言われています。
もちろん--debugをつけて再度実行します。

$ ./certbot-auto --debug

実行すると、Homebrewで自動的にいくつかのパッケージがインストールされていきます。途中、root権限が要求されるので、パスワードを入力してあげましょう。
パッケージのインストールが終わると、Certbotクライアントが起動して、TUIが表示されます。ここでは一旦NOを選択して、クライアントを終了します。
これでLet's Encryptのクライアントソフトウェア、Certbotの導入は完了です。

次に2番、Let's EncryptでSSL証明書を取得する方法を説明します。

参考:Let's encryptをHerokuにあるRailsアプリに適用する

僕はこちらを参考に進めました。
Let's Encryptでは、特定のURLへのアクセスに対して特定の値を返すことで認証を行い、証明書を発行してもらう仕組みのようです。
具体的には、

my_domain/.well-known/acme-challenge/REQUEST_KEY

というURLにアクセスしたら、

RESPONSE_KEY

という値を返せばいいということです。もちろん、REQUEST_KEYとRESPONSE_KEYは、Let's Encryptのコマンドを実行したときに取得できる値です。
さっそくやってみます。

まず、今回の対象はSinatraを利用しているアプリケーションなので、main.rbに以下のような記述を追加してあげます。

get "/.well-known/acme-challenge/:id" do
    ENV["LETSENCRYPT_RESPONSE"] if params[:id] == ENV["LETSENCRYPT_REQUEST"]
end

LETSENCRYPT_REQUESTとLETSENCRYPT_RESPONSEはHerokuの環境変数です。
記述を追加したら、デプロイを忘れないようにしましょう。

次に、Herokuの環境変数を設定するのですが、その前にHeroku Toolbeltという、Heroku用のコマンドラインツールなどを含んだアプリケーションをインストールする必要があります。
次のステップでも必要になるので、インストールしておきましょう。

Heroku Toolbelt

無事インストールが完了したら、環境変数をセットします。セットの前にHerokuにログインしておきます。メールアドレスとパスワードを入力するだけです。

$ heroku login
Enter your Heroku credentials.
Email: my_email
Password (typing will be hidden): 
Logged in as my_email
$ heroku config:set LETSENCRYPT_REQUEST=aaa -a my_app
$ heroku config:set LETSENCRYPT_RESPONSE=bbb -a my_app

環境変数をセットしたら、ちゃんと期待通りの動作をするか、確認しておきましょう。
今回の例だと、

my_domain/.well-known/acme-challenge/aaa

にアクセスして、

bbb

という値が返ってくれば、期待通りの動作をしていることになります。

さて、いよいよSSL証明書を取得します。Certbotのディレクトリに移動して、以下のコマンドを実行します。

$ cd certbot/
$ ./certbot-auto certonly --manual -d my_domain
Requesting root privileges to run certbot...
  /Users/USERNAME/.local/share/letsencrypt/bin/letsencrypt certonly --manual -d my_domain
Password:

root権限のパスワードを入力して表示された画面でYESを選択すると、以下のような指示が表示されます。

Make sure your web server displays the following content at
http://my_domain/.well-known/acme-challenge/REQUESTREQUESTREQUESTREQUESTREQUESTREQUESTR before continuing:

RESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONS

If you don't have HTTP server configured, you can run the following
command on the target server (as root):

mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
cd /tmp/certbot/public_html
printf "%s" RESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONS > .well-known/acme-challenge/REQUESTREQUESTREQUESTREQUESTREQUESTREQUESTR
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()" 
Press ENTER to continue

ここで、新しいターミナルのウィンドウを開いて、そちらでHerokuの環境変数を設定し直します。

$ heroku config:set LETSENCRYPT_REQUEST=REQUESTREQUESTREQUESTREQUESTREQUESTREQUESTR -a my_app
$ heroku config:set LETSENCRYPT_RESPONSE=RESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONSERESPONS -a my_app

環境変数の設定が完了したら、元のターミナルのウィンドウに戻って、Enterキーを押します。
認証が成功すると、こんな感じの情報が表示されます。

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/my_domain/fullchain.pem.
   Your cert will expire on 2016-11-01. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

この先必要になるのは、

  • crtファイル:/etc/letsencrypt/live/my_domain/fullchain.pem
  • keyファイル:/etc/letsencrypt/live/my_domain/privkey.pem

です。
確認してみましょう。

$ sudo ls /etc/letsencrypt/live/my_domain/
cert.pem        chain.pem    fullchain.pem    privkey.pem

これでSSL証明書の取得は完了です。

最後に3番、取得したSSL証明書を個人のドメインに反映させる方法を説明します。

参考:Heroku SSL (Beta) | Heroku Dev Center

基本的にはこちらの解説にしたがって進めていきます。
日本語の情報を調べてみると、月額20ドルかかるとかなんとか出てきたりしますが、Beta版のHeroku SSLを使うことで無料で(クレジットカードの登録などをすることなく)反映できました。

SSLを使用するには、フラグを有効にしてCLIプラグインをインストールする必要があるようなので、ますはそちらを行います。

$ heroku labs:enable http-sni -a my_app
Enabling http-sni for my_app... done
$ heroku plugins:install heroku-certs
Installing plugin heroku-certs... done

もしかしたら何かメッセージが出てくるかもしれません。
次に、いよいよ証明書を反映させます。

$ sudo heroku _certs:add /etc/letsencrypt/live/my_domain/fullchain.pem /etc/letsencrypt/live/my_domain/privkey.pem -a my_app

ここでもし、

Only one SNI endpoint is allowed per app (try sni:update instead)

や、

Must pass either --type with either 'endpoint' or 'sni'

というメッセージが出て失敗する場合は、先ほどのコマンドの代わりに、

$ sudo heroku _certs:update /etc/letsencrypt/live/my_domain/fullchain.pem /etc/letsencrypt/live/my_domain/privkey.pem -a my_app

を実行してみてください。
確認のため、my_appの入力が求められますので、入力します。 無事反映されたら、以下のコマンドで証明書の詳細を確認できます。

$ heroku _certs:info -a my_app

また、SSLのセットアップが正しく行われているかの確認は以下のコマンドです。

$ curl -vI https://my_domain

最後に、DNSが正しく設定されているかの確認です。

$ heroku domains -a my_app
=== my_app Heroku Domain
my_app.herokuapp.com

=== my_app Custom Domains
Domain Name    DNS Target
──────   ────────────
my_domain      dns_target.com
$ dig my_domain cname +short
dns_target.com.

このようなレスポンスがあれば正しく設定されていますが、グローバルに反映されるにはタイムラグがあるようなのでそこには注意しましょう。
これで証明書の反映は完了です。

以上、少し長くなってしまいましたが、Let's Encryptで取得したSSL証明書を、Herokuで公開しているアプリケーションに反映させた話でした。
少々ややこしいですが、これで個人のドメインにも証明書を反映させることができます。更新時の自分の役に立てばいいのですが。