[Rails][Nginx][AWS] Let's EncryptをEC2上のRailsに入れてHttpsにする

Amazon linux 2 の場合は[こちら](https://qiita.com/Masahiro_T/items/9b81b31b3c21bd03fc09#amazon-linux-2%E3%81%AE%E5%A0%B4%E5%90%88)

---

EC2にssh接続しているとします。
まず、certbotをクローンします。

```bash:terminal
cd /usr/local
sudo git clone https://github.com/certbot/certbot
cd certbot
```

次に、証明書の作成を申請します。

申請するにあたって、Let's Encryptが認証するためのディレクトリを作成します。
（他の記事では```/var/www/html```などとしている場合が多いですが、Amazon Linuxではデフォルトでそのようなディレクトリはないので、新たに作成します。）

```bash:terminal
# ディレクトリ名は自由ですが、今回は letsencrypt-webroot とします。
sudo mkdir /var/www/letsencrypt-webroot
```

さらに、nginxの設定を追加します。

```bash:terminal
# 【】は置き換えてください
sudo vi 【nginxの設定ファイルのパス（例：/etc/nginx/conf.d/myconf.conf）】
```

```conf:nginxの設定ファイル
server {
  listen 80;

  ...

  # 以下を追加（^~は前方一致です）
  location ^~ /.well-known/acme-challenge/ {
      root /var/www/letsencrypt-webroot;
    }
}
```

その後、nginxを再起動します。

```
sudo service nginx restart
```

次に、以下のコマンドで認証可能かどうかを確認します。
（認証申請が1時間に5回Failすると、一定時間申請できなくなります（[Rate Limits - Let's Encrypt - Free SSL/TLS Certificates](https://letsencrypt.org/docs/rate-limits/)）。そのため、先に認証可能かどうかを確認します。）


```bash:terminal
# 【】の所は置き換えてください
/usr/local/certbot/certbot-auto certonly --webroot --agree-tos --debug -m 【メールアドレス（例：○○@○○.com）】 -d 【ドメイン名（例：○○.com）】 -w /var/www/letsencrypt-webroot --dry-run
```

以下が出たら認証可能です。

```
IMPORTANT NOTES:
 - The dry run was successful.
```

上のコマンドの```--dry-run```を外して認証申請をします。
（登録されたメールアドレスには、Let's Encryptからの通知（認証の有効期限が近づいてきた時の通知など）が届きます。）

```bash:terminal
/usr/local/certbot/certbot-auto certonly --webroot --agree-tos --debug -m 【メールアドレス（例：○○@○○.com）】 -d 【ドメイン名（例：○○.com）】 -w /var/www/letsencrypt-webroot
```

以下が出たら認証成功です。

```
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/○○.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/○○.com/privkey.pem
   Your cert will expire on 2019-07-30. 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
```

証明書が発行されたかを確認します。

```bash:terminal
# 【】は置き換えてください。
sudo ls -lrth /etc/letsencrypt/live/【ドメイン名（例：○○.com）】/
```

以下が表示されればOKです。

```
total 4.0K
-rw-r--r-- 1 root root 692 May  1 11:41 README
lrwxrwxrwx 1 root root  51 May  1 11:41 privkey.pem -> ../../archive/【ドメイン名（例：○○.com）】/privkey1.pem
lrwxrwxrwx 1 root root  53 May  1 11:41 fullchain.pem -> ../../archive/【ドメイン名（例：○○.com）】/fullchain1.pem
lrwxrwxrwx 1 root root  49 May  1 11:41 chain.pem -> ../../archive/【ドメイン名（例：○○.com）】/chain1.pem
lrwxrwxrwx 1 root root  48 May  1 11:41 cert.pem -> ../../archive/【ドメイン名（例：○○.com）】/cert1.pem
```

次に、nginxの設定ファイルをSSL用に更新します。

```bash:terminal
# 【】は置き換えてください
sudo vi 【nginxの設定ファイルのパス（例：/etc/nginx/conf.d/myconf.conf）】
```

```conf:nginxの設定ファイル
# 【】は置き換えてください。

# 1. server{}の中を以下の様に書きかえてください。
server {
  # listenを80から443に変更
  # listen 80;
  listen 443 ssl;
  
  location @app {
    ...

    # 以下を追記
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
  }

  # 以下を追記
  ssl_certificate /etc/letsencrypt/live/【ドメイン名（例：○○.com）】/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/【ドメイン名（例：○○.com）】/privkey.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}

# 2. 以下を追加してください。
server {
  listen      80;
  server_name 【ドメイン名（例：○○.com）】;
  return      301 https://$host$request_uri;
}
```

nginxを再起動します。

```bash:terminal
sudo service nginx restart
```

以上でSSLの設定は完了です。
ブラウザからこのサイトにアクセスしてみましょう。https接続になるはずです。
また、```http://```として接続した場合でも、自動でhttps接続になるはずです。

---

Let's Encryptの有効期間は3カ月と短いです。
自動で更新されるように、crontabで設定しておきましょう。

```bash:terminal
sudo crontab -e # rootでcrontabを編集すれば、設定コマンド内でsudoを使わなくて済みます。
```

```conf:crontab
0 4 * * * /usr/local/certbot/certbot-auto renew --no-self-upgrade --debug --post-hook "service nginx restart"
```

※ ``--no-self-upgrade``を付ける事で、自動でバージョアップするのを防いでいます。（[AWSドキュメント](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-2.html#letsencrypt)より）

※ ``--debug``を付けているのは、下記のエラーが出ないようにするためです。

※ ``--post-hook``で証明書更新後にNginxも再起動する様にしています。

```
FATAL: Amazon Linux 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!
Alternatively, you can install OS dependencies yourself and run this script
again with --no-bootstrap.
```

この例では、毎日午前4時に自動で更新チェックがかかるようになります。

設定後、cronデーモンを再起動します。

```bash:terminal
sudo service crond restart
```

---

# Amazon Linux 2の場合

上記の方法では Amazon Linux 2 でエラーが出ます。この場合は、[こちら](https://qiita.com/MysteriousMonky/items/4d3d857c0e68d4bfff39)を行なったあと、Nginxの設定ファイルに以下の追記をしてください。（`` 【ドメイン名（例：○○.com）】``は置き換えてください。）その後、``sudo service nginx restart``をしてください。

```conf:nginxの設定ファイル
server {
  # listen 80 のようになっていると思います。これを以下のように変えてください。
  listen 443 ssl;

　# ドメイン名は変更してください！
  server_name 【ドメイン名（例：○○.com）】;

  location @app {

    ...

    # 以下を追記 ------------------------------------
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    # ---------------------------------------------
  }

  # 以下を追記 ------------------------------------
  # ドメイン名は変更してください！（2箇所）
  ssl_certificate /etc/letsencrypt/live/【ドメイン名（例：○○.com）】/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/【ドメイン名（例：○○.com）】/privkey.pem
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # ---------------------------------------------
}

# 以下を追記　------------------------------------
# httpでのアクセスをhttpsに転送する設定です。 
# ドメイン名は変更してください！
server {
  listen      80;
  server_name 【ドメイン名（例：○○.com）】;
  return      301 https://$host$request_uri;
}
# ----------------------------------------------
```

#### 追記

[上記の参照サイト](https://qiita.com/MysteriousMonky/items/4d3d857c0e68d4bfff39)でのcrontabの設定は、以下のように書き換えても良いかもしれません。``root``を消して（私の環境では動かなかったため）、``service nginx restart``を追加しています。

```conf:crontab
# before
0 0,12 * * * root certbot renew --no-self-upgrade

# after
0 0,12 * * *      certbot renew --no-self-upgrade && service nginx restart
```

**追記**

crontabのコマンドをさらに改変しても良いでしょう。
- ``0,12 時に実行`` → ``4時に実行``（1回で十分なので）
- ``post-hook``として``service nginx restart``を設定して、証明書の更新が行われた時だけNginxも更新する様にする。

```conf:crontab
0 4 * * * certbot renew --no-self-upgrade --post-hook "service nginx restart"
```

# 参考
[Nginx Rails unicorn HTTPS SSL 対応 - Qiita](https://qiita.com/HeRo/items/f9eb8d8a08d4d5b63ee9)
[【Ruby on Rails】Nginxとunicornを使ってHTTPS（SSL）対応する方法 | Y-hilite](https://qiita.com/keisukeYamagishi/items/d176b359b90f6a6b8729)
[Let's Encrypt で Nginx にSSLを設定する - Qiita](https://qiita.com/HeRo/items/f9eb8d8a08d4d5b63ee9)