AWS ELBに組み込んだLet’s Encryptをダウンタイムゼロで更新する


## 0. 背景

無料のSSL証明書ということで話題のLet's Encryptですが、実際の利用のためには **90日ごと** に証明書を更新しないといけないという制約から、なかなか本番利用するには腰が重くなってしまいます。
（もちろん本家では、**全自動**で更新することを推奨しています。）

ましてや、`AWS ELB`などのロードバランサーを利用し、配下に**複数の**Webサーバがいると、`ACMEプロトコル`を通過させるだけで一苦労です。

そこで、nginxのリバースプロキシを使うことで、既存のアプリケーションに影響をあたえること無く、Let's Encryptの更新を済ませる方法を試しました。


## 1. 前提

今回は、以下の様な環境を想定します。

- `www.example.com` で受けるELBの配下に**複数の**Webサーバがある
- `stg.example.com` で受けるELBの配下にWebサーバがある
- `www.example.com` と `stg.example.com` に利用する証明書は1つにまとまっていて良い

![](https://s3-ap-northeast-1.amazonaws.com/tady-monosnap/Lets_Encrypt_-_draw.io_2016-03-23_02-11-36.png)


## 2. letsencrypt実行サーバの用意

Amazon Linuxからの`letsencrypt`の実行はまだ不安定で、本番利用のサーバに不要なライブラリをインストールしたくはないので、あたらしく`letsencrypt`実行専用のサーバを用意します。

今回は、CentOS7を利用しました。

![](https://s3-ap-northeast-1.amazonaws.com/tady-monosnap/EC2_Management_Console_2016-03-23_02-16-24.png_1260883_2016-03-24_00-49-51.png)

サーバ起動後に必要な依存関係は `git` のみです。
その後、`letsencrypt`をインストールします。
また、後述のリバースプロキシのために、`firewalld`を切っておきます。
（`firewalld`を閉じる前に、セキュリティグループが閉じていることを確認してください）

```shell-session:shell
# yum install git
# systemctl stop firewalld.service

# cd /usr/local/src
# git clone https://github.com/letsencrypt/letsencrypt.git
# cd letsencrypt
```

これでサーバの準備は完了です。
Route 53から、`letsencrypt.example.com`というドメインがこのサーバを指すようにレコード指定しておきましょう。

このまま、以下のコマンドを実行すると`Let's Ecnrypt`から証明書を取得しますが、指定したすべてのドメインへのリクエスト(`ACMEプロトコル`)が自分のサーバに届かないといけないので、失敗します。



## 3. リバースプロキシの設定

`www.example.com`と`stg.example.com`へのアクセスが`letsencrypt実行サーバ`へ迂回するように、それぞれのサーバで動いている`nginx`のリバースプロキシ設定を行います。

Let's EncryptのACMEプロトコルは `/.well-known/...` へアクセスしてくるので、このディレクトリだけプロキシすれば十分です。

この設定のおかげで、本番環境への影響がなくなります。

```nginx:/etc/nginx/conf.d/app.conf
server {
  listen 80;
  server_name www.example.com;

  ...

  # lets encrypt ACME proxy
  location /.well-known {
    proxy_pass http://letsencrypt.example.private;
  }
}
```

ここで、リバースプロキシ先のホストを`letsencrypt.example.private`と置くことで、次回更新時に同じ作業をスキップすることができます。

その後は、Route 53で`letsencrypt.example.private`のPrivate DNSを設定しましょう。

![](https://s3-ap-northeast-1.amazonaws.com/tady-monosnap/Route_53_Management_Console_2016-03-23_02-32-22.png)



## 4. letsencryptの実行

`letsencrypt実行サーバ`に戻って`letsencryptコマンド`を実行しましょう。

```shell-session:shell
# pwd 
/usr/local/src/letsencrypt

# ./letsencrypt-auto certonly --standalone -d www.example.com -d stg.example.com -d letsencrypt.example.com
```

上記を実行すると、以下のディレクトリに証明書が生成されます。

```shell-session:shell
# cd /etc/letsencrypt/live/letsencrypt.example.com
# ls
cert.pem  chain.pem  fullchain.pem  privkey.pem
```

`privkey.pem`を`プライベートキー`、`fullchain.pem`を`パブリックキー証明書` と読み替えて、ELBのSSL証明書を設定します。

![](https://s3-ap-northeast-1.amazonaws.com/tady-monosnap/EC2_Management_Console_2016-03-23_00-57-22.png)

以上で、ダウンタイムなしのELBの証明書更新が完了します。

今回は試していませんが、`AWS CLI`を利用することで、全自動にすることも可能です。

## 5. まとめ

無料で使えるSSL証明書とはいうものの、その設定にはまだ枯れていない部分があります。
Tokyoリージョンでの`AWS Certificate Manager`が利用できるまではこの記事が役に立つのではないでしょうか？


## 6. 参考URL

- [Let's Encrypt](https://letsencrypt.org/)
- [Let's Encrypt を支える ACME プロトコル](http://jxck.hatenablog.com/entry/letsencrypt-acme)
- [Let's Encrypt で手軽に HTTPS サーバを設定する](http://qiita.com/ww24/items/9fa19594b4e3a8eb9b6f)
- [Let's Encrypt 証明書の自動発行とELB自動登録を行ったログ](http://qiita.com/hidekuro/items/482520f220a305dc147b)
- [大量のELBのSSL証明書をAWS CLIで簡単に更新する](http://dev.classmethod.jp/cloud/aws/elb-ssl-certificate-renew-from-cli/)
- [Will Let’s Encrypt work for me? (Multiple servers serving one domain)](https://community.letsencrypt.org/t/will-lets-encrypt-work-for-me-multiple-servers-serving-one-domain/6830/8)