はじめに
先日、お客様から次のようなご要望をいただきました。

ACMEに対応した新しいアプライアンス製品が欲しい
証明書の有効期間が47日に短縮される流れを受けて、社内の証明書をまとめて自動管理・自動更新できる専用の箱が欲しい

証明書の有効期間短縮の話は耳にしていましたが、こうして具体的なご要望として届くと、現場のニーズの高まりを実感しています。
ただ、仕組みが手に入るまで証明書の期限は待ってくれません。
まずは手元の製品でできることを考えたところ、EasyBlocks 監視シリーズの追加プラグイン機能が使えそうだと思い当たりました。
標準の監視コマンドに証明書期限の項目はありませんが、これまでも標準にない監視はプラグインを自作して追加してきました。
そこで今回は証明書の有効期限を監視するプラグインを作ってみます。スクリプトは自分で書かず、AIに書かせてみることにしました。
そもそも47日とは何の話か?
2025年4月、CA/BrowserフォーラムでBallot SC-081v3が可決されました。TLSサーバー証明書の最大有効期間を、現行の398日から段階的に短縮していくという内容です。
| 時期 | 最大有効期間 |
|---|---|
| ~2026年3月14日発行分 | 398日 |
| 2026年3月15日以降 | 200日 |
| 2027年3月15日以降 | 100日 |
| 2029年3月15日以降 | 47日 |
注目すべきは、この短縮がすでに始まっているという点です。
今年3月以降に発行される証明書は最長200日なので、これまで年1回だった更新作業は年2回になります。
なお、社内のプライベートCAから発行した内部向け証明書は、この規定の対象外です。とはいえ管理の手間が減るわけではありません。
47日という期間では手作業での更新は現実的ではないので、ACMEによる自動更新が本筋の答えになります。冒頭のご要望も、まさにそこを見据えたものです。
しかし・・・現場を見渡すと、ネットワーク機器やアプライアンスの管理画面、UPS、複合機のように、ACMEクライアントを動かせない機器が必ず残ります。しかも、そういう機器ほど普段は誰も画面を開きません。
自動化を進めるにしても、まずは期限を継続的に見る仕組みが必要です。
標準機能ではどこまで見えるか
EasyBlocks 監視の標準の監視コマンドに証明書期限の項目はありませんが、監視の土台にはNagiosと monitoring-pluginsが入っています。
標準プラグインのcheck_httpには証明書の残り日数で判定する「-C」オプションがあるので、まず試してみました。
# /usr/lib/nagios/plugins/check_http -H www.plathome.co.jp -S -C 30,7 --sni
OK - Certificate '*.plathome.co.jp' will expire on Sat Nov 28 23:59:59 2026 +0000.
問題なく動作し、残り30日でWARNING、7日でCRITICALという判定ができるので、これをそのまま使う手もあります。
ただ、この出力には足りないと感じた点がありました。
- 残り日数が出ない。期限日は分かるが、あと何日かは人間が数えることになる
- 閾値が固定日数しか渡せない
- その証明書が何日ものなのかが分からない
特に後半の2つは、47日時代を考えるとそのままでは使えません。
「残り30日で警告」は47日時代に成立するのか
証明書の期限監視というと、なんとなく「残り30日で警告」を設定してしまいがちかと思われます。有効期間が1年ある前提なら妥当な設定でした。
手元で確認できた2枚の証明書を見てみます。公式サイトのものと、EasyBlocks 監視自身のWeb UIの出荷時証明書です。
notAfter=Nov 28 23:59:59 2026 GMT → 有効期間 約396日(公式サイト)
notAfter=Jun 12 00:21:34 2036 GMT → 有効期間 約3650日(出荷時の自己署名)
この2枚に、これから増える200日ものと47日ものを加えて、「残り30日」が寿命のどのあたりなのかを並べるとこうなります。
| 有効期間 | 残り30日は寿命の | 警告が鳴り始めるまで |
|---|---|---|
| 3650日(出荷時の自己署名) | 0.8% | 約9.9年間、無警告 |
| 396日(現行の1年もの) | 7.6% | 約1年間、無警告 |
| 200日(2026年3月〜) | 15% | 約5.6か月、無警告 |
| 47日(2029年3月〜) | 64% | 更新から17日で警告開始 |
同じ設定値が、10年ものでは「10年近く何も起きない」、47日ものでは「更新した直後からほぼ鳴り続ける」という具合に振れてしまいます。
では残存割合で見ればいいのかというと、それも単独では破綻します。10年の証明書を「残り20%で警告」にすると、残り2年の時点で警告が始まってしまいます。
つまり日数と割合の両方で見て、さらに歯止めが必要です。この判定を標準プラグインの固定閾値では表現できないので、自作することにしました。
AIに指示した内容
プラグインは実行環境の作法に従わないと動かないので、まず環境の情報を伝えました。
- 標準出力は1行のみ、終了コードは 0=OK / 1=WARNING / 2=CRITICAL / 3=UNKNOWN
- 引数は$HOSTADDRESS$と$ARG1$以降で受け取る
- Debianベース、OpenSSL、timeoutコマンドあり
- 既存の自作プラグインと同じ書き方に揃える(bashで書く、PATHを明示設定、getoptsでオプション解析、一時ファイルを作らない)
次に仕様です。
ホスト・ポート・警告日数・危険日数・残存割合・割合判定の歯止め・SNI名・タイムアウトをオプションで受け取り、判定は次の順で評価する。
- 証明書が取得できない/日付がパースできない → UNKNOWN(3)
- すでに期限切れ → CRITICAL(2)
- 残り日数 <= 危険日数 → CRITICAL(2)
- 残り日数 <= 警告日数 → WARNING(1)
- 残存割合 <= 閾値 かつ 残り日数 <= 歯止め → WARNING(1)
- 上記以外 → OK(0)
出力には残り日数だけでなく有効期間と残存割合、自己署名かどうかも載せるよう指示しました。標準プラグインで足りないと感じた点を埋める形です。
出てきたもの
これで166行のプラグインが出てきました。肝心の判定部分を抜粋します。
remain_sec=$(( end_epoch - now_epoch ))
total_sec=$(( end_epoch - start_epoch ))
remain_days=$(( remain_sec / 86400 ))
ratio=$(( remain_sec * 100 / total_sec ))
if [ ${remain_sec} -lt 0 ] ; then
echo "CRITICAL: cert already expired ..."
exit ${CRITICAL}
elif [ ${remain_days} -le ${crit_days} ] ; then
...
elif [ ${ratio} -le ${warn_ratio} ] && [ ${remain_days} -le ${ratio_cap_days} ] ; then
echo "WARNING: cert expires in ${remain_days} days, ${ratio}% of lifetime left ..."
exit ${WARNING}
else
echo "OK: cert expires in ${remain_days} days ..."
exit ${OK}
fi
ありがたかったのは、個別に調査することなく動作するコードが得られた点です。openssl s_client でのSNI指定の書き方、x509 で返る日付の形式、それをdate -dでエポック秒に変換する手順。こうした確認を挟まずに、いきなり動くものが出てきました。
スクリプト全文の掲載は省きますが、上記の指示を投げれば手元でも同じようなものが作れるはずです。
有効期間の違う証明書で検証する
200日ものや47日ものの証明書は今はまだ手元にありません。閾値の判定を確かめるには有効期間の違う証明書が必要です。
そこで検証用の自己署名証明書を有効期間別(10年・396日・200日・47日・期限切れ済み)に用意し、openssl s_serverでローカルに待ち受けさせました。
既定値のまま47日の証明書を監視すると、こうなりました。
$ ./check_cert_expiry -h 127.0.0.1 -p 4434
WARNING: cert expires in 26 days (2026-08-25, lifetime 47d, 57% remaining)
→ 寿命の半分以上を残した状態で警告が鳴ってしまう
$ ./check_cert_expiry -h 127.0.0.1 -p 4434 -w 10 -c 5
OK: cert expires in 26 days (2026-08-25, lifetime 47d, 57% remaining)
→ 短期証明書に合わせて閾値を詰めるとOK
期限切れの証明書はCRITICALになり、TLSを喋らないポートや到達できないアドレスもハングせずUNKNOWNで戻りました。
EasyBlocks 監視に登録する
実機に登録します。「追加ファイル管理」で管理対象ファイルに「監視コマンドプラグイン」を選び、作成したファイルをアップロードします。

