Nginx + Let's Encryptでサクッと証明書登録とhttps化

nginxで立てたサーバーを手軽かつ無料でhttps通信ができるようにする
### 用語の確認
* Let's Encrypt : 認証局（CA）の名称
* certbot : Let's Encryptが提供する証明書発行、更新用のCLIツール
* ACME challenge : 証明書発行のためのサーバーの検証チェック方式
    * 80番ポートを使用。既にサーバーが用意されている前提。
    * https://letsencrypt.org/ja/docs/challenge-types/#http-01-%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8

### 前提
* domain取得済み(今回はexample.comとする)
* nginxにより`http://example.com`へ接続ができる状態
## version
```
os : AlmaLinux release 9.4 (Seafoam Ocelot)
nginx : 1.20.1
certbot : 2.11.0
```

### nginx設定
ACME challengeで使用するための設定
```nginx.conf
server {
    listen       80;
    server_name  example.com;

    
    location / {
        root     /usr/local/nginx/html;
    }
}
```

###  certbotのインストール
```
# yum install epel-release
# yum install certbot
```

###  certbotで証明書発行
```
# mkdir -m 755 -p /usr/local/nginx/html/.well-known/acme-challenge
# certbot certonly --webroot -w /usr/local/nginx/html -d example.com -m sample@example.com --agree-tos -n
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.com

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 2024-09-30.
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```

オプションの解説
* `-m sample@example.com`
    * 通知用のメールアドレス
* `-d example.com`
    * 使用するドメイン
* `--webroot`
    * ACME challengeの実行方法 (`--webroot`/`--standalone`)
    * >1: Runs an HTTP server locally which serves the necessary validation files under the /.well-known/acme-challenge/ request path. Suitable if there is no HTTP server already running. HTTP challenge only (wildcards not supported). (standalone)

    * >2: Saves the necessary validation files to a .well-known/acme-challenge/ directory within the nominated webroot path. A seperate HTTP server must be running and serving files from the webroot path. HTTP challenge only (wildcards not supported). (webroot)
* `-w /usr/local/nginx/html`
    * tokenの配置先 : `{path}/.well-knwon/acme-challenge/{token}`
    * CAが`http://example.com/.well-known/acme-challenge/{token}`を見に行く
* `--agree-tos -n`
    * ACME登録への同意と非インタラクティブに実行

### nginxのssl設定
```nginx.conf
server {
    listen       80;
    server_name  example.com;

    
    location / {
        root     /usr/local/nginx/html;
    }
}

# 発行した証明書を登録
server {
    listen       443 ssl;
    server_name  example.com;

    ssl_certificate /etc/nginx/fullchain.pem;
    ssl_certificate_key /etc/nginx/privkey.pem;
    
    # ...
}
```

```
# cp /etc/letsencrypt/live/example.com/fullchain.pem /etc/nginx/fullchain.pem
# cp /etc/letsencrypt/live/example.com/privkey.pem /etc/nginx/privkey.pem
```

### 証明書の更新
発行した証明書は約3ヶ月で期限切れとなるため、以下のように定期的な更新が必要になります
```
# certbot renew
# cp /etc/letsencrypt/live/example.com/fullchain.pem /etc/nginx/fullchain.pem
# cp /etc/letsencrypt/live/example.com/privkey.pem /etc/nginx/privkey.pem
# nginx -s reload
```
期限が近くなるとメールで通知してくれる。
期限より30日前以前に更新しようとすると以下のようなメッセージが出て失敗します。
```
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certificate not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificates are not due for renewal yet:
  /etc/letsencrypt/live/example.com/fullchain.pem expires on 2024-09-30 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```