nginx と acme-client の組合せで SNI 証明書を複数発行し運用する方法

前程として、対象鯖は dns で正引きが出来る事。
今回は例題として以下のホスト名を用いる。
example.jp
example.com
example.org
xn--w22as22a.example.jp
以上４ホスト名
OS は FreeBSD 11.2-RELEASE を前提として記述しているが、ディレクトリや軽微なコマンドを読み替える事で他の unix 系 OS 等にも対応できると思われる。
また本記事は FreeBSD 経験者を対象に書いているので、事細かい配慮はされていません。

#www/nginx 導入
web server の nginx をインスコして OS 再起動後でも自動起動の設定をする

```sh
cd /usr/ports/www/nginx
make install
sysrc nginx_enable="YES"
```

#nginx の設定
```conf:/usr/local/etc/nginx/nginx.conf
http {
    server {
        listen  [::]:80;
        listen  80;
        server_name  example.jp;
        location ^~ /.well-known/acme-challenge/ {
            alias /usr/local/www/acme/;
        }
        location / {
            return  301 https://$host$request_uri;
        }
    }
    server {
        listen  [::]:80;
        listen  80;
        server_name  example.com;
        location ^~ /.well-known/acme-challenge/ {
            alias /usr/local/www/acme/;
        }
        location / {
            return  301 https://$host$request_uri;
        }
    }
    server {
        listen  [::]:80;
        listen  80;
        server_name  example.org;
        location ^~ /.well-known/acme-challenge/ {
            alias /usr/local/www/acme/;
        }
        location / {
            return  301 https://$host$request_uri;
        }
    }
    server {
        listen  [::]:80;
        listen  80;
        server_name  xn--w22as22a.example.jp;
        location ^~ /.well-known/acme-challenge/ {
            alias /usr/local/www/acme/;
        }
        location / {
            return  301 https://$host$request_uri;
        }
    }
}
```

#nginx の設定確認
```sh
nginx -t
```
ここで「test is successful」が表示しなければ nginx.conf の記述ミスか、個別環境の差異を吸収し切れていないので、再度確認する。

#nginx 起動
```sh
service nginx start
```

#acme-client の設定下準備
```sh
touch /usr/local/www/acme/hogehoge
fetch -qo- http://example.jp/.well-known/acme-challenge/hogehoge
```
ここでエラーが出れば、dns 含めた記述ミスなので、再度確認。
問題が無ければ他のホスト名も確認しておく。

```sh
fetch -qo- http://example.jp/.well-known/acme-challenge/hogehoge
fetch -qo- http://example.com/.well-known/acme-challenge/hogehoge
fetch -qo- http://example.org/.well-known/acme-challenge/hogehoge
fetch -qo- http://xn--w22as22a.example.jp/.well-known/acme-challenge/hogehoge
```
エラーが無ければ、テストファイルを削除しておく。

```sh
unlink /usr/local/www/acme/hogehoge
```

#security/acme-client 導入
```sh
cd /usr/ports/security/acme-client
make install
```

#acme-client の設定
```conf:/etc/periodic.conf
###	security/acme-client
weekly_acme_client_enable="YES"
weekly_acme_client_domains="example.jp example.com example.org xn--w22as22a.example.jp"
```

#証明書の発行手続き
```sh
acme-client -vnN example.jp example.com example.org xn--w22as22a.example.jp
```
実行すると以下のようなログが流れていく

