【備忘録】自動更新スクリプトによりLet’s EncryptのSSL証明書期限切れを回避する

実現したいこと:Let’s Encryptの証明書を自動更新したい。
 
 
シャチ
駆け出しインフラエンジニアの備忘録です。
施工管理職から転職し2年目になります。

経緯
前提としてVPSサーバ内でメールサーバを構築しLet’s EncryptのSSL証明書を適用している。
他ブログで紹介されている自動更新cronが自分の環境ではうまく動作せず、定期的にSSL証明書の期限切れが起こりメールソフトからメールの送受信ができなくなっていた。
その度に新しいSSL証明書を発行し置き換えをしていたものの、古い証明書を参照している箇所があることでエラーが解消しなかった。参照箇所の特定にも長時間取られており(←駆け出しエンジニアならでは?)、この煩わしさから解放されたい思いから自動更新をセットアップする。

 
関連記事

実現したいこと:VPSサーバからWindows PCにsshでアクセスをできるようにして、ローカルにバックアップを取りたい。   シャチ 駆け出しインフラエンジニアの備忘録です。▷情報処理所持資格[基本情報技術者][情報セキ[…]

SSL証明書の有効期限を確認する

VPS(centos7)

・SSL証明書の有効期限を以下のコマンドで確認。

certbot certificates

コマンドを管理者権限で実行すると、以下のようにSSL証明書の情報が表示され、”Expiry Date”の項目で有効期限を確認。
Let’s EncryptのSSL証明書は、30日前にならないと更新ができない。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: XXXXXXXXXX 
    Serial Number: XXXXXXXXXX
    Key Type: RSA
    Domains: XXXXXXXXXX
    Expiry Date: 2022-10-29 17:00:23+00:00 (VALID: 79 days)
    Certificate Path: /etc/letsencrypt/live/XXXXXXXXXX/fullchain.pem <-サーバ証明書と中間証明書が結合されたファイル
    Private Key Path: /etc/letsencrypt/live/XXXXXXXXXX/privkey.pem <-秘密鍵
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

シャチ
上記の例だと有効期限まで残り79日あることが分かる。

SSL証明書の更新をテストする

VPS(centos7)
・SSL証明書の更新を以下のコマンドでテストできる。
実際にはテスト用のSSL/TLS証明書の取得を実施。
certbot renew --dry-run
・コマンドを管理者権限で実行すると、以下のようにテスト結果が表示される。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/XXXXXXXXXX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Simulating renewal of an existing certificate for XXXXXXXXXX
Performing the following challenges:
http-01 challenge for XXXXXXXXXX
Cleaning up challenges
Failed to renew certificate XXXXXXXXXX with error: Problem binding to port 80: Could not bind to IPv4 or IPv6.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
All simulated renewals failed. The following certificates could not be renewed:
  /etc/letsencrypt/live/XXXXXXXXXX/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 renew failure(s), 0 parse failure(s)
シャチ
上記の例ではPort 80へバインドできなかったため、テストが失敗。
 
・Let’s Encrypt 総合ポータルサイトでは、”certbot renew”コマンドには以下仕様があることが記載されている。

過去に Standalone プラグイン で取得した証明書が1枚有る場合、certbot renew 実行時にもデフォルトで同じプラグインが使用されます。
 
 SSL証明書取得時に以下コマンドを使用していたため、テスト時にも”Standalone”プラグインが使用された?よう。
Standalone使用時は、 TCP Port80 or 443を使用するため、動作中のWebサーバを一時的に終了させる必要がある。
certbot-auto certonly --standalone -d XXXXXXXXXX(domain name)

 

 

・VPS環境ではApacheが起動しているため、以下コマンドでApacheのサービスを停止させる。

systemctl stop httpd

・再度、証明書更新のテスト用コマンドを管理者権限で実行すると、下記のようにテスト結果が表示される。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/XXXXXXXXXX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org
Simulating renewal of an existing certificate for XXXXXXXXXX
Performing the following challenges:
http-01 challenge for XXXXXXXXXX
Waiting for verification...
Cleaning up challenges

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/XXXXXXXXXX/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
・今度は上記のように表示され、正常に更新テストが終了したことが確認できる。

スクリプトによりSSL証明書を自動更新する

・SSL証明書の自動更新コマンドを実行し、正しく更新されたか確認するため”letsencrypt.log”をsendmailにて送信するスクリプトを作成。

#!/bin/bash

# set the address
mail_from="XXXXXXXXXX"
rcpt_to="XXXXXXXXXX"

# set the subject
HOSTNAME="XXXXXXXXXX"
APPLICATION="XXXXXXXXXX"
SUBJECT="certbot renew result"
TITLE="[${APPLICATION}][${HOSTNAME}][${SUBJECT}]"
PASS="/var/log/letsencrypt/letsencrypt.log"

# perform certbot renew
systemctl stop httpd && certbot renew

if [ $? -eq 0 ]; then
  echo "renew successed"
else
  echo "renew failed"
fi

systemctl start httpd

# send letsencrypt-logs by sendmail
LOG=`tail -n 11 $PASS`
/usr/sbin/sendmail -t << EOF
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-2022-jp
From: ${mail_from}
To: ${rcpt_to}
Subject: ${TITLE}

---

$LOG

---

EOF

参考:簡単なログ監視用のシェルスクリプト|Classi株式会社:Qitta

 

・上記のスクリプトを毎月1日の3時に実行するようにcronへ登録する。

vi /etc/crontab
0 3 1 * * root /home/"User_name"/XXXXXXXXXX.sh

 

・これによりSSL証明書の自動更新のセットアップが完了。有効期限30日まではSSL証明書の更新はスキップされるが、スクリプトが正常に動作しているかを毎月必ず確認できるようになる。

SSL証明書が期限切れになった場合の対処

SSL証明書が有効期限切れになった場合は、新たにSSL証明書を発行する必要がある。
 

新しい証明書の発行

・まず初めに、Webサーバが動作していた場合にサーバを一時的に停止する。

systemctl stop httpd

・次に、新しいSSL証明書を発行し、Webサーバを再開する。

certbot-auto certonly --standalone -d XXXXXXXXXX
systemctl start httpd

・”/etc/letsencrypt/live/”フォルダへ新しいSSL証明書データが格納されるので、古いSSL証明書を”backup”フォルダへ移動する。

mv "old-certificate" /etc/letsencrypt/live/backup

 

古いSSL証明書を参照している箇所の修正

・”/etc/letsencrypt/renewal”にある古いSSL証明書更新用の設定ファイルを”backup”フォルダへ移動する。

mv "XXXXXXXXXX.conf" /etc/letsencrypt/renewal/backup

・postfixの設定ファイルでSSL証明書と秘密鍵を参照している箇所を新しい証明書へ置き換える。
※以下、”=”の右辺を新しい証明書の”Certificate Path”と”Private Key Path”へ書き換え。

vi /etc/postfix/main.cf
 smtpd_tls_cert_file = /etc/letsencrypt/live/XXXXXXXXXX/fullchain.pem
 smtpd_tls_key_file = /etc/letsencrypt/live/XXXXXXXXXX/privkey.pem

・dovecotの設定ファイルでSSL証明書と秘密鍵を参照している箇所を新しい証明書へ置き換える。
※以下、”=”の右辺を新しい証明書の”Certificate Path”と”Private Key Path”へ書き換え。

vi /etc/dovecot/conf.d/10-ssl.conf
 ssl_cert = </etc/letsencrypt/live/XXXXXXXXXX/fullchain.pem
 ssl_key = </etc/letsencrypt/live/XXXXXXXXXX/privkey.pem

・postfixとdovecotをrestartし、ステータスにエラーが出ていなければ修正完了。

systemctl restart postfix && systemctl restart dovecot
systemctl status postfix
systemctl status dovecot

 

まとめ:【備忘録】自動更新スクリプトによりLet’s EncryptのSSL証明書期限切れを回避する

SSL証明書更新の”certbot renew”コマンド時にはTCP Port80 or 443を使用するため、動作中のWebサーバを一時的に終了させる必要があった。
結果的に参照したサイトがWebサーバを停止せずにcertbot renewコマンドのみをcronに登録していたため、自動更新がエラーとなり証明書の期限切れが起こっていた。
内容を理解しないままググったことをコピペした際に陥りやすい事象な気がするため、何をやっているのか自分で理解しながら取り入れる必要性を改めて感じる結果となった。。
 
シャチ
構築する際は”公式ドキュメント”を見るべきと学びました。

>
CTR IMG