一台のクライアントのアクセスを一時停止することと、恒久的に削除することは、どちらかを取り消そうとするまで同じ作業に見える。証明書の失効はサーバーが最終的なものとして扱う記録を書き込み、それを取り消すには発行元のデータベースを手作業で編集するしかない。稼働中のトンネルを落とすのは信頼に何も影響しない管理コマンドだ。レバーは、要求がどれほど緊急に見えるかではなく、鍵がまだ信頼できるかで選ぶ。

思い込み:失効と停止は同じもの

請負業者を一週間停止するとき、反射的に失効を選びがちだ。失効は何かをオフにするように聞こえるからだ。整って見え、十秒で終わり、戻る道はデータベース編集だけになる。この反射は理解できるが、ドアを閉めるというよりパスポートを焼くことに近い。停止した端末と漏えいした鍵には別の対処が必要であり、安く取り消せるのは片方だけだ。

違いは人ではなく資格情報にある。正しい手元にある鍵は、信頼を失わずに停止し再開できる。コピーされた可能性のある鍵は決して復元すべきではない。復元は、コピーが存在しないと主張することになるからだ。

どちらのシステムに触れる前にも、答えを書き留める。鍵が露出したかどうかを言えないなら、保守的な答えは交換だ。

管理インターフェースで稼働中のセッションを落とす

OpenVPN は、セッションを一覧し、コモンネームで一つを終了できる管理インターフェースを提供する。localhostで有効にし、公開インターフェースから離し、セッションを今すぐ消す必要があるときに終了コマンドを送る。コマンドが閉じるのはそのトンネルだけで、他のクライアントは動き続ける。

これは進行中の問題への対応であり、停止ポリシーではない。同じプロファイルは一分で再接続できる。資格情報に何も変わっていないからだ。セッションを落とす必要があり、恒久的な判断がまだ続いているときに使う。

終了する前にセッションが接続されていたことを確認する。一度も接続していないクライアントを追いかけないためだ。下の状態コマンドは現在接続中のものを出力する。

printf 'status 2\nquit\n' | nc 127.0.0.1 7506
printf 'kill kiosk-14\nquit\n' | nc 127.0.0.1 7506

終了コマンドがそのようなクライアントは無いと報告したなら、証明書のコモンネームは入力した文字列ではない。プロファイルのファイル名ではなく状態出力を読むこと。

CRLが実際に制御しているもの

サーバーは証明書に保存された失効フラグを見るのではない。crl-verifyで指定された証明書失効リストを新しい接続のたびに読み、そのリストにシリアル番号がある証明書を拒否する。そこから二つの帰結が生じ、どちらも運用上重要だ。ファイルはサーバーが権限を落とすアカウントから読める必要があり、すでに接続しているクライアントは再接続するまで動き続ける。

二つ目の帰結はインシデントの最中に人を驚かせる。証明書を失効しても、その証明書を使っているトンネルは閉じない。セッションを直ちに終わらせる必要があるなら、失効と管理コマンドによる終了を組み合わせ、二つの操作を手順書の別のステップとして扱う。

コピーした最後のファイルの時刻を信じるのではなく、リストを見てサーバーが実際に何を強制するかを確認する。重要なのはシリアル番号の件数だ。

openssl crl -in /etc/openvpn/server/crl.pem -noout -text | sed -n '1,14p'
ls -l /etc/openvpn/server/crl.pem

エントリの無いリストと正しいエントリのあるリストは、ディレクトリ一覧では同じに見える。だからシリアル番号の件数を出力する価値がある。パーミッションの行も同じくらい重要で、デーモンが読めないファイルは拒否されたクライアントのように見える障害を生む。

記録を復元する、慎重に

証明書を復元するとは、発行データベースの記録を反転させ、新しいリストを公開することだ。easy-rsa 3 はそのデータベースを PKI ディレクトリ配下の index.txt として保持するが、古い配置では別の構成を使っていた。行は状態文字と有効期限で始まる。失効した行は、シリアル番号、証明書のファイル名、識別名に進む前に失効のタイムスタンプを挿入する。行を戻すとは、そのタイムスタンプを削除して文字を反転させ、他はそのままにすることだ。

まずファイルをバックアップし、テキストエディタで打つのではなくスクリプトで編集する。行が持つフィールド数を変える打ち間違いは、その後追加されるすべての証明書の発行を壊す。だから変更を自動化し、触る前にファイルをコピーする。

同じ名前の二回目の失効もよく失敗する。一回目の実行が、その証明書と鍵をツールが期待するディレクトリの外へ移してしまったからだ。するとツールは失効を求められたものを見つけられず、エラーメッセージも原因を常に明確に示すわけではない。

cp /etc/easy-rsa/pki/index.txt /root/index.txt.$(date +%F-%H%M)
grep -c '^R' /etc/easy-rsa/pki/index.txt
easyrsa gen-crl

新しいリストを公開し、所有権を直し、サーバーに読ませる。多くの導入では再読み込みで足りるが、確認は新しい接続から取る必要がある。既存のセッションは何も再確認していないからだ。

その案件にどのレバーがふさわしいかを確認する

手順書全体を決める一つの問いを立てる。秘密鍵は今も本来あるべき場所だけにあるか。そうなら、セッションの終了と文書化された一時停止で案件は収まり、恒久的な記録は残らない。そうでないなら、唯一安全な答えは新しい鍵で、古い証明書は失効したままにする。

次に、構成の差分ではなく接続の試行で結果を検証する。差分は意図を教え、接続はサーバーが何を強制しているかを教える。

openssl crl -in /etc/openvpn/server/crl.pem -noout -text | grep -c 'Serial Number'
printf 'status 2\nquit\n' | nc 127.0.0.1 7506

復元したクライアントが接続し状態出力に現れれば、記録の編集は機能した。失効したクライアントが拒否されログが CRL を名指しすれば、失効は機能した。その間の何かはパーミッションか再読み込みの問題であり、次の要求が来る前に直す価値がある。

関連記事