VPN を独自の名前空間の中で起動し、そこであるアプリケーションを動かしても、通信はホストのルートから外へ出てしまう。壊れているわけではない:名前空間が支配するのは、そこに入るプロセスだけであり、漏えいは決して入らなかったプロセスから生じた。インターフェース、アドレス、ルート、ファイアウォール規則、ソケットが名前空間の所有物である。ファイル、アカウント、稼働中のサービスはそうではなく、驚きの多くはここから来る。

名前空間が所有するもの、そしてソケットが生まれる場所

インターフェースを名前空間へ移すとホストはそれを失い、内側にトンネルを作ればホストからは見えない。これが名前空間で限定された VPN の仕組みである:デバイスとそれを指すルートは、選ばれたプロセスだけが届く文脈に住む。ホストは既定のルートを保つので、外側のすべては手を付けられないまま残る。

WireGuard では現在の所在と同じくらい作成時の文脈が重要である:UDP ソケットはインターフェースが作られた名前空間に束縛され、デバイスが他所へ移った後でもそのままである。暗号化されたパケットがどのルーティングテーブルに従うかは、その生まれた場所で決まる。ソケットがピアへのルートを持たない文脈に留まれば、インターフェースは問い合わせを行い、ハンドシェイクは決して外へ出ない。まず生まれた場所を、次に現在の所在を確認すべきである。

名前空間が分離しないもの

ここではファイルシステム、アカウント、秘密のいずれも分離されない、そして名前空間を作ったからといって、そうした境界が現れるわけでもない。リゾルバの設定はファイルであり、内側のプロセスもホストのリゾルバを指す設定を読み、それが意味するルートで問い合わせを外へ送れる。要求を稼働中のブローカーやシステムサービスに渡すアプリケーションは、まったく別の文脈のプロセスを関与させる。どちらも、この境界が答えることを想定していなかった要求である。

対処は、/etc/netns/<name>/resolv.conf に名前空間ごとのファイルを置くこと、または用意したファイルを bind mount して内側から到達できるリゾルバを指すことである。ホストの /etc/resolv.conf を編集しても、名前空間が見るものは何も変わらない。

プロセスの配置こそが制御である

得られる分離は、内側で起動したプロセスの分離そのものである。名前空間の exec ヘルパー経由で起動したシェルと、それが生むものすべては内側に留まり、ホストのセッションから起動したデスクトップアプリケーションは留まらない。文脈を作り、デバイスを移し、それから両側を証明する。

ip netns add vpn1
ip link set wg0 netns vpn1
ip netns exec vpn1 wg show
wg show

名前空間の内側から最初の wg show を、ホストから二番目を読み、どちらか一方だけがピアを一覧することを期待する。両方が一覧するならデバイスは一度も動いておらず、どちらも一覧しないなら別の場所へ行っている。起動元がサービスマネージャーなら、編集すべきは unit ファイルであり、デスクトップのショートカットではない。名前空間の設定がない unit は、デーモンをホストの文脈で起動し、設計を静かに打ち消す。

アプリケーションの文脈から試す

アプリケーションが使うのと同じバイナリと同じユーザーで、名前空間の内側で検査を実行する。DNS と両方のアドレスファミリーを含めるべきである:考えもしなかった IPv6 ルートがよくある抜け道だからである。自分のシェルからの試験は、自分のシェルが分離されていることしか証明しない。

次にホストで同じ検査を実行し、答えが異なることを確認する:内側ではトンネルのアドレスと意図した出口、外側ではホストのルートとオフィスの出口、そしてリゾルバは名前で確かめる。名前空間はプロトコルではなく配備にちなんで名付け、後片付けのスクリプトが二つを取り違えないようにする。

転送、障害時の方針、後片付け

ゲートウェイなら、転送を意図的に有効にし、トンネルが落ちたときに何が起きるべきかを決める。名前空間は内側に導入したルートを遮らないため、ホストへの退避ルートを作ればそれが使われる。これは方針の決定であり、障害がそれを試す前に書き記しておくべきである。

名前空間の名前、インターフェース、設定の所有者を記録し、後片付けが配備の作ったものだけを削除するようにする。ロールバック中にホストのネットワークを初期化すると、後片付けが障害になる。また network_namespaces(7) のページは短く、その道を設計する前に読む価値がある。

関連記事