Menu/Prof
スズキマコト
自由人
元々は楽器屋のギター兄ちゃん。
趣味でプログラミングしてるうちに
本職になってしまった人。

過去に喋っていた言語
c pascal Assembler
perl PHP Python Ruby など
javascriptなどは都度必要に応じて。
最近Mac買ったのでswift勉強してます。

体ぶっ壊して死にかけたので人生RESET中。
おすすめ記事!

CloudflareのCDN利用時のSSL自動更新を支えるRustガード設計

Some visuals are licensed via Canva Pro (includes commercial rights).
Usage complies with Canva’s license terms at the time of use.
License policy: canva.com/policies/content-license-agreement

このBLOGでは、Web配信の高速化や保護を担うCDNとしてCloudflareを利用し、proxyを有効にしています。ところが、その構成で運用しているとレンタルサーバーが提供する無料SSLの自動更新は確実に失敗します。
回避方法としてCloudflareのDNSレコードをProxied(オレンジ雲)からDNS onlyへ切り替えると、自動更新は通ります。更新を止めていたのは、Cloudflareのproxyが無料SSLのドメイン所有確認経路に入っていたことです。

レンタルサーバーの無料SSLは、更新時に認証局からドメインの所有確認を受けます。今回使われていたのは、サーバー上の`/.well-known/acme-challenge/`へ置かれた確認用ファイルを認証局がHTTPで読み取る方式です。proxyが有効なままでは、そのアクセスがCloudflareを経由し、レンタルサーバーの確認用ファイルを正しく取得できません。所有確認に失敗するため、証明書も更新されません。

DNS onlyへ切り替えると、認証局はCloudflareを経由せず、レンタルサーバーへ直接アクセスできます。確認用ファイルが読めるようになり、所有確認と証明書更新が通ります。仕組みは明快ですが、更新のたびにproxyを手でOFFにし、完了を待ってONへ戻す運用は面倒で、戻し忘れも起こり得ます。

そこでこの記事では、Cloudflareのproxyを更新時だけ安全に外し、証明書の更新を確認したら必ず元へ戻すRustガードを設計します。単なるDNS切替ではなく、対象の固定、変更前状態の保存、SNI付きの証明書観測、失敗時の原状復帰までを一つの処理として扱います。

contents

Cloudflareのproxyが無料SSL更新を止めた仕組み

青いネットワーク側証明書と暖色のオリジンサーバー側証明書が左右に分かれ、じぴこがCloudflare配下で見える証明書の違いを比べている。

SSL証明書の更新は、有効期限を書き換えるだけの処理ではありません。認証局は新しい証明書を発行する前に、そのドメインが本当に対象サーバーで管理されているかを確認します。今回止まったのは証明書の発行処理そのものではなく、その前段にあるドメイン所有確認です。

認証局が確認用ファイルを読めず所有確認に失敗する

レンタルサーバーは更新時に、一時的な確認用ファイルを`/.well-known/acme-challenge/`配下へ置きます。認証局は指定されたURLへアクセスし、正しい文字列が返ることを確認してから証明書を発行します。これはHTTP-01認証と呼ばれる仕組みです。

Cloudflareのproxyが有効なとき、認証局からのアクセス経路は`認証局 → Cloudflare → レンタルサーバー`になります。この環境では、Cloudflareを経由したアクセスでレンタルサーバー上の確認用ファイルを正しく取得できず、認証局の所有確認が失敗しました。それが無料SSLを更新できなかった直接の原因です。

DNS onlyにすると認証局がオリジンへ直接届く

DNSレコードをDNS onlyへ切り替えると、Cloudflareのproxyは通信経路から外れます。認証局からのアクセスは`認証局 → レンタルサーバー`となり、確認用ファイルへ直接届きます。

実際に、この切替後は所有確認が通り、レンタルサーバーの無料SSLを更新できました。したがって今回必要なのは、原因を延々と切り分けることではなく、更新期間だけproxyを確実に外し、更新後に元の状態へ戻す運用です。

更新時だけproxyを外して必ず元へ戻す

proxyを常時OFFにすれば更新経路は単純になります。しかし、それではCloudflareのCDNやWAFなど、proxyを有効にすることで得ている機能も使えません。このBLOGで必要なのは、proxyを捨てることではなく、無料SSLの更新中だけ一時的にDNS onlyへ切り替えることです。