```log:log
acme-client: /usr/local/etc/acme/privkey.pem: account key exists (not creating)
acme-client: /usr/local/etc/ssl/acme/private/privkey.pem: domain key exists (not creating)
acme-client: adding SAN: example.com
acme-client: adding SAN: example.org
acme-client: adding SAN: xn--w22as22a.example.jp
acme-client: https://acme-v01.api.letsencrypt.org/directory: directories
acme-client: acme-v01.api.letsencrypt.org: DNS: 2600:140b:1:181::3a8e
acme-client: acme-v01.api.letsencrypt.org: DNS: 2600:140b:1:185::3a8e
acme-client: acme-v01.api.letsencrypt.org: DNS: 23.42.210.228
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: example.jp
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: example.com
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: example.org
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-authz: req-auth: xn--w22as22a.example.jp
acme-client: /usr/local/www/acme/HhYYlbiFlhOtlp9x1aBPgZzURFWLObWBrc72k6SAxsU: created
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/uW0ibZCGOH8TJzRmi6mkQwQ-WB5svZRW4omby4-kteE/5874344542: challenge
acme-client: /usr/local/www/acme/ldhbr5pVkPJcJd5vqWDzn8d8XA5T_yAjT6bmbCgxsCw: created
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/kwiPqOzuQkZyGDIqJYH9hAJbQuyvzaUKOaJXafQ2-mw/6213869611: challenge
acme-client: /usr/local/www/acme/UDsdFCp0mIMsxu_uD8qwXxhJx9ePK_o19Z6P7zR2o7Y: created
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/jIjdHQQxItemccxAVpRDQV4nfThYdlGHwjN5Yb3-hNM/6213910989: challenge
acme-client: /usr/local/www/acme/m2OoOOj_068FX5ADOuceiFmP-XMywS9Mf5_tCSq52dU: created
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/Cynvst15YKcEe1melPpmykZd97TBHRpMAUV42eiW9k8/6213911979: challenge
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/uW0ibZCGOH8TJzRmi6mkQwQ-WB5svZRW4omby4-kteE/5874344542: status
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/kwiPqOzuQkZyGDIqJYH9hAJbQuyvzaUKOaJXafQ2-mw/6213869611: status
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/jIjdHQQxItemccxAVpRDQV4nfThYdlGHwjN5Yb3-hNM/6213910989: status
acme-client: https://acme-v01.api.letsencrypt.org/acme/challenge/Cynvst15YKcEe1melPpmykZd97TBHRpMAUV42eiW9k8/6213911979: status
acme-client: https://acme-v01.api.letsencrypt.org/acme/new-cert: certificate
acme-client: http://cert.int-x3.letsencrypt.org/: full chain
acme-client: cert.int-x3.letsencrypt.org: DNS: 2600:140b:1:18b::ce0
acme-client: cert.int-x3.letsencrypt.org: DNS: 2600:140b:1:193::ce0
acme-client: cert.int-x3.letsencrypt.org: DNS: 23.42.221.179
acme-client: /usr/local/etc/ssl/acme/chain.pem: created
acme-client: /usr/local/etc/ssl/acme/cert.pem: created
acme-client: /usr/local/etc/ssl/acme/fullchain.pem: created
```

```log:/var/log/nginx/access.log
2600:3000:1511:200::1d - - [14/Nov/2018:13:25:04 +0900] "GET /.well-known/acme-challenge/HhYYlbiFlhOtlp9x1aBPgZzURFWLObWBrc72k6SAxsU HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
2600:3000:1511:200::1d - - [14/Nov/2018:13:25:07 +0900] "GET /.well-known/acme-challenge/ldhbr5pVkPJcJd5vqWDzn8d8XA5T_yAjT6bmbCgxsCw HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
2600:3000:1511:200::1d - - [14/Nov/2018:13:25:09 +0900] "GET /.well-known/acme-challenge/UDsdFCp0mIMsxu_uD8qwXxhJx9ePK_o19Z6P7zR2o7Y HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
2600:3000:1511:200::1d - - [14/Nov/2018:13:25:12 +0900] "GET /.well-known/acme-challenge/m2OoOOj_068FX5ADOuceiFmP-XMywS9Mf5_tCSq52dU HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
```

ここで、エラーになると厄介。
ひとまず

```text
/usr/local/etc/ssl/acme
```
このディレクトリを一旦移動させて、再度 acme-client コマンドを叩くのが楽であるが、短期間に繰り返し実行すると、*再認証制限*に*引っかかる*ので*要注意*。（３０回／日）
丸々最初から証明書を作り直すと幸せになれます。


#nginx の SSL 設定
冒頭の設定ファイル**最後の一行**との間に追記する事。
*インデントに注意！*

