Ubuntu

Let’s Encryptのワイルドカード証明書が自動更新されている!?

このサイトとは別にサーバーを運営していて、そこでもLet’s Encryptのワイルドカード証明書を使用している。
Cronから自作のスクリプトを呼び出し、期限が30日を切ったら自動更新をしており、更新された証明書を各サーバーに証明書を配っている。

今年になってサーバーをUbuntu 18.04から22.04に差し替えたので、その処理ごと移行してあり、初回はうまく動いてくれた。
しかし、2回目、3回目と、証明書は更新されているのに、各サーバーへの配布がされなかった。

何故だろう?と思って調べてみることにした。



広告


対応

結論からすると、certbot.timerによって起動されたcertbotが、ワイルドカード証明書を自動更新するのを止めた。
これによって、自前のスクリプトが動作し、ワイルドカード証明書を更新した後の処理が行われる。

$ sudo systemctl stop certbot.timer
$ sudo systemctl disable certbot.timer
Removed /etc/systemd/system/timers.target.wants/certbot.timer.

これは動かないはずなんだけれども、念のため、Cronに仕掛けられたファイルも止めておいた。

$ sudo mv /etc/cron.d/certbot /etc/cron.d/.certbot.org

ワイルドカード証明書を更新するには、DNS-01とのやりとりが必要。
運用中のスクリプトでは、コマンドラインでDNSのTXTレコードを書き換えるスクリプトを指定する形で、自動(≠対話式のつもり)でDNS-01とやりとりをするようになっている。
一度これが成功すると、どうやらそのコマンドラインパラメーターを覚えてくれているようで、その後はcertbot.timerで起動されたときにうまいことやってくれていたのだった。

ワイルドカード証明書を発行した後にやりたいこともあるので、certbot.timerを止めて、自前のスクリプトが動くようにしたという話。

調べてみた

なぜ自動起動されるのか

systemdのタイマーが仕掛けられており、1日2回起動している。

最初、Cronのファイル名を変更して動かないようにしていた。
でもcertbotが動いているようだった。

/var/log/letsencrypt/letsencrypt.log

2023-12-03 03:47:55,753:DEBUG:certbot._internal.main:certbot version: 1.21.0
2023-12-03 03:47:55,754:DEBUG:certbot._internal.main:Location of certbot entry point: /usr/bin/certbot
2023-12-03 03:47:55,754:DEBUG:certbot._internal.main:Arguments: ['-q']
2023-12-03 03:47:55,754:DEBUG:certbot._internal.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#manual,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
2023-12-03 03:47:55,772:DEBUG:certbot._internal.log:Root logging level set at 40
2023-12-03 03:47:55,776:DEBUG:certbot._internal.display.obj:Notifying user: Processing /etc/letsencrypt/renewal/hogeserver.hogeddns.jp.conf
2023-12-03 03:47:55,790:DEBUG:certbot._internal.plugins.selection:Requested authenticator <certbot._internal.cli.cli_utils._Default object at 0x7f3af4e48d90> and installer <certbot._internal.cli.cli_utils._Default object at 0x7f3af4e48d90>
2023-12-03 03:47:55,805:DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): r3.o.lencr.org:80
2023-12-03 03:47:56,120:DEBUG:urllib3.connectionpool:http://r3.o.lencr.org:80 "POST / HTTP/1.1" 200 503
2023-12-03 03:47:56,121:DEBUG:certbot.ocsp:OCSP response for certificate /etc/letsencrypt/archive/hogeserver.hogeddns.jp/cert4.pem is signed by the certificate's issuer.
2023-12-03 03:47:56,125:DEBUG:certbot.ocsp:OCSP certificate status for /etc/letsencrypt/archive/hogeserver.hogeddns.jp/cert4.pem is: OCSPCertStatus.GOOD
2023-12-03 03:47:56,134:DEBUG:certbot._internal.display.obj:Notifying user: Certificate not yet due for renewal
2023-12-03 03:47:56,135:DEBUG:certbot._internal.plugins.selection:Requested authenticator manual and installer None
2023-12-03 03:47:56,136:DEBUG:certbot._internal.display.obj:Notifying user:
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2023-12-03 03:47:56,136:DEBUG:certbot._internal.display.obj:Notifying user: The following certificates are not due for renewal yet:
2023-12-03 03:47:56,136:DEBUG:certbot._internal.display.obj:Notifying user:   /etc/letsencrypt/live/hogeserver.hogeddns.jp/fullchain.pem expires on 2024-02-28 (skipped)
2023-12-03 03:47:56,136:DEBUG:certbot._internal.display.obj:Notifying user: No renewals were attempted.
2023-12-03 03:47:56,136:DEBUG:certbot._internal.display.obj:Notifying user: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2023-12-03 03:47:56,136:DEBUG:certbot._internal.renewal:no renewal failures

