GCPのVMインスタンスにCertbotでワイルドカードSSL証明書を取得しnginx-proxyで利用する

# 背景

１つのVMインスタンス内に複数のWEBアプリケーションがデプロイされている状況で、これを docker-compose で構築しております。また、専用ドメインを取得し jwilder/nginx-proxy（Dockerイメージ）を使ってサブドメインを分けてそれぞれのアプリケーションにリクエストを転送しております。

今までは jrcs/letsencrypt-nginx-proxy-companion というDockerイメージを利用して、そのDockerコンテナ内で Letsencrypt のSSL証明書を取得していました。
しかし、各WEBアプリケーション（Dockerコンテナ、サブドメ）ごとにSSL証明書を発行していたため、Letsencryptの制限に引っかかってしまいました。

対策として、jrcs/letsencrypt-nginx-proxy-companion の利用をやめ、VMインスタンスにCertbotを導入しワイルドカードSSL証明書を取得して対応したのでそのメモ。


# 参考にした記事

* https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal&tab=wildcard
* https://certbot-dns-google.readthedocs.io/en/stable/


# Certbotインストール
サーバーは Ubuntu 18.04

```
sudo snap install core; sudo snap refresh core
sudo apt-get remove certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-google →これはGCPのDNSを使っている場合のプラグイン
```


# GCPサービスアカウント作成&アタッチ

 「IAM > サービスアカウント」にて専用のサービスアカウントを作成する、その時のロールは `dns.admin（dns管理者）` 
上記サービスアカウントをVMインスタンスにアタッチする。


# SSL証明書を発行

ワイルドカードSSL証明書を作成する
```
$ sudo certbot certonly \
   --dns-google \
   -d *.example.com

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.example.com
Waiting 60 seconds for DNS changes to propagate

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2023-07-24.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```



# nginx-proxy（Dockerコンテナ）へ反映

SSL証明書をnginx-proxyに読み込ませるため docker-compose.yml を修正

```
  nginx-proxy:
    image: jwilder/nginx-proxy
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./docker-configs/nginx-proxy/certs:/etc/nginx/certs →ここを追加
```

```
cd docker-configs/nginx-proxy/certs

Certbotで作成したワイルドカードSSL証明書をコピーし、nginx-proxyに読み込ませる
sudo cp /etc/letsencrypt/live/example.com/fullchain.pem ./example.com.crt
sudo cp /etc/letsencrypt/live/example.com/privkey.pem ./example.com.key

Dockerコンテナを再起動
docker-compose down && docker-compose up -d
```

nginx-proxyは /etc/nginx/certs 配下の <ホスト名>.crt と <ホスト名>.key を見つけると自動的に使うようになっている。
ここまででSSL証明書が反映されhttpsで通信できるはず。

# 自動更新設定

## まずはSSL証明書更新のシュミレーション

```
# 自動更新シュミレーション（--dry-runは実際には更新しないシュミレーションを意味する）
sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for *.example.com
Waiting 60 seconds for DNS changes to propagate

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```



## シェルスクリプト作成

rootユーザーで実行する想定

```
sudo vim /home/admin/renew_ssl_certs.sh

#!/bin/bash

# Renew the ssl cert.
certbot renew

# Copy the ssl cert to the folder that the nginx-proxy can read.
cd /home/admin/docker-configs/nginx-proxy/certs
cp /etc/letsencrypt/live/example.com/fullchain.pem ./example.com.crt
cp /etc/letsencrypt/live/example.com/privkey.pem ./example.com.key

# Restart docker containers to apply the new ssl cert.
cd /home/admin
docker-compose down --remove-orphans && docker-compose up -d
```



## rootユーザーのCRONとして登録

```
sudo crontab -e
—-----------------------------------------------------------
00 01 26 * * /home/admin/copy_ssl_certs.sh
```

数分後に実行してみてログを確認する

```
sudo less /var/log/syslog
—-----------------------------------------------------------
Apr 26 00:59:01 **** cron[958]: (root) RELOAD (crontabs/root)
Apr 26 01:00:01 **** CRON[2226]: (root) CMD (/home/admin/copy_ssl_certs.sh)
Apr 26 01:00:01 **** systemd[1]: Started snap.certbot.certbot.225709fe-f47d-43ac-81ce-e978a985b56d.scope.
```