AWS EC2 Amazon Linux 2にNginxをインストールしてcertbotでTLS有効化

## EC2 Amazon Linux 2のイメージの作成
ベースイメージをAmazon Linux 2に指定してインスタンスを立ち上げる。
起動の際にセキュリティグループからHTTP&HTTPSのインバウンドを許可しておくとスムーズ。
また、SSHについては**マイIP**を指定して、キーペアを保存している自分のマシン以外からアクセスできないようにしておくと警告がなくなる。
無料枠であれば30GBまでストレージを作成できるので、そちらも任意で変更しておくと良い。

パブリックDNSを利用してHTTPを利用したリクエストができるように、Elastic IPの関連付けも行っておくこと。

## Nginxのインストール&有効化
Amazon Linux 2ではデフォルトでNginxが入っていない。
`amazon-linux-extras`リポジトリからNginxをインストールする必要がある。

```shell
# Nginxがリポジトリとして登録されていることを確認
$ amazon-linux-extras
# nginx1.12=latest         available      [ =1.12.2 ]
# ↑↑↑↑↑↑↑↑↑  この文字列をinstallで指定する

$ sudo amazon-linux-extras install nginx1.12

$ amazon-linux-extras
# nginx1.12=latest         enabled        [ =1.12.2 ]
# インストール後利用可能になっていることを確認

$ sudo systemctl start nginx.service
# Nginxを起動する

$ sudo systemctl status nginx.service
# Active: active (running) の文字列が見つかれば起動されている

$ curl -I <YOUR_INSTANCE_PUBLIC_DNS>
# 以下のようなHTTPのヘッダーが取得できればOK

# HTTP/1.1 200 OK
# Server: nginx/1.12.2
# Date: Mon, 29 Apr 2019 08:58:21 GMT
# Content-Type: text/html
# Content-Length: 3520
# Last-Modified: Mon, 17 Dec 2018 00:17:59 GMT
# Connection: keep-alive
# ETag: "5c16eb37-dc0"
# Accept-Ranges: bytes
```

## certbot-autoのインストール
```shell
# /usr/bin のディレクトリに追加するので、rootユーザとして実行する
# certbotのプログラムをダウンロードし、/usr/bin/certbot-autoディレクトリに保存
# /usr/bin にはPATHが通っているので、そのまま利用できる
$ sudo curl https://dl.eff.org/certbot-auto -o /usr/bin/certbot-auto

# certbot-autoコマンドの実行権限をrootに限定する
$ sudo chmod 700 /usr/bin/certbot-auto
```

### Amazon Linux 2 でcertbot-autoを利用可能にする
certbotではAmazon Linux 2のOS実行環境にまだ対応していないため、コマンドがうまく動作しない。そのため、インストールしたプログラムの一部を書き換える必要がある。

`/usr/bin/certbot-auto`ファイル内で`Amazon`で検索をかける。
該当する箇所のプログラムを次のように書き換える（**書き換える際にはroot権限が必要となる**）。

#### Before
該当箇所は消去するのではなく、コメントアウトするだけでも良い。

```shell
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
  Bootstrap() {
    ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
  }
  BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
```

#### After
```shell
elif grep -i "Amazon Linux" /etc/issue > /dev/null 2>&1 || \
   grep 'cpe:.*:amazon_linux:2' /etc/os-release > /dev/null 2>&1; then
  Bootstrap() {
    ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
  }
  BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
```

## Nginxのドキュメントルートを確認する
HTTPサーバとしてのNginxがどこのディレクトリをドキュメントルートとしているかを確認し、certbot-autoコマンドを実行する際に利用できるようにする。

```shell
# Nginxの設定ファイルの内容のうち、ドキュメントルートに指定している箇所を検索
$ cat /etc/nginx/nginx.conf | grep root -n
# 次のような検索結果が返される
# 65行目はコメントアウトされているので
# 42行目の設定の/usr/share/nginx/htmlがドキュメントルートに指定されていること
# を確認できれば良い

# 42:        root         /usr/share/nginx/html;
# 65:#        root         /usr/share/nginx/html;
```

