Heroku SSL + Let's Encrypt で、ワイルドカードなSSL証明書を設定する

## はじめに

- Heroku で、Let's EncryptのSSL証明書をACM(Automated Certificate Management)で導入できますが、 **ワイルドカードに対応していません** 。 なので、自分で設定する必要があります。
- この記事では、自動更新に関する内容を含みません。ごめんなさい・・・また別の記事で書きます。
- ワイルドカードのSSL証明書を作成する部分も含みません。
    - :triangular_flag_on_post: [Let's Encrypt ワイルドカード証明書の取得手順メモ](https://blog.apar.jp/web/9619/)の記事が参考になります！！
- サンプルコード内で、Herokuアプリ名は、 `sushi` です。

## 本題

Heroku を利用していて、ワイルドカードSSLを無料で入れたくて調べた内容をメモ的に記載します。手順としては、以下２点です。

1. ドメインを追加する（独自ドメイン）
2. SSL証明書をアップロードする

### ドメインを追加する

<https://devcenter.heroku.com/articles/custom-domains>

```sh
$ heroku domains:add *.example.com --app sushi
Added *.example.com to sushi... done
...
```

ちなみに、[Heroku SSL](https://devcenter.heroku.com/articles/ssl)を利用してます。[Heroku SSL](https://devcenter.heroku.com/articles/ssl) と、[SSL Endpoint](https://devcenter.heroku.com/articles/ssl-endpoint)は別物なので、気をつけてください。今回は、Heroku SSLを利用します。

SSL Endpointを利用した場合には、エンドポイントを指定してあげる必要があります。今や、ほとんど使う機会はないと思います。
<https://devcenter.heroku.com/articles/ssl-endpoint#endpoint-details>

### 証明書の準備

- 証明書＋中間CA証明書
`/etc/letsencrypt/live/＜ドメイン名 (example.com)＞/fullchain.pem`

- 秘密鍵
`/etc/letsencrypt/live/＜ドメイン名 (example.com)＞/privkey.pem`

を使います。

`certbot` を利用して、Let's Encrypt のSSL証明書を作る方法は、こちらのサイトが丁寧でわかりやすかったです。[Let's Encrypt ワイルドカード証明書の取得手順メモ](https://blog.apar.jp/web/9619/)

### Heroku SSLに証明書をuploadする

```sh
$ sudo heroku certs:update /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/example.com/privkey.pem --app sushi
Resolving trust chain... done
 ▸    Potentially Destructive Action
 ▸    This command will change the certificate of endpoint example-1234 from ⬢ sushi.
 ▸    To proceed, type sushi or re-run this command with --confirm shushi

> sushi
Updating SSL certificate example-1234 for ⬢ sushi... done
```

*Herokuコマンドの復習*

- 証明書の追加 `certs:add`
`heroku certs:add [証明書＋中間CA証明書のファイルパス] [秘密鍵のファイルパス] --app [アプリ名]`

- 証明書の更新 `certs:update`
`heroku certs:update [証明書＋中間CA証明書のファイルパス] [秘密鍵のファイルパス] --app [アプリ名]`

### 最後に適応されているのかを確認

```sh
$ curl -vI https://www.example.com
* About to connect() to www.example.com port 443 (#0)
*   Trying 50.16.234.21... connected
* Connected to www.example.com (50.16.234.21) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
*    subject: C=US; ST=CA; L=SF; O=SFDC; OU=Heroku; CN=www.example.com
*    start date: 2011-11-01 17:18:11 GMT
*    expire date: 2012-10-31 17:18:11 GMT
*    common name: www.example.com (matched)
*    issuer: C=US; ST=CA; L=SF; O=SFDC; OU=Heroku; CN=www.heroku.com
*    SSL certificate verify ok.
GET / HTTP/1.1
User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3
Host: www.example.com
Accept: */*
```

`SSL certificate verify ok` と記載されていれば、成功です。

*curlコマンドのおさらい*

- HTTPレスポンスヘッダーの取得（`-I`）
`$ curl -I http://www.example.com/`

- 詳細をログ出力（`-v`もしくは`--verbose`）
`$ curl -v http://www.example.com/`

##### ミスった場合には、Rollbackが用意されています。
<https://devcenter.heroku.com/articles/ssl-endpoint#undo>

```
heroku certs:rollback --app sushi
```

## ちなみに

### Let's Encrypt ワイルドカード証明書の取得手順メモ

:triangular_flag_on_post: [Let's Encrypt ワイルドカード証明書の取得手順メモ](https://blog.apar.jp/web/9619/)の記事が参考になります！！
certbot コマンド実行（SSL証明書作成）

```sh
$ certbot certonly --manual \
--server https://acme-v02.api.letsencrypt.org/directory \
--preferred-challenges dns \
-d "*.example.com" -d example.com \
-m admin@example.com \
--agree-tos \
--manual-public-ip-logging-ok
```

そうすると、以下のようにDNSのTXTレコードを修正してねって言われるので言われたように修正する

```
Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:

R31KSgjQrbga7AH5_H6KdJLjD6-5JNp3KEIRVEotSw0

Before continuing, verify the record is deployed.
```

TTLは少なく設定したほうが良いと思います。60とか。

### DIGでTXTレコードが反映されたかどうかを確認する

```sh
$ dig -t TXT _acme-challenge.example.com

#=>

; <<>> DiG 9.10.6 <<>> -t TXT _acme-challenge.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14090
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;_acme-challenge.example.com.	IN	TXT

;; ANSWER SECTION:
_acme-challenge.example.com.	37 IN	TXT	"R31KSgjQrbga7AH5_H6KdJLjD6-5JNp3KEIRVEotSw0"

;; Query time: 4 msec
;; SERVER: 61.122.116.147#53(61.122.116.147)
;; WHEN: Mon Sep 14 10:33:04 JST 2020
;; MSG SIZE  rcvd: 115
```