無料 SSL 証明書を発行して簡単に Web サイトを HTTPS 対応させる方法

## 概要
クラウドサービスで立ち上げた Web サイトが、ログイン認証など個人情報を打ち込んだりする可能性が出てきたので、念の為 Web サイトを HTTPS 対応しておこうと思って、その備忘録。

本来はお金が多少かかるところ、無料で SSL 証明書を発行するサービス「[Let’s Encrypt](https://letsencrypt.org)」を利用して、HTTPS サイトにしてみた。

GCP (Google Cloud Platform) の GCE (Google Compute Engin) で立ち上げた仮想サーバに対して HTTPS 対応を行う。
ちなみに、GCE じゃなくて GAE (Google App Engin) だったら、普通にコンソールメニューの操作から無料で HTTPS にできるらしい。(GAE にしときゃよかった・・・)

## 前提

- サーバマシン： GCP の GCE の仮想サーバ
- OS： CentOS 7
- Python： 2.7.5 (必要なので、無かったら Python 入れてください。無くても他に方法は一応あるけど)
- Git： 1.8.3.1 （こちらも必要なので、無かったら入れてください。もちろん、無くても方法はあるけど）
- Web サービス： Nginx

## SSL 証明書について

基本的なところですが、SSL 証明書は、その Web サイトを運営する個人・組織の実体を確認してからでないと発行できないもの。
そんな SSL 証明書には、以下のようにいくつか種類がある。

|タイプ|内容|
|:--|:--|
|DV SSL証明書|人の手間が発生せず自動でできる為、無料で利用できる|
|EV SSL証明書|大人が動いているので、お金かかる|
|OV SSL証明書|大人が動いているので、お金かかる|

今回利用するのは「DV SSL証明書」

## Let's Encrypt とは
SSL サーバ証明書を無料で発行して、HTTPS を普及させる事を目的としているプロジェクト。
発行される SSL 証明書は、有効期限 3ヶ月。
なので、継続的に利用するなら、更新する何かしらの対応が必要になってくる。

しかし、利用時には、「[Certbot](https://certbot.eff.org)」というソフトウェアを使用することで、SSL/TLS サーバ証明書の取得・更新作業を自動化できる方式になっている。
独自ドメインがあれば、簡単なコマンド操作で SSL/TLS 証明書（無料）を取得できる。

ちなみに、非公式ながら、「[Let's Encrypt 総合ポータル](https://letsencrypt.jp)」という日本語の解説サイトもあるので参考にしてみても良いかも。

## 手順

以下、基本的にルート権限が必要なので `sudo` とか付けてますが、適宜読み替えてください。

### 1. サーバに Certbot を導入

インストールする方法もあるが、簡単なので git でクローンするだけの方法をとる。
場所はどこでもいいので、適当な場所にクローン。

```bash
# ソフト導入ディレクトリに移動 (システム管理者用のみが利用するライブラリ等を格納する場所)
cd /usr/local
# ルート権限でクローンする
sudo git clone https://github.com/certbot/certbot
```

#### 確認

Cerbot の動作確認として、ヘルプが表示できるかを利用して確認

```bash:/usr/local/
cd certbot
./certbot-auto --help
```

上記のコマンドを打って、以下のようなヘルプが表示されればひとまずOK。

```bash
...
Help for certbot itself cannot be provided until it is installed.
  --debug                                   attempt experimental installation
  -h, --help                                print this help
  -n, --non-interactive, --noninteractive   run without asking for user input
  --no-bootstrap                            do not install OS dependencies
  --no-self-upgrade                         do not download updates
  --os-packages-only                        install OS dependencies and exit
  --install-only                            install certbot, upgrade if needed, and exit
  -v, --verbose                             provide more output
  -q, --quiet                               provide only update/error output;
                                            implies --non-interactive
All arguments are accepted and forwarded to the Certbot client when run.
```

### 2. SSL サーバ証明書を取得する

#### Web サービスを止めておく

証明書取得で使う Certbot は 80 番ポートを使用します。なので、Web サーバ (今回だと Nginx) を一旦、止めておきましょう。

```bash
sudo systemctl stop nginx # 停止コマンド
sudo systemctl status nginx # Web 動いているか確認
```

#### インストール方法①： 対話式で取得する場合

以下、簡単に処理が終わる「スタンドアローン モード」を利用して取得する。

```bash:/usr/local/certbot/
./certbot-auto certonly --standalone -t
```

- 上記のコマンドを叩くと、色々とインストールを聞かれるので、ひとまず全部 `yes` や `Agree` で肯定
- 途中、メールアドレスどうするか聞かれたら、管理者用のメアドを適当に用意して設定しておく
- `lease enter in your domain name(s)` とドメインを聞かれたら、ドメインを入力 （ただし、名前解決できる、通信可能な状態出ないとダメ）

#### インストール方法②： 対話式ではなく、サクッと１行で取得する場合

オプションに、ドメインや管理者メアドとか付けて、以下のコマンド１行で終わり。

```bash:/usr/local/certbot/
./certbot-auto certonly --standalone -d www.hogehoge.com -m admin@www.hogehoge.com --agree-tos -n
```

#### インストールに成功した場合

以下のように「`Congratulations!`」とか出れば成功。それ以外の場合は、失敗してると思われる。（大体は、80 ポートを握ってる Web サーバとかを止めてないよエラーとか）
とりあえず、成功しても失敗しても「`IMPORTANT NOTES:`」とは必ず出る。

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

成功すると、`/etc/letsencrypt/` に以下の様に色んなファイルが出来あがっている。

```bash:処理成功後、出来あがるファイル群
/etc/letsencrypt/
      ├── accounts/ # アカウント情報
      ├── archive/  # 取得した証明書の実体 (ドメインごとのサブディレクトリに保存される)
      │     └── www.hogehoge.com/
      │           ├── cert1.pem
      │           ├── chain1.pem
      │           ├── fullchain1.pem
      │           └── privkey1.pem
      ├── csr
      ├── keys
      ├── live/     # 最新の証明書の実態へのシンボリックリンク
      │     └── www.hogehoge.com
      │           ├── cert.pem -> ../../archive/www.hogehoge.com/cert1.pem
      │           ├── chain.pem -> ../../archive/www.hogehoge.com/chain1.pem
      │           ├── fullchain.pem -> ../../archive/www.hogehoge.com/fullchain1.pem
      │           └── privkey.pem -> ../../archive/www.hogehoge.com/privkey1.pem
      ├── renewal/  # 証明書更新のための設定
      │     └── www.hogehoge.com.conf
      └── renewal-hooks
```

### 3. Web サーバの設定を変更

SSL 証明書が発行できたので、ポートを 80 番から 443 番に変更する。
また、HTTP (80 番) にアクセスされたら HTTPS (443 番) にリダイレクトするようにしておく。
私の場合は、`etc/nginx/conf.d/default.conf` に Nginx のサービス設定を書いてるので、そのファイルを以下のように変更。

```bash:etc/nginx/conf.d/default.conf
# 以下のカタマリを追記 (http を https にリダイレクトする Web サーバブロック)
server {
    listen 80;
    return 301 https://$host$request_uri;
}

server {
    listen       443 ssl; # 80 から変更
    server_name  localhost;
    # 以下 2行を追加
    ssl_certificate     /etc/letsencrypt/live/www.hogehoge.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.hogehoge.com/privkey.pem;

    ...
```

#### Web サービスを再開する

```bash
systemctl start nginx # 起動コマンド
systemctl status nginx # Web 動いているか確認
```

止めていたドメインを `https://ドメイン名` でアクセスしてみましょう。表示すれば成功。
ここまでで、ひとまず HTTPS の動作するための作業は完了。

### 4. SSL サーバ証明書の自動更新を設定する

SSL 証明書の有効期限は 3ヶ月なので、自動で更新するようにしておく必要がある。

#### 証明書取得の設定ファイルを webroot モードに書き換える

```bash:/usr/local/certbot
# ドキュメントルートを事前に作成
mkdir /var/www/hogehoge
# 所有権を Nginx に変更
chown -R nginx:nginx /var/www/hogehoge
# ドキュメントルートを指定して、設定ファイルを書き換え実施
./certbot-auto certonly --webroot -w /var/www/hogehoge -d www.hogehoge.com --agree-tos --force-renewal -n
```

またスタンドアローン時と同様、「`Congratulations!`」とか出れば成功。

#### ドキュメントルートを追記

Nginx の設定ファイルにドキュメントルート `root /var/www/hogehoge;` を追記。
listen ポートと server_name の下あたりで良い。

#### Web サーバ再起動

```bash
systemctl restart nginx
```

#### ひとまず、証明書の更新コマンドが効くか確認

```bash:/usr/local/certbot/
./certbot-auto renew
```

以下のように「まだ古くないから更新する必要ないよ」みたいなメッセージが表示されればOK.

```bash
Requesting to rerun ./certbot-auto with root privileges...
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/www.hogehoge.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

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

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/www.hogehoge.com/fullchain.pem expires on 2019-03-21 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```

#### 更新処理をバッチ化しておく

更新コマンドが問題なく動くなら、勝手に定期更新するように cron に以下の通りに設定しておく

##### cron の実行されるファイルを開く

```bash
vi /etc/crontab
```

`crontab -e` を使用して編集するのは避けておいた方が良い。（理由は、`E` の隣のキー `R` をタイプミスしやすく、 `-r` オプションを万が一でも実行してしまった場合、無条件で cron 設定を削除してしまうため）

##### 実行内容を記載する

以下の内容を、コメントを含めて記載しておく

```bash
# 毎月1日の4:00に SSL サーバ証明書を更新し、その後、Nginxをリロードする
0 4 1 * * /usr/local/certbot/certbot-auto renew && /bin/systemctl reload nginx
```

これで証明書の自動更新対応は完了。

### 参考：おまけ

cerbot の設定ファイルは、それぞれのモードの時に覗くと、以下の様になっている

- `./certbot-auto certonly --standalone` 時の設定ファイル

```bash:/etc/letsencrypt/renewal/ia-dev-square.com.conf
# renew_before_expiry = 30 days
version = 0.29.1
archive_dir = /etc/letsencrypt/archive/ia-dev-square.com
cert = /etc/letsencrypt/live/ia-dev-square.com/cert.pem
privkey = /etc/letsencrypt/live/ia-dev-square.com/privkey.pem
chain = /etc/letsencrypt/live/ia-dev-square.com/chain.pem
fullchain = /etc/letsencrypt/live/ia-dev-square.com/fullchain.pem

# Options used in the renewal process
[renewalparams]
authenticator = standalone
account = 0123a456bc789d011121e3fg1h4k1516
server = https://acme-v02.api.letsencrypt.org/directory
```

- `./certbot-auto certonly --webroot` に変更後の設定ファイル

```bash
# renew_before_expiry = 30 days
version = 0.29.1
archive_dir = /etc/letsencrypt/archive/ia-dev-square.com
cert = /etc/letsencrypt/live/ia-dev-square.com/cert.pem
privkey = /etc/letsencrypt/live/ia-dev-square.com/privkey.pem
chain = /etc/letsencrypt/live/ia-dev-square.com/chain.pem
fullchain = /etc/letsencrypt/live/ia-dev-square.com/fullchain.pem

# Options used in the renewal process
[renewalparams]
authenticator = webroot
account = 0123a456bc789d011121e3fg1h4k1516
server = https://acme-v02.api.letsencrypt.org/directory
webroot_path = /var/www/hogehoge,
[[webroot_map]]
www.hogehoge.com = /var/www/hogehoge
```


### 参考：「セキュリティ証明書の有効期限が●日前に切れています。」とブラウザで表示された場合

自動更新がうまくいっていないため、手動で更新しましょう。
まず、`su` コマンドで、管理者ユーザにしておく。

#### 認証取得

```
/usr/local/certbot/certbot-auto renew
```

#### Nginx更新

```
systemctl reload nginx
```