（上例だと）42行目の`/usr/share/nginx/html`ディレクトリ以下の`index.html`はEC2インスタンスのパブリックDNSにブラウザからアクセスした際に返されるHTMLの内容と同じであることが確認できる。
Nginxは公開可能なファイルを`/usr/share/nginx/html`に格納しており、`/etc/nginx/nginx.conf`の内容の通りに`http://<PUBLIC_DNS>/`へのリクエスト結果として`index.html`の内容を返していることがわかる（Nginxのデフォルトの挙動）。

## EC2インスタンスにドメインを割り当てる
EC2を指定した際にデフォルトで割り振られるPublic DNSの値はIPアドレスと連動しており、このIPアドレスの値が起動時に変更されうるため、あらかじめ取得したドメインに変更しておく必要がある。

Route53からドメインを取得し、EC2のElastic IPと関連付けを行う。
しばらく時間が経つとDNS Lookupが可能になるので、数十分後に指定したFQDNでブラウザからNginxのデフォルトページが見れることを確認すること。

## certbotで証明書を発行する
- <YOUR_DOCUMENT_ROOT>:
    - [Nginxの設定](#Nginxのドキュメントルートを確認する)で確認できるドキュメントルートの値
- <PUBLIC_DNS>:
    - [Route53](#EC2インスタンスにドメインを割り当てる)でインスタンスに紐づけたPublic DNSの値

```shell
# --webroot Webサーバを利用して証明書を取得する
# -w ドキュメントルートの値
# -d ドメインの値
# --debug Amazon Linux 2は試験的な運用のためこのオプションを指定
# -v 証明書発行の過程を詳細に表示する
$ sudo certbot-auto certonly --webroot -w <YOUR_DOCUMENT_ROOT> -d <PUBLIC_DNS> --debug -v

# 以下のような出力が最後に得られれば成功

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

# 保存されているディレクトリの中身を確認
$ sudo ls /etc/letsencrypt/live/<PUBLIC_DNS>/
```

## HTTPSの有効化
Nginxの設定ファイルを編集し、SSLが有効化された通信を可能にする。
以下のような設定を追加すれば良い。

```conf
server {
    listen 443 ssl;
    ssl_certificate "/etc/letsencrypt/live/<PUBLIC_DNS>/fullchain.pem";
    ssl_certificate_key "/etc/letsencrypt/live/<PUBLIC_DNS>/privkey.pem";
}
```

コンソールからNginxのサービスを再起動させれば、HTTPSでの通信が可能になる。

```shell
$ sudo systemctl restart nginx

$ sudo systemctl status nginx
# `OK` の文字が見つかれば良い
```

## 証明書の自動更新の有効化
cronを利用して、certbotの証明書発行を自動的に行う。

まずは、systemctlのパスを確認する。
```shell
$ which systemctl
# /usr/bin/systemctl など
```

次に、cronのスケジュールファイルを編集し、自動更新を可能にする。

```shell
$ crontab -e
```

以下の内容を追記する。
コマンド後半の`/usr/bin/systemctl`の箇所は適せん上の`which`コマンドの結果に書き換える。

```
0 3 10 * * /usr/bin/certbot-auto renew && /usr/bin/systemctl reload nginx
```

## HTTPによるアクセスをHTTPSにリダイレクトする
HTTPの設定を記述している箇所に次を追記する。

```conf
# Certbotのチャレンジ用のパスを用意する
location ^~ /.well-known/acme-challenge/ {
    root /usr/share/nginx/html;
}

# ルートパスの設定を書き換える
# ポート80(HTTP)によるアクセスを443(HTTPS)にリダイレクトする
location / {
    return 301 https://$host$request_uri;
}
```

上記の設定を行うことにより、通常のサービスで利用されるパスを開けることができる。