再起動はキルスイッチを試す最も一般的な方法であり、最もよく嘘をつくテストでもあります。クライアントのチェックボックスはあくまで設定であり、重要なのはネットワークスタックが立ち上がる瞬間からトンネルが実際に確立される瞬間までの間に、何かがブロックを強制しているかどうかです。Windows ではそれは通常 Windows Filtering Platform のフィルター、Linux ではユニットが読み込むルールセット、macOS では pf のアンカー、Android ではシステム自身の常時接続とロックダウンのフラグです。こうした仕組みは再起動をまたぐと大きく挙動が変わり、プラットフォーム層のものだけが誰かがサインインできる前に存在しています。

再起動は三つの別々の問いを立てる

問いを分けてください。第一に、ポリシーは起動時に何かが読む形でディスク上に存在するのか。第二に、その読み手はトラフィックが出られる前に動くのか。第三に、ポリシーは気にしているトラフィック、つまりすべてのプロセス、両方のアドレスファミリー、そして DNS を覆っているのか。一つ目がはいで二つ目がいいえなら、人々が「再起動後に残らないキルスイッチ」と呼ぶ失敗がそのまま起こり、起動時に数秒間トラフィックが漏れて、誰かが調べるまで誰も気づきません。

層は分けて考えてください。保存されたファイアウォールのルールセット、起動時に始まるサービス、クライアントの設定は、寿命の異なる三つの別物です。設定が再起動を越えるのは、設定ファイルの中の値だからです。パケットを止めているのはルールだけで、そして ルールはそれを保持していたカーネルとともに消えます。

ブロックを復元する対象を見つける

Linux では netfilter のルールはカーネルの中に住んでいます。自分で仕組まない限り起動時に誰も書き戻しませんが、iptables-save と netfilter-persistent、そして nftables.service が /etc/nftables.conf を読み込む形、または自分のスクリプトを走らせる systemd ユニットを使えば自動化できます。実際に有効になっているものを確認し、思い出した設定に頼らないでください。

systemctl is-enabled nftables netfilter-persistent 2>/dev/null
sudo nft list ruleset
sudo iptables -S OUTPUT

起動から数秒後の空のルールセットが、タイトルの問いへの答えです。Windows ではクライアントサービスに起動方法を尋ね、トンネルが落ちている間にプラットフォームのフィルター状態を書き出してください。

Get-Service | Where-Object { $_.Name -match 'vpn' } | Select-Object Name, Status, StartType
netsh wfp show state

二つ目のコマンドは wfpstate.xml を現在のディレクトリに書き出します。クライアントが接続する前にすでに存在する、ブロック動作のフィルターを探してください。Base Filtering Engine は起動の早い段階で始まり、永続フラグ付きで追加されたフィルターを復元するので、クライアントのウィンドウが存在する前にトラフィックを止めるキルスイッチもあれば、そうでないものもあるのです。macOS では sudo pfctl -s info が pf が有効かを報告し、sudo pfctl -s rules が何が読み込まれているかを示し、VPN クライアントは通常、メインのルールセットではなくアンカーにルールを結びつけます。utun3 を名指すルールは、次の起動後に別のインターフェースを指すことがあり、その番号は実行時に割り当てられるからです。

ネットワークが上がってからトンネルが上がるまでの隙間を見る

危険な区間は再起動全体ではなく、その隙間です。インターフェースがある時点でリースとデフォルトルートを受け取り、VPN サービスは別の時点で始まり、トンネルはさらに遅れて認証しますが、ユーザーがサインインして資格情報エージェントが動いた後になって初めてということもあります。ブロックが最後に適用されると、隙間にあるものはすべて平文で外に出ます。

systemd のホストでは、推測せずに順序を読んでください。VPN とファイアウォールのユニットで実行するのは systemctl cat で、After= や Before= が network-pre.target と network-online.target に対してどう並ぶかを見てください。ファイアウォールユニットは network-online.target より後に並ぶとインターフェースに負け、network-pre.target に要求され、その前に並んだものが勝ちます。Windows では同等の問いはサービスのスタートアップの種類と依存関係で、Manual に設定されたサービスは起動時のプロセスを止められないからです。要件にサインイン前のトラフィックが含まれるなら、それを満たすのはデバイスレベルの仕組みだけで、Always On VPN のデバイストンネルがマシン証明書で認証してユーザーのログオン前に接続し、Microsoft はデバイストンネルの構成を文書化しています ユーザートンネルとは別に Microsoft が文書化しています。Android は別の道をとり、常時接続 VPN を強制し、それなしの接続をアプリのプロセスではなくシステムから遮断します。

テストを実行し、二つのタイムラインを比べる

再起動は 漏れても損のない宛先 を使って行い、状態は隙間の後ではなく隙間の間に記録してください。最もきれいな方法は、プラットフォームが許す限り早く起動し、デフォルトルートとファイアウォールの状態を記録し、短いタイムアウトで一度だけ要求を試み、ローカルファイルに一行を追記する oneshot ユニットやスケジュールタスクという起動時プローブです。その記録を journalctl -b -u やシステムログから得た VPN サービスの起動時刻と比べてください。

次に通常のテストとして、トンネルを上げ、接続を切り、直接のトラフィックが止まるかを見て、元に戻します。二つ目に合格して一つ目に失敗するのが普通の結果で、それは強制がクライアント側にあり、起動時ではなくクライアントの接続時に適用されていることを意味します。私が最後に監査したマシンでは、ブロックは NetworkManager のディスパッチャスクリプトから来ており、存在し、正しく、そしてインターフェースが上がる前の数秒間には役に立たないものでした。再起動を生き延びたという事実は、どの層がどの瞬間から生き延びたのかを言えて初めて意味を持つので、両方の結果を書き留めてください。

戻る道を断たずに隙間を閉じる

起動時にブロックがないなら、二つ目のクライアントルールを足すのではなく、強制を一段下の層へ移してください。ルールセットを保存し、ネットワークが使えるようになる前に並ぶユニットから読み込み、そのユニットが自分が守るべき DNS 参照やネットワークマウントを必要としないことを確かめます。範囲はタイミングと同じくらい重要で、ユーザー ID や cgroup に一致するルールは、すべてのシステムデーモンをブロックの外に置きますし、IPv4 だけを扱うルールセットは IPv6 と DNS を使えるままにします。

自分の戻る道を残してください。起動時にすべてを止め、そのあと認証できないマシンにはネットワークがまったくなく、直すべきはブロックを消すことではなく資格情報か順序です。ローカルコンソールの手順を文書化し、トンネルが本当に必要とするもの、たとえばサーバーのアドレスやトンネルができる前に使うリゾルバーには、狭く意図した例外を残します。

この仕組みを信じる前に、四つのことを順に確かめてください。ポリシーが起動時に読まれる場所に保存されている。その読み手がインターフェースの使用可能前に動く。ブロックが両方のアドレスファミリーと DNS を覆う。ブロックが有効なまま VPN が認証できる。四つすべてが成り立てば、再起動で何も変わりません。

関連記事