```conf:/usr/local/etc/nginx/nginx.conf
    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;

        server_name  example.jp;
        access_log   /var/log/nginx/access_SSL_example.jp.log;
        error_log    /var/log/nginx/error_SSL_example.jp.log;

        ssl_certificate         /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_certificate_key     /usr/local/etc/ssl/acme/private/privkey.pem;
        ssl_session_timeout     5m;
        ssl_session_cache       shared:SSL:1m;
        ssl_session_tickets     on;
        ssl_dhparam             /etc/mail/certs/dh.param;
        ssl_protocols           TLSv1.2;
        ssl_ciphers ECDHE:ECDSA:!RC4:!NULL:!3DES:!CBC;
        ssl_prefer_server_ciphers on;
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
        ssl_stapling            on;
        ssl_stapling_verify     on;
        ssl_trusted_certificate /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_session_ticket_key /usr/local/etc/ssl/ticket.key;

        location / {
            root   /usr/local/www/example.jp;
        }
    }
    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;

        server_name  example.com;
        access_log   /var/log/nginx/access_SSL_example.com.log;
        error_log    /var/log/nginx/error_SSL_example.com.log;

        ssl_certificate         /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_certificate_key     /usr/local/etc/ssl/acme/private/privkey.pem;
        ssl_session_timeout     5m;
        ssl_session_cache       shared:SSL:1m;
        ssl_session_tickets     on;
        ssl_dhparam             /etc/mail/certs/dh.param;
        ssl_protocols           TLSv1.2;
        ssl_ciphers ECDHE:ECDSA:!RC4:!NULL:!3DES:!CBC;
        ssl_prefer_server_ciphers on;
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
        ssl_stapling            on;
        ssl_stapling_verify     on;
        ssl_trusted_certificate /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_session_ticket_key /usr/local/etc/ssl/ticket.key;

        location / {
            root   /usr/local/www/example.com;
        }
    }
    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;

        server_name  example.org;
        access_log   /var/log/nginx/access_SSL_example.org.log;
        error_log    /var/log/nginx/error_SSL_example.org.log;

        ssl_certificate         /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_certificate_key     /usr/local/etc/ssl/acme/private/privkey.pem;
        ssl_session_timeout     5m;
        ssl_session_cache       shared:SSL:1m;
        ssl_session_tickets     on;
        ssl_dhparam             /etc/mail/certs/dh.param;
        ssl_protocols           TLSv1.2;
        ssl_ciphers ECDHE:ECDSA:!RC4:!NULL:!3DES:!CBC;
        ssl_prefer_server_ciphers on;
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
        ssl_stapling            on;
        ssl_stapling_verify     on;
        ssl_trusted_certificate /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_session_ticket_key /usr/local/etc/ssl/ticket.key;

        location / {
            root   /usr/local/www/example.org;
        }
    }
    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;

        server_name  xn--w22as22a.example.jp;
        access_log   /var/log/nginx/access_SSL_xn--w22as22a.example.jp.log;
        error_log    /var/log/nginx/error_SSL_xn--w22as22a.example.jp.log;

        ssl_certificate         /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_certificate_key     /usr/local/etc/ssl/acme/private/privkey.pem;
        ssl_session_timeout     5m;
        ssl_session_cache       shared:SSL:1m;
        ssl_session_tickets     on;
        ssl_dhparam             /etc/mail/certs/dh.param;
        ssl_protocols           TLSv1.2;
        ssl_ciphers ECDHE:ECDSA:!RC4:!NULL:!3DES:!CBC;
        ssl_prefer_server_ciphers on;
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
        ssl_stapling            on;
        ssl_stapling_verify     on;
        ssl_trusted_certificate /usr/local/etc/ssl/acme/fullchain.pem;
        ssl_session_ticket_key /usr/local/etc/ssl/ticket.key;

        location / {
            root   /usr/local/www/xn--w22as22a.example.jp;
        }
    }
```

#/etc/mail/certs/dh.param ファイルの作成
nginx の SSL 対応設定ファイルの中で ssl_dhparam で必要とされるファイルを作成しておく

```sh
openssl dhparam 3072 -out /etc/mail/certs/dh.param
```
<ぼそ>ディレクトリの配置場所は /usr/local/etc/ssl 辺りが好ましい</ぼそ>

#nginx 設定ファイル再読み込み
```sh
nginx -k reload
```

おしまい😄