「社内トラフィックは VPN を通る」とだけ書いたスプリットトンネルのメモは、一つの判断を文書化し、残り三つを暗黙にしたことになる。パケット層では経路表が宛先アドレスでインターフェースを選び、名前層ではリゾルバーがクエリでサーバーを選ぶ。この二つの選択は独立して行われる。だから正しく文書化されたプレフィックスでも、内部名を公開アドレスに解決してしまう端末や、経路が完璧に見えるのに何も解決しない端末が生まれる。

二つの選択子、一つの接続試行

アプリケーションが files.corp.example に接続すると、パケットが存在する前に二回の照会が起きる。リゾルバーは検索ドメイン、リンクごとのルーティングドメイン、または明示的なプロファイル規則を使って、どのサーバーがクエリを受け取るかを決める。次に経路表が、宛先との最長プレフィックス一致を使って、得られたアドレスの行き先を決める。

この二つの判断をつなぐものは何もない。プライベートゾーンが 10.20.4.11 で答え、経路表が 10.20.0.0/16 をトンネルへ送っていれば、接続は成立する。どちらか一方を反転させると、もう一方に似た形で失敗する。DNS ポリシーが正しく経路が欠けていれば、名前は解決できてタイムアウトになる。経路が正しく公開リゾルバーを使っていれば、きれいに間違った場所へ経路付けられるアドレスが返る。

スプリットホライズン DNS はこれをさらに際立たせる。同じ名前が、内部リゾルバーからはプライベートアドレスを、インターネット向けリゾルバーからは公開アドレスを返すことがある。したがってプレフィックスだけを記録したメモでは、クライアントが実際にどちらを使うかを予測できない。リゾルバーの経路を書かなければ、アドレスは推測にすぎない。

ルートだけのメモが落とすもの

ルートだけの記録は、スプリットトンネルで最も多い障害、つまり名前に使えない。利用者は イントラネットが読み込めない と報告し、トンネルは上がっており、プレフィックスも存在する。チケットを引き継いだ人には、どのリゾルバーが答えるべきだったかの記録も、誤った検索サフィックスとブロックされたサーバーを区別する手段も、比較する基準もない。

二つ目の欠落は除外リストだ。スプリットトンネルの設計は、トンネルに入るものと同じくらい、外に残るものによって決まる。除外された範囲はたいてい最初に壊れる。より具体的なローカルルートやクライアント側のフィルターが、プロファイルに触れずに意図を上書きできるからだ。

三つ目はトンネルの向こう側だ。クライアントのルートはパケットを回線に載せるだけであり、ゲートウェイはそれを転送し、戻り経路を持たなければならない。strongSwan の 転送の手引き は、ゲートウェイ上の転送とファイアウォールの規則が機能の一部であり、前提にしてよい配管作業ではないことを思い出させる。

メモを実行可能にするフィールド

フローごとに一行、そしてフローとは宛先と名前と所有者の組み合わせだ。実際の障害に耐えるのは次のフィールドである。

FieldWhy it has to be written down
Destination prefix and address familyThe route is per family; an IPv4 note says nothing about a AAAA answer
Selected tunnel or direct pathNames the decision, not the symptom
Private namespace and resolverWhich domain, which server, which search suffix
Public name pathConfirms public names do not accidentally use the private resolver
Where the rule livesClient profile, server push, OS policy or management tool
Owner and review dateNamespaces change owner more often than routes change

「どこに住んでいるか」の行こそ、人が飛ばす行だ。サーバーが押し込むプレフィックス、クライアントプロファイルが追加するルート、シェルに打ち込んだルートは、誰が変更できるか、再接続後も残るかが異なる。このフィールドがなければ、次の技術者は三つのどれを見ているのか分からない。

ルールごとに一つのテスト、トンネルごとではない

各行には、ルートを試すものと名前を試すものという二つの無害な確認が要る。ルートについては、トンネルを必ず横切るものを要求し、到達性ではなく経路を確認する。たとえば tracert -d 10.20.4.11 を Windows で、または ip route get 10.20.4.11 を Linux で使い、名前についてはプライベート名を照会してどのサーバーが答えたかを記録し、メモが示すリゾルバーに対して resolvectl query files.corp.example または dig files.corp.example を使う。

否定的な期待も加える。トンネルが落ちているときにどうなるべきか、というものだ。VPN なしのブロックが有効な Android や iOS 端末上の除外アプリは、まったく接続できないかもしれない。デスクトップクライアントなら、トンネルが再接続している間も同じアプリが動き続けることがある。意図した答えを記録しておけば、「VPN がアプリを壊した」は検証できる主張になる。

記録は構成と一緒に保つ

メモはそれが説明するプロファイルの隣に置き、そのプロファイルと同じ版で管理する。誰も更新しない wiki の文書は噂にすぎず、押し込まれるルートやリゾルバーポリシーと同じリポジトリにある表なら同じ変更の中でレビューできる。

誰も更新しない wiki の文書は噂にすぎず、押し込まれるルートやリゾルバーポリシーと同じリポジトリにある表なら同じ変更の中でレビューできる。何かが壊れたら、別の例外を足す前に、適用されているルートと実際に効いているリゾルバーを記録と比べ、よくある三つの原因のうち二つ、古い名前空間の規則とアプリのポリシー外の補助プロセスは VPN の障害そっくりに見え、メモがプレフィックスだけを列挙していれば見えない。

メモを完成と呼ぶ前に、どのプレフィックスがどのアドレスファミリー向けか、どのプライベート名前空間をどのリゾルバーが答えるか、どの名前が意図的に公開のままか、各ルールはどこで構成され誰が所有するか、そしてルートを証明する一つのコマンドと名前を証明する一つのコマンドに書面で答えているかを確認する。どれかが欠けていれば、次の障害はゼロから診断されることになる。

関連記事