Let's Encrypt certbot renewで躓いた話


色々な方の記事を参考にしました。
ベストプラクティスではない+曖昧な部分がありますので、ご注意ください。

前提

  • CentOS 7.8
  • Nginx 1.16.1

SSL証明書の導入後

Let's Encryptから何やらメールが届いた。
「もう少ししたら有効期限が切れるから早く更新してね!」という内容。
とてもありがたいです・・・(cron動いてないんだね・・・)

cronの確認

$ crontab -l

0 4 1 * * certbot renew && systemctl reload nginx

cronは設定してあるので、certbotコマンドで躓いているようですね。

certbot renewの実行(エラー発生)

certbot renewコマンドを何度も失敗していると、時間を空けないとコマンドを実行することができなくなるようです。
そのため、--dry-runオプションをつけて実行。

# certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/hoge.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for hoge
Using the webroot path /var/www/HOGE for all unmatched domains.
Waiting for verification...
Challenge failed for domain hoge
http-01 challenge for hoge
Cleaning up challenges
Attempting to renew cert (hoge) from /etc/letsencrypt/renewal/hoge.conf produced an unexpected error: Some challenges have failed.. Skipping.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certs could not be renewed:
  /etc/letsencrypt/live/hoge/fullchain.pem (failure)

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

The following certs could not be renewed:
  /etc/letsencrypt/live/hoge/fullchain.pem (failure)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Running post-hook command: service nginx start
Error output from post-hook command service:
Redirecting to /bin/systemctl start nginx.service

1 renew failure(s), 0 parse failure(s)

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: hoge
   Type:   unauthorized
   Detail: Invalid response from
   https://hoge/.well-known/acme-challenge/xxxxxxxxxxx
   [xx.xx.xx.xx]: "<html>\r\n<head><title>404 Not
   Found</title></head>\r\n<body>\r\n<center><h1>404 Not
   Found</h1></center>\r\n<hr><center>nginx</center>\r\n"

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.

通信はできているけど、404エラーになってる・・・
エラー内容からして、ファイルを参照できていないようですね。

ところでcertbot renewって何してるんだろう。

調べたところ、.well-known/acme-challenge/というフォルダの中にファイルを作成して読み取っているようなことをしているとのこと。
(深く調べていないので曖昧なままです。)

nginxのエラーログを見てみます。

# less /var/log/nginx/hoge-error.log

2020/11/xx xx:xx:xx [error] 24978#0: *6 open() "/var/www/hoge/public/.well-known/acme-challenge/xxxxxxxxxxxxxxxxxxxxxx" failed (2: No such file or directory), client: xx.xx.xx.xx, server: hoge.jp, request: "GET /.well-known/acme-challenge/xxxxxxxxxxxxxxxxxxxxxxx HTTP/1.1", host: "hoge.jp", referrer: "http://hoge/.well-known/acme-challenge/xxxxxxxxxxxxxxxxxxxxxx"

hoge/publicフォルダを見てる。。
ということで、nginxのconfを修正します。

nginxのconf修正

Laravelで作成したアプリをデプロイしているため、rootはpublicを指定しています。
また、バーチャルホストでドメイン毎に分けているのでsites-availableにconfファイルを配置しています。

変更前

/nginx/sites-available/hoge.conf
server {
    listen 80;
    server_name hoge.jp;
    rewrite ^ https://$server_name$request_uri? permanent;
}

server {
    listen 443 ssl;

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets on;
    ssl_certificate     /etc/letsencrypt/live/hoge/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge/privkey.pem;
    server_name hoge;
    access_log /var/log/nginx/hoge-access.log main;
    error_log /var/log/nginx/hoge-error.log;
    root /var/www/HOGE/hoge/public;
    location / {
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

変更後

/nginx/sites-available/hoge.conf
server {
    listen 80;
    server_name hoge.jp;
    rewrite ^ https://$server_name$request_uri? permanent;
}

server {
    listen 443 ssl;

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets on;
    ssl_certificate     /etc/letsencrypt/live/hoge/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge/privkey.pem;
    server_name hoge;
    access_log /var/log/nginx/hoge-access.log main;
    error_log /var/log/nginx/hoge-error.log;
    root /var/www/HOGE/hoge/public;
    location ^~ /.well-known/acme-challenge/ {    #追記ここから
        default_type "text/plain";
        root         /var/www;
        allow all;
    }  # 追記ここまで
    location / {
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

その後、systemctl reload nginxでnginxのリスタート。

certbot renewの再実行

これでいける!と思って実行したけどダメでした。

/var/log/nginx/hoge-error.log
2020/11/xx xx:xx:xx [error] 24978#0: *4 open() "/var/www/.well-known/acme-challenge/xxxxxxxxxxxxxxxxxx" failed (2: No such file or directory), client: xx.xxx.xx.xxx, server: hoge.jp, request: "GET /.well-known/acme-challenge/xxxxxxxxxxxxxxxxxx HTTP/1.1", host: "hoge.jp", referrer: "http://hoge.jp/.well-known/acme-challenge/xxxxxxxxxxxxxxxxxx"

どういうことだってばよ。
調べても時間だけが過ぎていったので、試行錯誤した結果こうなりました。

1. /var/www/配下に.well-known/acme-challengeを作成。
2. パーミッションを777に変更(しなくても大丈夫かもです。)
3. --webrootオプションを使用

1に関しては、コマンド実行時に何かしらのファイルを作るので、前もってフォルダを作成しました。
また、他のドメインもSSL証明書を更新するにあたり、全てこのフォルダを参照するようにしています。

2が必要かどうか確認していません。
セキュリティ云々はもうええんや!はよ!という感じでとりあえずやってみた。
力尽きた感。

3はオプションを指定していないとどうしてもエラーが発生してしまいました。

これらが正規の手順かはわかりませんが、全体を通して3時間ほど詰まってしまったためこちらで対応しています。

最終的な実行結果

(二つのドメインが対象です。)

# certbot renew --dry-run --webroot -w /var/www/

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/hoge.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for hoge.jp
Using the webroot path /var/www for all unmatched domains.
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/hoge.jp/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/huga.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator webroot, Installer None
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for huga.jp
Using the webroot path /var/www for all unmatched domains.
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/huga.jp/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/hoge.jp/fullchain.pem (success)
  /etc/letsencrypt/live/huga.jp/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

※最終的にうまくいく前にpre-hookやpost-hookがなんちゃらとかエラーも発生しましたが、それは他サイトのやり方を真似ていたためでした。

# less /etc/letsencrypt/renewal/hoge.conf

バージョンやらなんやら...
...
...

# Options used in the renewal process
[renewalparams]
authenticator = webroot
account = xxxxxxxxxxxxxxxxxxxxx
server = https://xxxxxxxxxxxxxxxxxxxxxxxxx
webroot_path = /var/www,
[[webroot_map]]
hoge.jp = /var/www
pre-hook = xxxxxx ←こいつ

ここでnginxの停止などを書いていたためエラーが発生していました。
こちらには「停止させる必要ないよ。」と書いてあったので削除対応しています。
https://community.letsencrypt.org/t/certbot-renew-improperly-restarts-nginx/55375

参考にさせていただいたページ

nginxについてまとめ(設定編)
「certbot renew」のエラーを解決した話
Let's Encryptの更新エラーを解決した。
Let'sEncryptの取得&自動更新設定してみた(CentOS7.1&Apache2.4.6)