certbotでワイルドカード証明書を作る環境構築

やったときのログです。

# サーバ環境
- AWS EC2
- OSは CentOS(6.8)
  - 都合により、ちと古いバージョン。7系でも内容はさほど変わりないかと
- Route53を利用
- certbotのroute53用プラグインを使う

# サーバ設定
## python3, virtualenv をインストール
CentOS(6.8)に標準で入ってるpythonが2.6.6で、これだと pip を最新にしたときにサポート外で動かなくなるので、python3をインストールする。
そしてシステムのpython環境をいじらないよう、virtualenvをインストールしてそれ経由で使用する。

```
$ sudo yum install python-virtualenv
$ sudo yum install python34
$ python3 -V
Python 3.4.5
$ which python3
/usr/bin/python3
```

ホームディレクトリに移動して、virtualenvな環境を作る。

```
$ cd
$ virtualenv -p /usr/bin/python3 python3
Running virtualenv with interpreter /usr/bin/python3
Using base prefix '/usr'
New python executable in python3/bin/python3
Also creating executable in python3/bin/python
Installing setuptools, pip...done.

# virtualenvの環境に入る
$ . python3/bin/activate
(python3)[web@ip-10-0-0-82 virtualenv]$ pip -V
pip 6.0.8 from /var/www/virtualenv/python3/lib/python3.4/site-packages (python 3.4)

$ pip list
The directory '/var/www/.cache/pip/log' or its parent directory is not owned by the current user and the debug log has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want the -H flag.
The directory '/var/www/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want the -H flag.
You are using pip version 6.0.8, however version 10.0.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

# .cache ディレクトリ作りなさいとのことなので、作っておく
$ sudo mkdir .cache
$ chown web:web .cache

$ pip list
You are using pip version 6.0.8, however version 10.0.0 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
pip (6.0.8)
setuptools (12.0.5)

# virtualenv環境を離れるときは
$ deactivate
```