プラグイン名・ファイル名・表示名を入力し、実行引数設定に引数を指定します。プラグイン本体のパスは自動で付くので、引数部分だけを書きます。
-h $HOSTADDRESS$ -p $ARG1$ -w $ARG2$ -c $ARG3$
$HOSTADDRESS$ はプルダウンから選んで「追加」ボタンで挿入できます。入力後に「変数情報取得」の「取得」を押すと、動作確認用の入力欄が現れます。

ここで便利なのが「動作確認」機能です。監視パターンを作る前に、その場で実行して返り値と標準出力を確認できます。まずは監視サーバー自身のWeb UI(ポート4430)を対象にしてみます。

返り値は0、エラー出力もありません。有効期間3650日の自己署名証明書で、残り3605日、98%残っていることが1行で出ています。
続いて外部のCA発行証明書でも試します。ホストにFQDNを指定するとSNIは自動で付与されます。

こちらも返り値0。残り122日、有効期間395日、CN=*.plathome.co.jp と出ました。自己署名とCA発行、内部と外部、有効期間10年と約1年。どちらも問題なく取得できています。
監視対象と監視パターンを作る
動作確認が通ったので保存し、監視の設定に進みます。今回は監視対象を2つ登録しました。

あらためて一覧を眺めると、スイッチ、ルーター、Webカメラ、リブーターと、まさにACMEクライアントを動かせない機器が並んでいます。こうした機器の管理画面の証明書こそ、更新を忘れやすい対象です。
次に監視パターンです。ここで1つ注意点があります。監視パターンでは変数を1組しか指定できないため、ポート番号が違う対象は別のパターンに分ける必要があります。