※FQDNをマスクしている。

syslogを見てみると、systemdから起動されていることが分かった。

/var/log/syslog

Dec  3 03:47:54 hogehoge systemd[1]: Starting Certbot...
Dec  3 03:47:56 hogehoge systemd[1]: certbot.service: Deactivated successfully.
Dec  3 03:47:56 hogehoge systemd[1]: Finished Certbot.

systemdのユニットを確認してみる。
タイマーがセットされていて、それによって起動していた。

$ systemctl status certbot.service
○ certbot.service - Certbot
     Loaded: loaded (/lib/systemd/system/certbot.service; static)
     Active: inactive (dead) since Sun 2023-12-03 03:47:56 JST; 8h ago
TriggeredBy: ● certbot.timer
       Docs: file:///usr/share/doc/python-certbot-doc/html/index.html
             https://certbot.eff.org/docs
    Process: 4127995 ExecStart=/usr/bin/certbot -q renew (code=exited, status=0/SUCCESS)
   Main PID: 4127995 (code=exited, status=0/SUCCESS)
        CPU: 893ms

Dec 03 03:47:54 hogehoge systemd[1]: Starting Certbot...
Dec 03 03:47:56 hogehoge systemd[1]: certbot.service: Deactivated successfully.
Dec 03 03:47:56 hogehoge systemd[1]: Finished Certbot.

$ systemctl status certbot.timer
● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Sun 2023-09-24 14:26:09 JST; 2 months 9 days ago
    Trigger: Sun 2023-12-03 13:12:47 JST; 49min left
   Triggers: ● certbot.service

Sep 24 14:26:09 hogehoge systemd[1]: Started Run certbot twice daily.

タイマーの中身はこうなっていた。

/lib/systemd/system/certbot.timer