手作業では、切替後に戻し忘れる、更新確認を誤る、処理の途中で作業が止まる、といった事故が起こります。Rustガードは、現在のproxy状態を保存してからDNS onlyへ切り替え、オリジン証明書の更新を確認し、成功でも失敗でも保存した状態へ戻します。今回の自動化は、この一連の運用を安全に繰り返せるようにするためのものです。

更新主体とRustガードの責務を分ける全体フロー

上段のサーバー証明書更新と下段の五つの安全工程が別々の流れで並び、じぴこがRustガードの工程を指して責務分担を示している。

証明書を発行・配置する主体はレンタルサーバー側です。Rustガードは証明書を発行しません。Cloudflare DNSの限定切替、待機、オリジンへのTLS観測、原状復帰を一つの制御として束ねます。この境界が曖昧だと、更新失敗時にどこを調べるべきかも曖昧になります。

証明書を更新するのはレンタルサーバー側

レンタルサーバーが無料SSLを管理する構成では、秘密鍵、証明書要求、発行後の配置などはサーバー側の領域です。Rust CLIは、その内部状態を直接知っている前提を置きません。観測できるのは、指定した接続先IPへTLS接続したとき、対象ホスト名のSNIに対してどの証明書が提示されたかです。

この設計は責任逃れではありません。むしろ、判定根拠を明確にします。サーバー内部の「更新ジョブ完了」という表示ではなく、実際のオリジン接続で新しい証明書を受け取れるかを成功条件の一部にするからです。ただし、その観測だけで製品内部の更新処理や次回更新予約まで証明できるわけではありません。

Rust CLIが担うDNS切替・待機・観測・復帰

CLIの仕事は、次の五つに整理できます。

STEP
対象を検証する

対象ゾーン、対象レコード、接続先IP、期待するホスト名を検証する。

STEP
変更前の状態を保存する

現在のproxy状態と更新前証明書の識別情報を保存する。

STEP
DNS onlyへ変更する

保存後に限り、対象レコードをDNS onlyへ変更する。

STEP
更新を観測する

オリジン証明書をSNI付きで反復観測し、更新条件を判定する。

STEP
保存済み状態へ戻す

成功・失敗・タイムアウトのどの場合でも、保存済み状態へ戻す。

ここで「保存後に限り」が重要です。変更してから戻り先を記録する順序では、二つの処理の間で停止した瞬間に復旧根拠を失います。先に意図と現状を残し、その後で外部状態を変える。この順番がガードの中心です。

OFFから更新観測を経てONへ戻す基本系列

状態を大づかみに並べると、`Idle → Prepared → ProxyDisabled → Observing → Restoring → Completed` となります。エラー専用の終端を増やすより、失敗時も`Restoring`へ集め、復帰できたかどうかを別に記録するほうが運用判断を単純にできます。

たとえば、深夜の定期実行を仮定してみます。CLIは対象レコードがallowlistと一致することを確認し、現在の`proxied=true`と更新前証明書の情報をstate fileへ書きます。その後だけ`proxied=false`へ進みます。観測中にプロセスが終了しても、次回起動は`ProxyDisabled`という未完了状態を読み、まず復帰を試みます。新しい更新処理を重ねません。

この小さな場面は、DNS切替を「ON/OFFの二値操作」ではなく、戻り先を持つ運用トランザクションとして見る助けになります。短いスクリプトより状態は増えます。しかし、途中停止後に人間が記憶だけで復旧する負担は減らせます。

実装前に四つの運用を選び分ける

このガードは、proxyを一時的に外せる環境でだけ成立します。実装へ進む前に、三つの条件を順に見てください。proxy経由の機能が必要か。解除中のオリジン直接公開を受け入れられるか。対象レコードと開始時の戻り先を一意に保存し、復帰を試験できるか。

  • proxy経由の機能が不要で、オリジン直接公開を受け入れられるなら、常時DNS onlyが最も単純です。
  • proxy経由の機能は必要でも、一時解除の影響を受け入れられないなら、proxy切替を不要にするレンタルサーバーまたはDNS構成の見直しへ戻ります。
  • 一時解除はできても、allowlist、先行記録、復帰試験がまだ揃わないなら、監視付きの手動切替をチェックリストで運用します。
  • proxy経由の機能が必要で、一時解除を許容でき、対象と戻り先を一意に管理できるなら、Rustガードが採用候補になります。

「自動化できるか」だけでは足りません。解除中の影響と、戻れることを証明できるか。この二点に答えられて初めて、ガードを実装する理由が立ちます。