自機用は変数指定を 4430!30!7 としました。「!」区切りで$ARG1$以降に渡る値です。

外部サイト用は 443!30!7 です。
監視状況を確認する
保存すると、監視状況一覧に結果が並びます。


有効期間3650日の自己署名証明書と、395日のCA発行証明書。10倍近く期間の違う2枚を、同じプラグイン・同じ閾値で監視している状態です。
前者は残り98%、後者は残り30%。同じ「残り30日で警告」という設定が、対象によってまったく違う意味を持つことが分かります。
最後に警告が出る状態も確認しておきます。公式サイトの証明書は残り122日なので、警告の閾値を130日に変えてみました。

WARNINGになり、行が黄色く変わりました。証明書の期限が近づくとこう見える、という確認ができました。
今後について
今回作ったのは、期限切れに気づくための仕組みです。47日という期間を手作業で回すのは現実的ではないので、本筋の答えはやはり自動化になります。
そして仮に自動更新の仕組みが手に入っても、ACMEクライアントを動かせない機器への配布と適用は残ります。発行が自動化されるほど、「ちゃんと入れ替わったか」を確かめる仕組みの重要性は上がっていくのではないかと思っています。
冒頭でご紹介したACME対応製品のご要望、こうしたご要望が増えることで、実現の可能性が高まると考えています。同じような課題をお持ちの方、ご意見があればぜひお寄せください。
まとめ
標準の監視コマンドにない証明書の期限監視を、追加プラグインで実装してみました。判定ロジックと実装はAIに任せ、有効期間の違う証明書で検証し、実機に登録するところまで確認できました。
47日への短縮は2029年の話ですが、200日への短縮はすでに始まっています。更新の頻度が上がるほど、うっかりが起きる機会も増えます。
まずは手元の機器の証明書がいつ切れるのか、把握しておくところから始めてはいかがでしょうか。
今後もEasyBlocksシリーズとAIを組み合わせた検証があれば、引き続き記事でご紹介していきます。
