CA なしの OpenVPN — peer-fingerprint がピアを検証する仕組み
指紋が固定するのは 1 枚の証明書であり、共有の秘密ではない。ダイジェストが何を覆うのか、各側が許可リストをどう作るのか、更新に何がかかるのかを追う。
小規模な導入が指紋に手を伸ばすのは、認証局が誰も運用する時間を持たないインフラに見えるからだ。その直感は妥当だが、代わりに人が選ぶ近道——データチャネルの秘密を両端にコピーする——は妥当ではない。peer-fingerprint の構成は TLS の構造をそのまま保つ——各側は今も自分の秘密鍵と証明書を持ち、各側は今も相手を検証する。変わるのは検証をどこに固定するかであり、信頼できる発行者から、固定されたダイジェストの短いリストへ移る。ダイジェストは名前ではなく証明書全体を覆うため、更新は本当に時間を要する作業になる。
指紋が実際に約束するもの
証明書の指紋は DER で符号化された証明書全体のダイジェストであり、公開鍵のものでもサブジェクト名のものでもない。したがって、名前が同一で鍵も同じでも有効期限が違う二つの証明書は、異なる指紋を持つ。それが固定を精密にし、同時に更新を厄介にする。固定は鍵一般を信頼することを意味できない——それが一つの成果物を、その生涯の一瞬において名指しするからだ。ダイジェストは誰でも知り得るが、知ること自体は何も証明しない。
openssl x509 -in server.crt -noout -fingerprint -sha256
openssl x509 -in client-07.crt -noout -fingerprint -sha256
対応する秘密鍵を所持していることが、ピアがその証明書を持つ証拠であり、ダイジェストは相手側がそもそもその証明書を期待していたかを決める手段だ。この分離があるからこそ、この設計は旧来の共有秘密の取り決めではない——共有鍵は同じバイトを知る誰でも認証するが、固定は一つの証明書を認証する。別の端末は同じ秘密の複製ではなく自分の項目を得る——これはファイル上の小さな違いであり、被害範囲では大きな違いだ。
対応表は相互的であり、対称ではない
各端点には、自分の秘密鍵と証明書、そしてどのピア証明書を受け入れるかという宣言の二つが必要である。クライアントではその宣言がサーバー証明書のダイジェストであり、サーバーでは登録済みクライアントのダイジェストのリストである。Community 2.6 向けの小規模構成の公式例が peer-fingerprint を使った配置を示しており、実運用にこの信頼モデルを選ぶ前に読む価値がある。二つの方向は一つのリストを反転したものではなく、同じ瞬間に失敗するわけでもない。
| Verification direction | What is pinned | What never leaves that endpoint | How a wrong pin shows up |
|---|---|---|---|
| Client checks the server | One digest, for the server certificate | The server private key | The client aborts during the TLS handshake |
| Server checks the client | A digest per enrolled client | Each client private key | The server rejects the peer before a tunnel exists |
端末を追加するには、サーバー側の許可リストを編集するだけでよく、既存クライアントの秘密素材をコピーする必要も、サーバー鍵をどこかへ移す必要もない。二つの方向は独立に失敗するので、切り分けにも役立つ——ハンドシェイクを閉じた側が、期待を満たされなかった側である。クライアントがサーバーを拒否するなら、ダイジェストが提示された証明書と一致していない。サーバーがクライアントを拒否するなら、そのクライアントのダイジェストがリストにないか、登録されたものとは別の証明書を提示している。
登録 — 新しい端末はどう加わるか
新しい端末の鍵と証明書は秘密鍵が置かれる場所で生成し、その後は公開の成果物だけを、それを固定する必要のある側へ移す。想定されるダイジェストは、すでに信頼している経路で受け渡し、見慣れた名ではなく証明書全体を比較する。証明書と同じ未検証のダウンロードからコピーしたダイジェストは、二つのファイルが互いに一致することしか証明しない。これは同一性の確認ではなく整合性の確認であり、この設計が最も静かに弱まる経路である。
サーバー側では、ダイジェストの追加は通常の管理手段を通す構成変更であり、その後に再接続が続く。新しい端末が接続でき、ダイジェストを削除した端末が接続できなくなったことを確認する。肯定ケースだけを試すテストは許可リストを未検証のまま残す——半年後にあなたが置かれているのは、まさにその状態だ。
更新が高くつく瞬間である
証明書は必ず期限切れになり、鍵と名前が同じままでも新しい証明書は新しいダイジェストを持つ。発行者のもとでは、更新はその発行者を信頼するピアにとって透過的だ——固定のもとでは、証明書を固定するすべてのピアにとって登録の出来事になる。
期限日の後ではなく前に、順序を計画する。新しいダイジェストを検証する側へ配り、変更を適用し、その後にだけ端点の証明書を差し替える。この順序なら、どちらの証明書が現行かを両側が取り違える窓が生じない。リスト自体も見直し続けるべきだ——固定されたリストには、何か月も前に引退した端末の項目がたまっていく。古い項目はどれも、鍵が生き残れば今も認証される同一性であり、一つ消すのは片付けではなく安全保障の行為である。
信頼の対応表が決めないこと
認証は一つの層にすぎず、固定された対応表はアドレスプール、押し出されるルート、経路の MTU、ファイアウォールの方針については何も決めない。それらは別個の構成であり、別個の故障の仕方を持つ。認証は通るが通信が一切通らないトンネルは、決して立ち上がらないトンネルとは別の調査対象だ。
対応表は無限に拡張できるわけでもなく、より大きな導入は発行者ベースの PKI に行き着く。その好みは暗号ではなく運用の話であり、端末群が育つにつれて各ピアでリストを編集するより、一つの機関を通して同一性を追加・引退させるほうが安いからだ。クライアント実装がこの選択肢を支えない場合の答えは、そのクライアントが文書化する互換の信頼モデルか、支援される代替である。検証の警告を抑えてもその支援は増えず、この設計の価値は検証が今も行われることにある。