check・run・recoverを一つの外部契約にする

ライブラリを選ぶ前に、CLIの入口を三つへ分けると、実装と故障注入試験の対象が見えます。名前は例ですが、責務は混ぜません。

guard check

read-onlyです。Keychainからtokenを取得できるか、allowlistが一意か、APIで現在値を読めるか、オリジン証明書を観測できるかを確認し、stateもDNSも変更しません。

guard run

通常の更新ガードです。未完了stateがなければ、先行記録、DNS onlyへの切替、更新観測、復帰を実行します。未完了stateがあれば新しい更新を始めず、同じ復帰処理へ入り、その結果を返して終了します。

guard recover

その復帰処理を運用者と試験から明示的に呼ぶ入口です。未完了stateがなければDNSへ書き込まず、復旧対象なしとして終えます。

つまり、外向きのコマンドは三つでも、未完了stateを処理する実装経路は一つです。終了結果は後述する`completed`、`update_not_observed`、`restore_failed`、`conflict_detected`などへ集約し、コマンドごとに別の意味を持たせません。

対象限定と最小権限トークンで誤操作範囲を狭める

じぴこが複数の対象カードを手で止め、一つの承認カードだけを鍵穴付きゲートと施錠保管庫へ通して最小権限を示している。

自動化の安全性は、正常系の速さより、誤って触れられる範囲の狭さで決まります。コードが正しくても、対象指定が広ければ、設定ミス一つで別サイトのDNSへ影響します。実装前にゾーン、レコード、API権限、秘密情報の境界を固定してください。

ゾーンとDNSレコードをallowlistで固定する

対象を「指定ゾーンの全Aレコード」のような検索条件だけで決めるのは危険です。公開用の例では、ゾーン名、レコード名、種別、期待する現在値を明示したallowlistを用意します。実ドメインや実IPは記事やリポジトリへ書かず、`example.com`とドキュメント用IPなどのplaceholderへ置き換えます。

[[targets]]
zone = "example.com"
record = "www.example.com"
record_type = "A"
expected_origin = "192.0.2.10"

実行時には、APIが返したゾーンIDやレコードIDをそのまま信用せず、名前、種別、内容がallowlistと一致するかを再確認します。一件にも絞れない、または期待値が違う場合は変更しません。「見つかった最初の一件」を更新する便利さは、誤操作時の影響と釣り合いません。

OAuth依存を外してスコープ付きAPI tokenを使う

単一運用者のローカルCLIで、ブラウザ認可やrefresh tokenの維持まで抱える必要がなければ、対象と操作を絞ったAPI tokenのほうが構成を小さくできます。ここで重要なのは方式名ではなく、権限境界です。DNS編集に必要な範囲だけを与え、アカウント全体の広い権限を持たせない設計にします。

もちろん、API tokenなら自動的に安全になるわけではありません。広いゾーン権限を付ければ、漏えい時の影響も広がります。また、CLI側のallowlistはCloudflare側の権限を置き換えません。サーバー側の最小権限とクライアント側の対象限定を重ねることで、一方の設定ミスをもう一方が受け止めます。

macOS Keychainから実行時だけ秘密を受け取る

tokenは設定ファイルやコマンドライン引数へ直接置かず、macOS Keychainから実行時に取得します。コマンドライン引数はプロセス一覧や履歴へ残り得るため、token本体を渡す経路には向きません。Rust側はKeychain項目を示すplaceholder識別子だけを設定として持ち、取得した秘密はログへ出さないようにします。

取得処理には、見つからない、アクセスが拒否された、空文字だった、といった失敗があります。これらをDNS変更後に発見してはいけません。token取得とAPIの読み取り確認は`Prepared`へ入る前に済ませます。秘密が使えないなら、外部状態には一切触れず終了します。

公開コードではplaceholderだけを使う

公開例に必要なのは、実環境の識別子ではなく、置換箇所が分かる構造です。実ドメイン、実IP、アカウント名、Keychainのservice/account名、API token、Cloudflareの各種IDは含めません。ログ例にも同じ境界を適用します。

たとえば`zone_id`を丸ごと記録する代わりに、ハッシュ化した対象ラベルやローカルの論理名を使えます。ただし、障害対応で照合可能な情報まで消すと運用性が落ちます。公開物のサニタイズと、ローカルで必要な監査ログは別の保存先・保持方針として設計してください。

