Let’s EncryptのSSL証明書をお使いの方も多いと思いますが、90日ごとに証明書が切れてしまいます。Let’s Encrypt事務局が証明書の期限をアラートしてくれますが、3ヶ月に一度といえど、SSHでログインしてコマンドを叩くのは忘れてしまいがちですよね。

このため、cronを使って自動更新を試みました。

# crontab -e
30 * * * * certbot renew --post-hook "systemctl restart nginx"

手動で動かしている certbot renew --post-hook "systemctl restart nginx" を定期実行させてみます。このcrontabの内容は以下です。

  1. certbot renewコマンドでSSL証明書を更新
  2. 発行したSSL証明書を適用させるためにnginxを再起動

サンプルでは30分ごとになっていますが、90日以内に更新できればいいので、あくまで検証用です。

Mar 19 03:30:01 ip-172-31-24-233 CROND[7697]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Mar 19 03:30:01 ip-172-31-24-233 CROND[7698]: (root) CMD (certbot renew --post-hook "systemctl restart nginx")
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Saving debug log to /var/log/letsencrypt/letsencrypt.log)
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT ()
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Processing /etc/letsencrypt/renewal/example.com.conf)
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Cert is due for renewal, auto-renewing...)
Mar 19 03:30:02 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Non-interactive renewal: random delay of 384.201978393 seconds)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Could not choose appropriate plugin: The nginx plugin is not working; there may be problems with your existing configuration.)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (The error was: NoInstallationError("Could not find a usable 'nginx' binary. Ensure nginx exists, the binary is executable, and your PATH is set correctly.",))
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (Failed to renew certificate example.com with error: The nginx plugin is not working; there may be problems with your existing configuration.)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (The error was: NoInstallationError("Could not find a usable 'nginx' binary. Ensure nginx exists, the binary is executable, and your PATH is set correctly.",))
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT ()
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (All renewals failed. The following certificates could not be renewed:)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (  /etc/letsencrypt/live/example.com/fullchain.pem (failure))
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)
Mar 19 03:36:26 ip-172-31-24-233 CROND[7696]: (root) CMDOUT (1 renew failure(s), 0 parse failure(s))

エラーが出ました。"Could not find a usable 'nginx' binary. Ensure nginx exists, the binary is executable, and your PATH is set correctly." この部分ですね。

cronジョブは通常、シェル環境変数を読み込まないため、PATHが設定されていない場合があります。そのため、certbotコマンドがnginxの実行ファイルを見つけることができていないようです。。

解決するためには、crontabファイルにPATHを明示的に指定するか、certbotコマンドとnginxコマンドのフルパスを使用することが必要です。

方法1. crontabにPATHを明示的に指定する(検証済)

今回試したのはこちらです。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
30 * * * * certbot renew --post-hook "systemctl restart nginx"

環境変数 PATH の中にnginxの実行ファイルが存在するディレクトリを指定しました。

Mar 19 04:34:12 ip-172-31-24-233 CROND[8269]: (root) CMDOUT (Congratulations, all renewals succeeded: )
Mar 19 04:34:12 ip-172-31-24-233 CROND[8269]: (root) CMDOUT (  /etc/letsencrypt/live/example.com/fullchain.pem (success))
Mar 19 04:34:12 ip-172-31-24-233 CROND[8269]: (root) CMDOUT (- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)
Mar 19 04:34:12 ip-172-31-24-233 CROND[8269]: (root) CMDOUT (Running post-hook command: systemctl restart nginx)

無事、SSL証明書の更新とnginxの再起動に成功しました。

方法2. crontabにフルパスを指定する

もう1つはPATH変数を宣言せずにcronコマンドに直接パスを指定することでしょう。

30 * * * * /usr/bin/certbot renew --post-hook "/usr/bin/systemctl restart /usr/sbin/nginx"

こちらでも動くはずです。検証はしていません。certbot renewだけでなく、他のコマンドを使用することを想定すると方法1のほうがいいかもしれません。