pipで pip自体とsetuptoolsを最新にする。
そのうえで [certbot-dns-route53](https://github.com/certbot/certbot/tree/master/certbot-dns-route53) をinstallする。

```
$ pip install --upgrade pip
$ pip install --upgrade setuptools

(python3)[web@ip-10-0-0-82 ~]$ pip list
Package    Version
---------- -------
pip        10.0.0
setuptools 39.0.1

$ pip install certbot-dns-route53

$ pip list
Package             Version
------------------- ---------
acme                0.23.0
asn1crypto          0.24.0
boto3               1.7.4
botocore            1.10.4
certbot             0.23.0
certbot-dns-route53 0.23.0
certifi             2018.4.16
cffi                1.11.5
chardet             3.0.4
ConfigArgParse      0.13.0
configobj           5.0.6
cryptography        2.2.2
docutils            0.14
future              0.16.0
idna                2.6
jmespath            0.9.3
josepy              1.1.0
mock                2.0.0
parsedatetime       2.4
pbr                 4.0.2
pip                 10.0.0
pycparser           2.18
pyOpenSSL           17.5.0
pyRFC3339           1.0
python-dateutil     2.6.1
pytz                2018.4
requests            2.18.4
s3transfer          0.1.13
setuptools          39.0.1
six                 1.11.0
urllib3             1.22
zope.component      4.4.1
zope.event          4.3.0
zope.interface      4.4.3
```

## rootになった状態でvirtualenv環境に入って、 certbot を実行してみる
`certbot certificates` を実行して、正常に動くことを確認。

このコマンドでは生成した証明書の一覧を表示するが、まだ生成してないので何もない旨が表示される。
/etc/letsencrypt や /var/log/letsencrypt にディレクトリが作成されているはず。

```
[web@ip-10-0-0-82 ~]$ sudo -s
[root@ip-10-0-0-82 www]# cd virtualenv/
[root@ip-10-0-0-82 virtualenv]# . python3/bin/activate
(python3)[root@ip-10-0-0-82 virtualenv]# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log
...
```

## AWSでroute53を参照・更新するポリシーを作る
https://certbot-dns-route53.readthedocs.io/en/latest/ を参考に
ZONE IDのところを調べて書き換えて、設定。

AWSのIAMでグループ certbot を作って、必要なポリシーのみ持たせた。

```
                "route53:ListHostedZones",
                "route53:GetChange"
```

ユーザー certbot を作って、そのグループに所属させた。
アクセスキーとsecretを控えておく。
これをcertbotのroute53プラグインで使うので、
/root/.aws/config にファイル追加。


## 証明書の発行
最初の作成を行ったときの様子。
route53とのやりとりをするときに、何分か時間がかかる。

ドメイン名は仮に `example.work` とした

```
(python3)[root@ip-10-0-0-82 virtualenv]# certbot certonly --dns-route53 -d *.example.work -d example.work --server https://acme-v02.api.letsencrypt.org/directory
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Credentials found in config file: ~/.aws/config
Plugins selected: Authenticator dns-route53, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): hoge@example.work

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: Y
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for example.work
dns-01 challenge for example.work
Starting new HTTPS connection (1): route53.amazonaws.com
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
Resetting dropped connection: route53.amazonaws.com

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.work/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.work/privkey.pem
   Your cert will expire on 2018-07-18. 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"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - 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
```
できた証明書の確認

```
(python3)[root@ip-10-0-0-82 ~]# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Found the following certs:
  Certificate Name: example.work
    Domains: *.example.work example.work
    Expiry Date: 2018-07-18 04:05:07+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/example.work/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.work/privkey.pem
-------------------------------------------------------------------------------
```

できた証明書のファイルを、nginxなどで設定して利用する。


## 発行が無事にできたら、更新を試みる
まずは `certbot renew --dry-run` で試す。

```
(python3)[root@ip-10-0-0-82 ~]# certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.work.conf
-------------------------------------------------------------------------------
Cert not due for renewal, but simulating renewal for dry run
Credentials found in config file: ~/.aws/config
Plugins selected: Authenticator dns-route53, Installer None
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for example.work
dns-01 challenge for example.work
Starting new HTTPS connection (1): route53.amazonaws.com
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
Resetting dropped connection: route53.amazonaws.com

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/example.work/fullchain.pem
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/example.work/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
-------------------------------------------------------------------------------

IMPORTANT NOTES:
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
```

無事に完走した。

--dry-run を外して再び試すと、有効期限までまだ余裕あるから更新は行われず終了する。

```
(python3)[root@ip-10-0-0-82 ~]# certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.work.conf
-------------------------------------------------------------------------------
Cert not yet due for renewal

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

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/example.work/fullchain.pem expires on 2018-07-18 (skipped)
No renewals were attempted.
-------------------------------------------------------------------------------
```

--force-renew をつけると強制的に更新できる。
ので、実行。(短期間で何度もやると、制限を受けてしまう可能性があるので注意)

```
# certbot renew --force-renew

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

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.work.conf
-------------------------------------------------------------------------------
Credentials found in config file: ~/.aws/config
Plugins selected: Authenticator dns-route53, Installer None
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for example.work
dns-01 challenge for example.work
Starting new HTTPS connection (1): route53.amazonaws.com
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
Resetting dropped connection: route53.amazonaws.com

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/example.work/fullchain.pem
-------------------------------------------------------------------------------

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

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/example.work/fullchain.pem (success)
-------------------------------------------------------------------------------
```

## cronで1日おきに更新処理を動かす
前述の通り、有効期限まで余裕がある場合(defaultでは30日以上)は更新処理がスキップされる。
ひとまず1日おきに実行させてみる。

### cronで動かすscript
[virtualenv環境のpythonをcronで呼ぶ - pLog](http://pman0214.github.io/blog/cron-python-virtualenv.html) 
このへん参考にして、cron実行用に以下のscriptを置いた。
/root/certbot.sh

```
#!/bin/sh

PROG_DIR=/var/www/virtualenv/python3
source $PROG_DIR/bin/activate

certbot renew --post-hook '/etc/init.d/nginx reload' >> /var/log/letsencrypt/cron.log 2>&1
```

--post-hook は、更新が行われたあとに実行されるフック。ここでは証明書の更新を反映させたいので、nginxの再起動を行うよう指定している。

### crontabで以下を追加
AM2:30に実行される。
>30 2 * * * /bin/sh /root/certbot.sh

### logrotate でcron.logをローテート
/etc/logrotate.d/letsencrypt

```
/var/log/letsencrypt/cron.log {
    rotate 7
    daily
    missingok
    notifempty
    sharedscripts
    copytruncate
    compress
}
```

## nginxの設定
/opt/nginx/conf/nginx.conf で、証明書のパスを指定する箇所を変更する。

```
    # ssl(2018/04-)
    ssl_certificate      /etc/letsencrypt/live/example.work/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/example.work/privkey.pem;
```

## チェックサイトで確認
https://www.ssllabs.com/ssltest/
でSSL証明書の状態をチェックできる。

以下のサイトなど見ると、設定を変えればさらに良い評価にもできる模様。
[Webサーバー nginx における SSL証明書設定の安全性向上 ～SSL Server Test で A\+ 判定を目指して～ \| SaintSouth\.NET](https://www.saintsouth.net/blog/safety-of-ssl-certificate-setting-improvements-in-web-server-nginx-to-get-rankaplus-from-ssl-server-test/)

# 参考
certbot についての解説はここが良くまとまっている
https://free-ssl.jp/docs/using.html

wildcard対応などはこのへんを参考にした
https://marvelworks.org/archives/233

virtualenvまわり
https://dev.classmethod.jp/server-side/language/python-virtualenv-tutorial/