write-ahead stateと状態機械で途中停止に備える

状態カードが先に施錠装置へ保存され、停止ゲートと外部スイッチを経た戻り矢印が保存側へ循環し、じぴこが先行記録を守っている。

DNS変更はプロセスの外に残ります。CLIが落ちても、Cloudflare側の状態は自動では戻りません。だからこそ、外部変更より先に復帰情報を記録する必要があります。データベースのwrite-ahead logに似ていますが、単一のstate fileが同じ耐久性を持つわけではありません。原子的な更新と排他制御を別途設計します。

DNS変更より先に復帰情報を永続化する

stateには、少なくとも実行ID、開始時刻、対象の論理名、変更前proxy値、現在フェーズ、更新前証明書の識別情報を持たせます。token本体は保存しません。API上の識別子を保存する場合も、公開ログへ出す値とは分けます。

enum Phase {
    Prepared,
    ProxyDisabled,
    Observing,
    Restoring,
    Completed,
}

struct TargetState {
    logical_name: String,
    original_proxied: bool,
    changed_by_this_run: bool,
}

`changed_by_this_run`のような情報があると、もともとDNS onlyだったレコードを不用意にONへ戻す事故を避けられます。「通常はONのはず」という期待ではなく、開始時に読んだ実値へ戻すのが原則です。

状態遷移を冪等にして再起動時の入口を一つにする

再起動時に特別な復旧プログラムを別途動かすより、通常起動がstateを読み、未完了なら復帰へ進む入口を一つにします。各遷移は、同じ処理をもう一度実行しても結果を壊さないようにします。

たとえば`Restoring`で対象がすでに元のproxy値なら、成功として次へ進めます。逆に、現在値が保存値とも自分が設定した値とも違う場合は、第三者による変更の可能性があります。その場で上書きせず、人間の判断が必要な競合として止めるべきです。

state fileの原子的更新と多重実行の衝突を避ける

state fileを直接上書きしている途中で停止すると、次回起動が読めない中途半端な内容になる恐れがあります。一般的な対策は、同一ファイルシステム上の一時ファイルへ完全な内容を書き、必要な同期を行ったうえでrenameする方法です。ただし、ファイルシステムやOSの保証範囲は実装環境で確認してください。

同時に、プロセスロックも必要です。二つのCLIが同じ対象を操作すると、一方が保存した元の状態を、もう一方が変更後の状態として保存するかもしれません。ロック取得に失敗した実行は待ち続けるより、明確な終了コードで停止し、スケジューラ側で重複を把握できるようにします。

途中停止を検知して原状復帰を優先する

未完了stateを見つけたら、新しい証明書更新を始める前に復帰を優先します。ここには使いやすさとの摩擦があります。期限が近いからすぐ再試行したい場面でも、古い操作の後始末を先に行う必要があります。けれど、二つの操作を重ねれば、どの保存値が正しい戻り先か分からなくなります。

復帰にも失敗した場合、stateを`Completed`にして消してはいけません。`Restoring`のまま、最終エラー、試行回数、次の手動操作に必要な対象情報を残します。そして自動再試行には上限を設けます。認証失敗や競合を無限に叩くことは、復旧ではありません。

DNS proxyを安全にOFF・ONする更新手順

じぴこが制御盤のスイッチを操作し、事前チェックから観測、安全・タイムアウト分岐を経てDNSプロキシを元へ戻すU字経路を示している。

実際のDNS操作では、変更前の照合と変更後の再観測を分けます。APIが成功応答を返したことだけで、あなたが意図したレコードが期待値になったとは扱いません。対象を読み直し、状態が一致して初めて次へ進みます。

現在値が期待どおりか変更直前に照合する

切替直前に、ゾーン、レコード名、種別、内容、proxy値を取得します。事前の設定ファイルと違えば停止します。IP変更など正当な差分かもしれませんが、自動化がその理由を推測して上書きするべきではありません。

また、複数対象を扱うなら、変更前に全件を読み、全件が事前条件を満たすことを確認してから一件目を変更します。一件ずつ検証と変更を交互に行うと、後半の不一致が見つかった時点で前半だけ変更済みになります。それでも部分成功は起こり得るため、各対象の成功をstateへ逐次記録し、復帰対象を特定できるようにします。

対象レコードだけをDNS onlyへ切り替える

