Let's EncryptをHerokuにあるRailsアプリに適用する(2016/9月版)

## 2017/4/3追記
Herokuが公式にlet's encryptをサポートしてSSL証明書の自動更新までよろしくやってくれるようになりました。
また下記のQiitaの設定ではRailsにかぎらずHerokuのアプリケーションならなんでも適応されます。
http://qiita.com/mokamoto/items/cf98d3eb0ff39a342354

------------

これは以前書いた[Let's encryptをHerokuにあるRailsアプリに適用する](http://qiita.com/kon_yu/items/4fba3365f548407908f0)の更新記事です。Let's Encryptをローカルでインストールしたりする手順やコマンド名がcertbotと変わってしまったので新たに書き直した

前回からの差分は[certbotのインストール](http://qiita.com/kon_yu/private/302d8bc0924f292d378b#certbot%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)から読むと良いでしょう


## 前提条件
* 作業マシンmac(OSX)
* homebrewがインストール済み

# はじめに

無料でSSL証明書を発行できるLet's encryptを用いて、ステージング環境に対してSSL証明書を使えるようにした。
（以前はオレオレ証明書を使っていた)

ここでは以下のことを実施する

* Railsで組み込めるようにプログラムを変更する
* Let's encryptのコマンドを叩いて証明書を発行する

### 出来ないこと
SSL証明書の自動更新

普通のLinuxサーバの場合は自サーバでCrondでSSL証明書を更新しているが、Herokuの場合は今のところこれが出来ない。もしやり方がある場合は是非情報を提供していただきたい

## Railsプロジェクトに組み込む方法

Let's encryptでは証明書を取得するために、

> ドメイン名/.well-known/acme-challenge/xxxx

と言うURLにアクセスしてきて、特定の値をレスポンスとして返す必要がある
URLのxxxxとレスポンスの特定の値は後述するLet's encryptの証明書取得のためのコマンドを実行した際に取得できる

### ルーティングの設定をする
config/routes.rb

```ruby
BestTeacher::Application.routes.draw do
  # ssl証明書発行用
  get ".well-known/acme-challenge/:id" => "index#letsencrypt"
  
  # 以下省略
end

```

### コントローラの設定をする

適当なコントローラ(index_controller.rb)に、環境変数で指定したURLパラメタがあれば、また環境変数で設定したレスポンスを返すようにする

```ruby
class IndexController < ApplicationController
  def letsencrypt
    if params[:id] == ENV["LETSENCRYPT_REQUEST"]
    render text: ENV["LETSENCRYPT_RESPONSE"]
    end
  end
end
```

### Herokuの環境変数設定

ここではどう確認のために適当な値を環境設定で入力して、レスポンスが返ってくるか確認する
ステージングにデプロイして、動作確認をする

```
heroku config:set LETSENCRYPT_REQUEST=SAMPLE_REQUEST_KEY -a YOUR_APP_NAME
heroku config:set LETSENCRYPT_RESPONSE=SAMPLE_RESPONSE_KEY -a YOUR_APP_NAME
```


## certbotのインストール

```sh
> brew install certbot
```

## 証明書発行コマンド実行(更新の場合も同様)

sudoをつけて、証明書発行のコマンドを実行

```sh
> sudo certbot certonly --manual -d www.example.com(登録するサーバのFQDN)
```

上記コマンド実行すると以下のようなダイアログが表示される。

すでにLet's encryptで作成したSSL証明書がある場合は2の"Renew & replace the cert~"を選択する

![スクリーンショット 2016-09-23 11.18.59.png](https://qiita-image-store.s3.amazonaws.com/0/5390/3e60eaec-b483-b225-e386-98ecfb86c68f.png "スクリーンショット 2016-09-23 11.18.59.png")

実行すると以下のような文言が表示される

```
Make sure your web server displays the following content at
http://www.example.com/.well-known/acme-challenge/xxxxxxxxxxxxxxxxx before continuing:

yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

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" yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy > .well-known/acme-challenge/xxxxxxxxxxxxxxxxx

# 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
```


大事なのは以下の部分で、

```
Make sure your web server displays the following content at
http://your_app.herokuapp.com/.well-known/acme-challenge/xxxxxxxxxxxxxxxxx before continuing:

yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
```

上のように、

```
http://your_app.herokuapp.com/.well-known/acme-challenge/xxxxxxxxxxxxxxxxx
```
と言うアクセスに対して

```
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
```

というレスポンスを返すように環境変数を設定してあげる必要がある。

### Let's encryptに求められた環境変数の設定

```
heroku config:set LETSENCRYPT_REQUEST= XXXXXXXXXXXXXXXXX -a YOUR_APP_NAME
heroku config:set LETSENCRYPT_RESPONSE= XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -a YOUR_APP_NAME
```


環境変数の設定が完了したら、コンソールで放置していた以下の画面でエンターキーを押す

```
Press ENTER to continue
```

※ここでうっかりエンターボタンを押してしまうと、適切なレスポンスを返せずに失敗してしまうが、
もう一度certbotのコマンド実行からやり直しても、ある程度の時間は求められるurlとレスポンスの値は変わらないので安心だ


処理がうまくいくと以下のメッセージが表示される

```
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.example.com/fullchain.pem.
   Your cert will expire on 20xx-xx-xx. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot 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

```

PC内の以下の場所にキーが保存されたことがわかる

/etc/letsencrypt/live/www.example.com/fullchain.pem


## HerokuにSSL証明書を更新/登録する

-----
※(参考)SSL証明書の登録は、古いSSL Endpointの方式から新しい方式に変わったので移行する場合は以下の記事を参考にすると良い

[Herokuで2016/9/22から無料でSSL証明書を登録できるようになったので、既存のSSL Endpointから移行する方法](http://qiita.com/kon_yu/items/e4443cd47d3601d8feb4)

-----


herokuSSL証明書を登録するにはこの公開書きファイルfullchain.pemと、もう一つ秘密鍵ファイルprivkey.pemが必要で、

それぞれ以下のパスに存在する

```
/etc/letsencrypt/live/www.example.com/fullchain.pem 
/etc/letsencrypt/live/www.example.com/privkey.pem
```

アプリケーションをGit管理しているディレクトリへ移動し、鍵ファイルをコピーする

```
> cp /etc/letsencrypt/live/www.example.com/privkey.pem /etc/letsencrypt/live/www.example.com/fullchain.pem your_application_git_dir
```

更新の場合

```
heroku certs:update fullchain.pem privkey.pem -a YOUR_APP_NAME
```


新規追加の場合

```
heroku certs:add fullchain.pem privkey.pem -a YOUR_APP_NAME
```