Dockerコンテナで起動するnginxをLets encryptを利用してHTTPS化する


# 修正点
6/28 再度試したところ、letsencryptで証明書を取得するところの選択肢1,2が逆転していました・・・十分注意して確認の上操作していただければと思います。
3/14 letsencryptの証明書を取得するときの引数が一つ抜けていたので修正。大変失礼致しました。

# はじめに

[前回](http://qiita.com/marufeuille/items/91c68e2bafb8a4c1d889)の続きでnginxコンテナをhttps化対応させます。
証明書はみんな大好きLet's encryptを利用します。

# 構成
## 構成の検討
- はじめはローカルにクライアントソフトを入れて、とか考えてたんですが、DockerイメージでLet's encryptの証明書を取得できる物があるようなので、それを利用する事を考えます。
- let's encryptで証明書を取得する時、そのホストが該当するDNS名でアクセスできる必要があります。
- アクセスするポートは80番か、443番でいいようなので、80番をlet's encrypt用、443をnginx用となるように構成を変更します。

## 構成イメージ

![Kobito.MhEsn0.png](https://qiita-image-store.s3.amazonaws.com/0/99704/1cd55c58-15cb-9ba0-dde1-b142e26fe0d2.png "Kobito.MhEsn0.png")


# 設計
## フォルダ構造

|項目|値|備考|
|:---|:--:|:---|
|コンフィグの置き場所|$(pwd)/nginx/etc/nginx/|以下にconf.dをフォルダを用意しておき、マウントさせます。|
|証明書の置き場所|/etc/letsencrypt|このディレクトリ以下に取得した証明書を配置します。|
|コンテンツの置き場所|$(pwd)/www|コンテンツはカレントディレクトリ直下のwwwディレクトリに配置します。|

## nginx

- あまり難しい設定は実施せず、最低限のもののみ投入します。

|項目|値|備考|
|:---|:--:|:---|
|listen|443 ssl default_server|80番はlistenせず、443のみ待ち受けるものとします。|
|ssl_certificate|/etc/letsencrypt/live/ドメイン名/fullchain.pem|証明書のパスを指定します|
|ssl_certificate_key|/etc/letsencrypt/live/ドメイン名/privkey.pem|証明書のパスを指定します。|
|server_name|ドメイン名|指定したドメイン名で待ち受けるようにします。|
|root|/usr/share/nginx/html|コンテナ内のドキュメントルートを/usr/share/nginx/htmlとします。|
|index|index.html|インデックスファイルはindex.htmlのみとします。|

## Docker

- 同様に最低限のものとします。(前回書いた共通の項目はここには記載しません)

|項目|値|備考|
|:---|:--:|:---|
|-v|$(pwd)/nginx/etc/nginx/conf.d:/etc/nginx/conf.d:ro|conf.dディレクトリをマウントします。|
|-v|$(pwd)/www:/usr/share/nginx/html:ro|カレントディレクトリ直下にあるwwwディレクトリを/usr/share/nginx/htmlにマウントします。|
|-v|/etc/letsencrypt:/etc/letsencrypt:ro|let's encryptで取得した証明書ディレクトリをコンテナ側にマウントします。|

# 設定
## let's encrypt証明書の取得
- 一旦nginxコンテナを止めた上で、該当するDNS名が設定されているホスト上で下記を実行します。

```bash
$ docker run -it --rm -p 80:80 --name letsencrypt ¥
      -v "/etc/letsencrypt:/etc/letsencrypt" ¥
      -v "/var/lib/letsencrypt:/var/lib/letsencrypt" ¥
      quay.io/letsencrypt/letsencrypt:latest certonly --preferred-challenges http-01
```

```bash
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Failed to find apache2ctl in PATH: /opt/certbot/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Place files in webroot directory (webroot)
2: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel):対象のドメイン名を入力
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for 対象のドメイン名
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/対象のドメイン名/fullchain.pem. Your
   cert will expire on 2017-06-08. 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
```

- これでホスト側の/etc/letsencrypt配下に証明書が生成されてますね！

- ちなみに更新するときは、下記でいけるようです。(実際には期限が来ていないのでまだ更新はできていません。)

```bash
$ docker run -it --rm -p 80:80 --name letsencrypt ¥
      -v "/etc/letsencrypt:/etc/letsencrypt" ¥
      -v "/var/lib/letsencrypt:/var/lib/letsencrypt" ¥
      quay.io/letsencrypt/letsencrypt:latest renew
```

```bash
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/対象のドメイン名.conf
-------------------------------------------------------------------------------
Cert not yet due for renewal

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/対象のドメイン名/fullchain.pem (skipped)
No renewals were attempted.
```

- 自動更新のcronを仕込んでも良いのですが、初回は手動でやりたいので、一旦は仕込みません。

## nginxの設定

- /etc/nginx/conf.d/default.confは下記のような設定とします。


```nginx
server {
    listen 443 ssl default_server;
    ssl_certificate /etc/letsencrypt/live/ドメイン名/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ドメイン名/privkey.pem;

    server_name ドメイン名;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}
```

## コンテナの起動

- --logXXX系のオプションは前回のfluentdの名残なので、不要であれば消して貰えればと思います。

```bash
$ docker run --name nginx -v /etc/localtime:/etc/localtime:ro ¥
      -p 443:443 -v $(pwd)/nginx/etc/nginx/conf.d:/etc/nginx/conf.d:ro ¥
      -v $(pwd)/www:/usr/share/nginx/html:ro ¥
      -v /etc/letsencrypt:/etc/letsencrypt:ro ¥
      --log-driver=fluentd --log-opt=fluentd-address=XX.XX.XX.YY:24224 ¥
      --log-opt=tag=docker.{{.FullID}} -d nginx
```

# 最後に
- 簡単に証明書の取得ができるletsencryptは素晴らしいですね。
- 今回はDockerホストが1つだったため考慮してませんが、実際には証明書をnfs等に配置しておいて、ホストがコケたときは別のホスト上でnginxを再起動することなどを考慮したほうが良いかと思います。

# 参考
- [DockerでLet’s Encryptしよっか](http://qiita.com/sawanoboly/items/9fdde1707de5e975dd15)
- [How To Secure Nginx with Let's Encrypt on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04)