変更はallowlistの対象だけに限定し、`proxied=false`以外の属性を不用意に書き換えません。更新APIがレコード全体を送る形式なら、直前に取得した値を基に要求を組み立て、古い設定の上書きを避けます。APIの具体的なエンドポイントや必須項目は、利用時点の公式仕様で確認が必要です。

一時的にproxyを外すと、CDNやWAFなどproxy経由の機能を通らない時間が生まれます。これは「数分なら無害」とは限りません。オリジンを直接公開してよい構成か、アクセス制御や負荷の条件はどうか、切替可能な時間帯はいつかを事前に決めてください。許容できないなら、この方式そのものが不採用です。

伝播待ちを固定sleepではなく再観測で扱う

固定秒数のsleepは実装が簡単ですが、短すぎれば誤判定し、長すぎればproxy解除時間を延ばします。待機は締切を持つポーリングとして扱い、DNSの観測結果とTLS接続結果を分けて記録します。毎回同じ短い間隔で叩くのではなく、上限付きの間隔調整も検討できます。

ただし、外部DNS観測で期待値が見えたことは、すべての利用者から同じ状態が見えることを保証しません。ここでも成功条件を過大にしないことが大切です。CLIは「指定した観測点で条件を満たした」と記録し、全世界の伝播完了とは書かないようにします。

成功でも失敗でも保存済み状態へ戻す

更新を確認できた場合だけでなく、タイムアウト、TLSエラー、APIエラーの場合も`Restoring`へ進みます。復帰時は、開始時に保存したproxy値を使います。すべてを機械的に`true`へするのではありません。

復帰APIが失敗したら、その失敗を元の更新失敗より高い優先度で通知します。更新できなかっただけなら次回へ持ち越せますが、proxy状態が戻っていない場合は公開経路そのものが想定外だからです。ログと終了コードでも、`update_not_observed`と`restore_failed`を区別すると、対応順を決めやすくなります。

部分成功を終了結果へまとめる

API変更、証明書観測、proxy復帰を別々に記録したうえで、CLIの最終結果は一つに畳みます。実装時の最小マトリクスは次のように置けます。

終了結果観測できた状態通知優先度次のアクション
completed更新を観測し、保存済みproxy値への復帰も確認した通常実行記録を完了させる
update_not_observed締切まで更新は見えなかったが、proxy復帰は確認した警告製品側の更新条件やログを確認し、再試行時期を決める
restore_failed更新観測の成否にかかわらず、保存済みproxy値への復帰を確認できない最優先定期実行を止め、手動で現在値と保存値を照合して復帰する
conflict_detected現在値が保存値とも、この実行が設定した値とも一致しない高上書きせず、第三者変更の有無を確認する
precondition_failed外部変更前の照合、token取得、ロック取得などで止まった警告DNSは変更せず、前提を直してcheckからやり直す

大切なのは、証明書更新の成否だけでプロセス全体を成功・失敗に分けないことです。更新が見えなくても戻れていれば公開経路は復旧済みです。反対に、証明書が更新されていても戻せなければ、運用上は最優先の障害です。

SNI付きTLS観測でオリジン証明書の更新を判定する

ネットワークと地球の二つの経路が中央の施錠証明書へ入り、虫眼鏡を持つじぴこがSNI付きTLS観測の比較カードを確認している。

証明書の更新確認では、接続先と検証したいホスト名を分離します。接続先はオリジンIP、SNIは公開ホスト名です。公開ホスト名を通常どおり名前解決して接続すると、Cloudflareのエッジへ到達し、確認したいオリジン証明書ではなくCDN側の証明書を見る可能性があります。

CDN終端を避けてオリジンへ直接接続する

オリジンを直接観測するには、接続先IPを明示しつつ、TLS ClientHelloでは対象ホスト名をSNIとして送ります。証明書のホスト名検証も、その公開ホスト名に対して行います。単にIPアドレスへHTTPS接続するだけでは、名前ベースの仮想ホストが別の証明書を返す可能性があります。

この観測は外部から見える結果を確かめるものです。レンタルサーバー管理画面の表示と食い違う場合でも、どちらかを即座に真と決めず、接続先IP、SNI、取得時刻、証明書識別情報を並べて調べられるようにします。

接続先IPとSNIのホスト名を分離する

Rust実装では、TCP接続先を`192.0.2.10:443`のようなドキュメント用例で表し、TLSのserver nameを`www.example.com`として別に渡す構造にします。利用するTLSライブラリのAPIや既定の検証動作はバージョンにより確認が必要です。証明書検証を無効化して「取得だけ」行う実装は、成功条件を弱めるため避けます。