[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00 ← 毎日0時と12時にイベント発生
RandomizedDelaySec=43200     ← 12時間の範囲でランダムに遅延させる
Persistent=true

[Install]
WantedBy=timers.target

確かに毎日2回起動しており、起動のタイミングはランダムになっている。

$ zgrep "Starting Certbot" /var/log/syslog*
/var/log/syslog:Dec  3 03:47:54 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 26 10:19:58 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 26 20:23:16 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 27 02:58:42 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 27 19:58:51 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 28 03:33:58 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 28 13:51:42 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 29 03:49:47 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 29 14:20:21 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 30 11:36:19 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Nov 30 20:03:53 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Dec  1 09:44:38 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Dec  1 13:19:09 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Dec  2 00:48:04 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.1:Dec  2 21:42:22 hogehoge systemd[1]: Starting Certbot...
/var/log/syslog.2.gz:Nov 19 08:58:35 hogehoge systemd[1]: Starting Certbot...
…

なぜ自動更新されるのか

certbotの自動更新は、証明書の取得や直近の更新に使用されたプラグインやオプションを再利用するようになっていた。

具体的には、systemdで以下が起動されている。

/lib/systemd/system/certbot.service

[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://certbot.eff.org/docs
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true

certbot -q renewが呼び出されている。
-qはエラー出力がされなくなり、non-interactiveになる。
renewは全ての証明書を更新。

renweについてmanページを見てみると、以下の説明がされていた。
DeepL先生に翻訳してもらった。

The 'renew' subcommand will attempt to renew any certificates previously obtained if they are close to expiry, and print a summary of the results.  By default, 'renew' will reuse the plugins and options used to obtain or most recently renew each certificate. You can test whether future renewals will succeed with `--dry-run`. Individual certificates can be renewed with the `--cert-name` option. Hooks are available to run commands before and after renewal; see https://certbot.eff.org/docs/using.html#renewal for more information on these.

'renew'サブコマンドは、以前に取得した証明書の有効期限が近い場合に更新を試み、その結果の概要を表示します。デフォルトでは、'renew'は各証明書の取得や直近の更新に使用されたプラグインやオプションを再利用します。今後の更新が成功するかどうかは `--dry-run` でテストできます。個々の証明書は `--cert-name` オプションで更新できる。更新の前後にコマンドを実行するためのフックが利用できます。これらの詳細については https://certbot.eff.org/docs/using.html#renewal を参照してください。

かなり便利。

DNS-01にチャレンジする場合には、DNSの書き換えが必要だけれど…

/var/log/letsencrypt/letsencrypt.log

2023-11-30 20:06:51,891:INFO:certbot._internal.auth_handler:Performing the following challenges:
2023-11-30 20:06:51,891:INFO:certbot._internal.auth_handler:dns-01 challenge for hogeserver.hogeddns.jp
2023-11-30 20:06:51,891:INFO:certbot._internal.auth_handler:dns-01 challenge for hogeserver.hogeddns.jp
2023-11-30 20:06:51,892:INFO:certbot.compat.misc:Running manual-auth-hook command: /hogehoge/auth-hook.php
2023-11-30 20:06:56,293:INFO:certbot.compat.misc:Running manual-auth-hook command: /hogehoge/auth-hook.php

ちゃんと自作スクリプトが呼び出されているので、これでTXTレコードが更新され、DNS-01チャレンジが成功していたのだった。

なぜサーバーリプレース時に気付かなかったのか

Ubuntu 18.04からUbuntu 22.04への移行では、新しいUbuntu 22.04サーバーを構築し、そこに色々サービスを持ってきた。

  • 新サーバーでcertbotをインストールしたけれど、この段階ではrenewはうまく動かない。
  • Let’s Encryptでワイルドカードを更新する自前のスクリプトを新サーバーに持ってくる。
    そして、Cronに仕掛け、自前のスクリプトが起動され、証明書が更新された(これは、Cronのレポートメールが残っている)。
    これにより、certbotはrenewの方法を覚える。
  • certbot.timerは毎日2回 certbot -q renew を起動しており、証明書の期限が30日を切ったときに証明書が更新される。
  • certbot.timerによって証明書が更新されていたから、Cronは毎日1回自前のスクリプトを起動しているけれど、それは空振りしていた。

ログが全て残っているわけではないから予測だけれども、こんなことだったのかなと。
最初がうまくいったから大丈夫、と安心していたけれども、こんな問題が発生するとは。

サーバーを移行したら、こんな風に長い周期のものも忘れずにチェックしないと、不具合を起こすことがあるんだなぁ…

certbotのCron設定

調べはじめた最初に、certbotが仕掛けたCronを止めていたはずなのに、certbotは動いていた。
じゃあCronには何が設定されているの?

/etc/cron.d/certbot

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew

manページを見ると…

-x FILE
        FILE exists and execute (or search) permission is granted

EXPRESSION1 -a EXPRESSION2
        both EXPRESSION1 and EXPRESSION2 are true

! EXPRESSION
        EXPRESSION is false

-d FILE
        FILE exists and is a directory

/run/systemd/systemディレクトリは存在するのでtestは失敗するから、certbotは実行されない。

最初のところで、/etc/cron.d/certbotの名前を変えているけれども、今のUbuntuではやる必要もない。
とはいえ、パッケージに念のため含まれているのだろうから、念のためやっておいたという話。

さいごに

問題が分かったその時にちゃんと手を打っておけば、2回目(3回目の更新)に同じ問題が発生することはなかった。
システムなんだから、たまたま失敗、たまたま成功なんてことはないのだから、その時に調べておけば良かったな。

広告
ろっひー

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他