Certbot が更新の成功を報告し、ディスク上のファイルには新しい日付が付き、それでもクライアントは古い ID に出会います。SoftEther は SSL サーバー証明書をサーバーレベルの独自設定に保持しているため、リーフとその鍵をインポートするまで、ディスク上の新しいファイルでは何も変わりません。以下の手順は、一度の手動インポート、更新のたびに走るラッパー、そしてリスナーが実際に提示する内容を読む確認を扱います。どの手順も、次の更新後に比較できる証拠を残します。

更新されたファイルだけではサーバーの ID は変わらない

発行とデプロイは別々の出来事であり、失敗の仕方も別です。ACME クライアントはファイルをリネージディレクトリに書き込んで成功を記録しますが、SoftEther は設定に最後に読み込まれた証明書を使い続けます。したがって終了ステータスがゼロで示すのはファイルであってサービスではなく、誰も見ない限り両者は食い違ったままになります。インポートをデプロイ手順、更新をその入力として扱ってください。

この区別は、混乱しやすい半端な障害も説明します。古い証明書は有効期限まで動き続けるので、最初は何も壊れて見えません。期限が切れるとすべてのクライアントが一斉に失敗し、これはデプロイ手順の漏れではなく障害のように見えます。更新のたびに提示 ID を確認すれば、その驚きは日常的な観察になります。

リスナーに合ったチャレンジを選ぶ

証明書にはクライアントが検証するホスト名を入れる必要があるため、内部ラベルやアドレスではなく公開 DNS 名を使ってください。HTTP-01 はチャレンジを一瞬だけポート 80 で応答させる必要があり、これは一時的なリスナーであって SoftEther 自身のポートを動かす理由には決してなりません。DNS-01 はレコードで制御を証明し、受信 HTTP をまったく必要としません。

無人更新とは、一つのゾーンに限定した認証情報による自動 DNS 連携のことであり、二か月ごとに手で TXT レコードを打つことではありません。ワイルドカードはチャレンジの種類を変えるだけで、その自動化の必要性は変わりません。最初の証明書ができる前にこれを決めてください。後からチャレンジの種類を変えると、クライアント設定とファイアウォールの両方に触れることになります。

ここにいる間に成果物を整理しておきましょう。リーフと中間証明書は個別のファイルとしても、結合された 一つのチェーンファイルとしても届き、秘密鍵はまた別で、インポートに必要なのは対応する鍵を伴ったチェーンであり、公開証明書だけではありません。

サーバーレベルでチェーンをインポートする

証明書に触れる前にサーバー設定をバックアップし、管理インターフェースにまだ到達できることを確認してください。証明書はサーバーオブジェクトに属するため、Virtual Hub 管理者ではなくサーバー管理者として接続します。次にチェーンと鍵を絶対パスで、コマンドを実行するアカウントが読めるファイルから読み込みます。パスが絶対なのは、コマンドが vpncmd を実行したプロセスの作業ディレクトリから解決するためです:

ServerCertSet /LOADCERT:/etc/letsencrypt/live/vpn.example.com/fullchain.pem /LOADKEY:/etc/letsencrypt/live/vpn.example.com/privkey.pem

成功を前提にせずコマンドの出力を読み、渡したファイルを信じるのではなくチェーンを読み直してください。PEM バンドル全体をインポートするビルドと最初の証明書だけを取るビルドの境界は 一か所に文書化されていないため、目の前のビルドでは確認が決め手になります。リスナーが提示する証明書を数えてください。中間証明書のないリーフは、最初のブロックだけを消費したインポーターの特徴です。

インポートした鍵はサーバーを実行するアカウントだけが読めるようにし、管理パスワードはシェル履歴に残さないでください。この最初のインポートでは、パスワードをコマンド引数で渡さずプロンプトで入力します。この二つの習慣は費用がかからず、証明書デプロイが守るべきものを漏らす最も一般的な経路を取り除きます。最初のインポートの前に、事後ではなく行ってください。

フックするのはデプロイであり、更新ではない

手動インポートが動いたら、その手順をそのまま小さなラッパーに入れ、更新の成功後に ACME クライアントから呼び出します。Certbot はフック用に RENEWED_LINEAGE と RENEWED_DOMAINS を設定するので、複数の証明書を管理するマシンでもラッパーは一つの証明書に対して動作できます。ホスト上のすべての更新に反応するのではなく、リネージで絞り込んでください。ラッパーはサーバーの権限で動くデプロイコードなので、その立場の他のスクリプトと同じようにレビューしてください。

certbot renew --deploy-hook /usr/local/sbin/softether-deploy.sh

無人で動くフックには、専用の保護された認証情報ストアと、人に届く失敗シグナルが必要です。発行が成功してインポートが失敗したら、別の証明書を要求するのではなくインポートを再試行してください。二回目の要求は新しいファイルの陰にデプロイの欠陥を隠してしまいます。Certbot の更新ドキュメントにも、何も更新しない実行は正常な結果だと明記されており、両者を区別するのはフックです。

提示されたチェーンとファイルを比較する

読むべきはディスクではなくリスナーです。クライアントが使うサーバー名でクライアント向けポートに接続し、指紋と配信されたチェーンを、フックが入れたリネージと比較します。二つのコマンドで足り、最初のものは中間証明書が届くかどうかも示します:

openssl s_client -connect vpn.example.com:443 -servername vpn.example.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
openssl s_client -connect vpn.example.com:443 -servername vpn.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates -fingerprint -sha256

その後に新しいクライアント接続を行ってください。インポート前に確立したセッションは以前の ID で認証されており、新しい ID については何も証明しません。クライアントが古いリーフをピン留めしている場合、公開チェーンが有効でも協調した更新が必要で、その作業は更新と同じランブックに入ります。サービス前段のリバースプロキシが良い証明書を提示していても、VPN リスナーは古いものを持ったままかもしれません。だからこそ確認ではクライアントが実際に到達するポートを指定する必要があります。指紋を更新日と並べて記録し、次の比較が記憶ではなく確認になるようにしてください。

関連記事