また、オリジンIPを設定ファイルへ置く場合は、DNSレコードの現在値との一致を切替前に確認します。サーバー移転後に古いIPへ接続し続ければ、期限が変わらないという誤った観測を繰り返します。接続先の指定は、allowlistと同じく更新対象の境界です。

証明書の識別情報と有効期限を更新前後で比較する

更新前に、提示証明書の識別に使う情報と有効期間を保存します。更新後の判定は、単に「TLS接続できた」では不十分です。更新前とは異なる証明書であり、対象ホスト名の検証に通り、有効期限が期待する方向へ進んでいることを組み合わせます。

どの識別情報を採用するかは、証明書チェーンとライブラリの扱いを踏まえて決めます。少なくともleaf証明書を対象にし、シリアル番号だけ、期限だけ、と単一項目へ寄せすぎないほうが確認しやすいでしょう。ただし「何日延びれば成功」のような製品固有の値は、実測や仕様なしに固定しません。

タイムアウトと不一致を成功扱いにしない

TLS接続のタイムアウト、名前不一致、信頼検証失敗、更新前と同じ証明書は、いずれも更新成功の証拠ではありません。観測を継続できる一時的エラーと、即時停止すべき設定不一致を分類します。たとえば一度の接続タイムアウトは締切まで再試行できますが、SNIの設定値がallowlistと一致しないなら再試行しても直りません。

ここで未解決の問いが残ります。レンタルサーバー側の更新が始まる正確な条件と時間は何か。これはRustガードの推測で埋めず、製品仕様、サーバーログ、自然発生した更新周期の試験で確かめる項目です。分からないままでも、締切を超えたらproxyを戻し、未確認として通知する安全な挙動は定義できます。

定期実行と未実証範囲を含む運用上の判断基準

四段の導入ラダーに観測・監督・自動化・日程の記号が並び、じぴこが警告旗を立てて未実証範囲と運用判断を確認している。

CLIが一度手動で動いただけでは、更新運用が完成したとは言えません。定期実行、多重起動防止、ログ、通知、手動復旧の入口まで含めて初めて、長期運用へ載せられます。同時に、未実証の全周期を「自動化済み」と呼ばない慎重さも必要です。

スケジューラ起動とロックで多重実行を防ぐ

macOSのスケジューラから起動する場合、対話シェルと環境変数、PATH、Keychainへのアクセス条件が異なる可能性を前提にします。CLIや設定のパスは明示し、token本体を環境変数へ常設しません。実行前のdry-runまたはread-only checkを用意すると、権限切れや対象差分をDNS変更前に検出できます。

スケジュール間隔は、証明書の期限だけでなく、最大実行時間と重ならないように決めます。それでも前回処理が残る可能性があるため、ファイルロックなどで単一実行を保証します。ロック所有者のPIDだけで生存を決めると再利用の問題があるため、開始時刻や実行IDを併せて診断できる形が望まれます。

構造化ログから手動復旧へ切り替える条件を決める

ログには、tokenや実識別子を出さず、実行ID、論理対象、状態遷移、観測時刻、結果分類を残します。文章だけのログより、後から「proxyを外したが戻せなかった実行」を抽出できる構造化形式が役立ちます。

手動復旧へ切り替える条件も先に定義します。復帰APIの連続失敗、保存値と現在値の第三者競合、state fileの破損、対象レコードの消失などです。このときCLIは、無理に正常状態を演出せず、変更を止め、必要な確認項目を表示します。自動化の価値は、人間を完全に排除することではなく、人間が判断すべき地点を狭くすることにあります。

自然発生したOFF・更新・ONの全周期は未実証と明示する

テスト用の証明書差し替えや手動更新で各部を確かめても、自然な更新時期にレンタルサーバーが期待どおり動くとはまだ言い切れません。自動OFF・サーバー側更新・自動ONの自然発生した全周期は未実証です。この表示は弱点ではなく、運用判断に必要な情報です。

初回導入では、read-only観測、手動切替を伴う監視付き試験、自動復帰まで含む限定運用、定期実行という順に段階を分けられます。各段階で、何を観測できたか、どの条件が推測のままかを更新します。自然更新を一度通過した後も、一回の成功だけで製品仕様を一般化しません。

小さな検証から本番導入へ進む判断項目

Rustガード採用後の本番投入条件

本番へ進む前に、少なくとも次を確認します。

  • 対象ゾーンとレコードがallowlistで一意に決まる。
  • tokenが必要最小限のゾーン・DNS編集権限に限定されている。
  • token取得失敗時はDNSへ書き込まない。
  • 変更前stateが原子的に保存され、再起動時に読める。
  • 部分成功と途中停止から、保存済みの元状態へ戻せる。
  • オリジンIPとSNIを分離したTLS観測ができる。
  • 更新前後の証明書を複数の根拠で比較できる。
  • タイムアウト時にもproxyを戻し、復帰失敗を高優先度で通知できる。
  • スケジューラ環境でKeychain、設定、ロックが期待どおり動く。
  • proxy解除中のセキュリティと負荷の影響を受け入れられる。

一項目でも曖昧なら、すぐ全面自動化する必要はありません。DNS onlyを常用する、更新時だけ手動チェックリストで切り替える、proxy切替を不要にする構成へ見直す、といった代替案があります。Rustで書けることと、自動化すべきことは同じではありません。

付録

この記事で紹介したCloudflare SSL更新ガードのRust CLIは、公開用のソースコード一式をGitHubで確認できます。実運用環境とは連動しない独立したスナップショットで、設定例には実ドメイン、実IP、API token、Cloudflare IDなどの機密情報を含めていません。

項目公開内容
リポジトリcloudflare-ssl-renewal-guard
実装言語Rust
公開バージョンv1.0.0
ライセンスMIT License
公開形態記事に対応したfrozen reference snapshot
主な収録内容Rustソースコード、README、設定例、テスト、Cargo構成一式
利用前に確認してください

このリポジトリは継続的な保守やサポートを約束するものではありません。実環境へ導入する場合はREADMEを読み、対象のゾーン、DNSレコード、オリジンIP、SNIホスト名を自分の環境に合わせて設定してください。

まとめ

Cloudflare配下でSSL更新が止まったように見えたら、まずCDN側とオリジン側の観測を分けます。Rustガードの責務は証明書発行ではなく、対象限定、変更前の先行記録、DNS proxyの一時切替、SNI付き観測、保存済み状態への復帰です。

採用前には、proxy機能の必要性、解除中の影響、戻り先の一意性を照合してください。製品固有の更新条件と自然発生したOFF・更新・ONの全周期は未実証です。read-only確認と監視付き試験から始め、更新未観測と復帰失敗を分けて扱えることを確かめてから、自動化の範囲を広げます。

FAQ

導入前に迷いやすい境界だけを確認します。

Q. Cloudflareのproxyを常時OFFにすれば、このガードは不要ですか?

proxy経由の機能が不要で、オリジン直接公開を受け入れられるなら、常時DNS onlyは一時切替ガードを使わない有力な選択肢です。

Q. Rust CLIがレンタルサーバーの証明書を更新するのですか?

いいえ。証明書更新はレンタルサーバー側、Rust CLIはDNS切替、待機、SNI付き観測、復帰を担当します。

Q. 公開URLをブラウザで確認するだけでは不十分ですか?

proxy有効時はCloudflare側の証明書を見ている可能性があります。オリジン証明書は、接続先をオリジンIP、SNIを公開ホスト名として直接観測します。

Q. 更新を確認できなかった場合、proxyはOFFのままにしますか?

いいえ。締切後も保存済み状態へ戻します。復帰を確認できない場合はrestore_failedとして定期実行を止め、最優先で手動対応します。

Q. API tokenを設定ファイルへ置いてもよいですか?

推奨しません。tokenはmacOS Keychainから実行時に取得し、Cloudflare側の権限も対象ゾーンと必要操作へ限定します。

シェアしてくれると喜びます
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

makotoのアバター makoto Blogger&YouTuber

サーバー管理者として17年ほど仕事でサーバー触ってました。
www,mail,dns,sql各鯖をすべてFreeBSDで運用してましたが現世ではかなりレアなタイプになるみたいですね笑

viやシェルスクリプトとかperlとかgccとかFreeBSDとか実はbashよりtcshが好きとか時々寝ぼけるのは
その名残でしょう。

今まで縁の下の力持ち的な他人のためにプログラムを書き他人のためにサーバー構築し他人のためにWEBサイトを創る的な世界から
自分の好きなことに集中できる環境は実に気持ち良いですね。
現役は引退済みなので難しいことはやりませんしやれません。

現在 ほぼ自由人。

contents