Mac VPN の永続ルート:networksetup か接続フックか
再接続のたびに打ち込むルートは方針ではない。プレフィックスが VPN サービス、一時的な試験、ネゴシエーション後にしか分からないアドレス用のフックのどれに属するかを決める。
接続のたびに打ち込むルートはその場を解決するが、一週間は解決しない。macOS では、永続的な選択は通常 VPN サービスに保存されたプレフィックスであり、サービスが接続のたびにそれを入れ直し、停止すると取り除くからだ。代替は試験用の一時的な route add と、ネゴシエーション後にしか分からないアドレス用の接続フックだ。
ルートがサービスに属するかセッションに属するかを決める
プレフィックスが何に依存するかを問う。ネイティブ VPN サービスの背後の固定プライベートネットワークなら、ルートはそのサービスに属する。接続ごとに変わるピアアドレスに依存するなら、フックや状態駆動のジョブの方が適した所有者かもしれない。次の五分間だけ経路を確認するなら、一時的なルートで足りる。
サービスに紐づくルートは最もきれいなライフサイクルを持つ。サービスとともに現れ、サービスが接続されていないときは消えるので、指し示すトンネルより長生きできない。その性質は、一行のコマンドの手軽さより価値がある。
セッションに紐づくルートはインターフェースに属し、その番号は予告なく変わる。再接続は前のセッションと違う utun 名をトンネルに与えることがあり、ハードコードされたコマンドは何も指さないルート、あるいはもっと悪く別のトンネルを指すルートになる。
現在のルートとサービスの状態を読む
システムが実際に使う名前から始める。ネットワーク設定のサービス名が以下のコマンドの識別子であり、現在のルート表は接続中のクライアントが何を入れたかを示す。トンネルが上がっている間に両方を読む。
scutil --nc list
networksetup -listnetworkserviceorder
netstat -rn | head -30
route get 10.20.5.5
その検索は、カーネルがこのアドレスに対して現時点でどのゲートウェイとインターフェースを選ぶかを示す。すでにトンネルを選んでいるならプレフィックスは覆われており、新しいルートは冗長だ。ローカルルーターを選ぶなら、トンネルにそのプレフィックスがない。
接続中にピアアドレスを控える。それがサービスに紐づくルートのゲートウェイ値になり、通常は netstat -rn でトンネルの既定ルートに対して表示される宛先だ。
networksetup でプレフィックスを保存する
networksetup ツールはネットワークサービスに追加ルートを結びつける。OfficeVPN という名前のサービスと、トンネルのピア経由で届くプライベートプレフィックスの場合、形式は次のとおりだ:
sudo networksetup -setadditionalroutes OfficeVPN 10.20.0.0 255.255.0.0 10.8.0.6
networksetup -getadditionalroutes OfficeVPN
サービス名の後の各三つ組が、宛先、そのネットマスク、ゲートウェイとしてのトンネルピアを与える。 一度の呼び出しで複数のプレフィックスを運べ、空の呼び出しは保存済みリストを消す。 取得側が検証の手順であり、リストが空なら、設定は思っているサービスに適用されていない。
ゲートウェイはトンネルのピアでなければならず、物理ルーターではない。ローカルゲートウェイを名指すルートはプライベートプレフィックスを物理インターフェースから外へ送り、これは手順を増やしただけの元の失敗だ。古いピアは、正しく見えるルートが決して通信を運ばない最も一般的な理由だ。古いメモからアドレスをコピーするのではなく、接続状態からピアを読み直す。
route add は試験にだけ使う
直接ルートは、プレフィックスを恒久化する前に機能を証明するのに役立つ。インターフェース形式ならゲートウェイの検索を避けられる:
sudo route -n add -net 10.20.0.0/16 -interface utun4
ルートは次の再接続で消え、今日存在するインターフェース番号に依存する。それは診断であって展開ではない。試験が通り、サービスに紐づくルートが通らないなら、まずゲートウェイ値を比べるべきであり、それが最もよく間違う項目だ。
分割動作を強制するために VPN の既定ルートを置き換える助言には注意する。フルトンネルを強制するクライアントは、再接続やポリシーの更新後にルートを入れ直すことがあるので、手作業の変更は静かに取り消される。少数のプライベートプレフィックスはルーティングの問題であり、フルトンネルのポリシーに逆らってすべてのインターネット通信をローカルに保つのはサーバー側の判断だ。
アドレスがネゴシエーションされる場合にだけフックを選ぶ
PPP ベースのリンク、たとえば L2TP では、リンクが確立すると macOS が /etc/ppp/ip-up を実行する。スクリプトはインターフェース名とネゴシエーションされたローカルおよびリモートのアドレスを受け取るので、サーバーが割り当てたピアに依存するルートを追加できる。固定のサービスルートでは表現できないのはまさにこの場合だ。
IKEv2 やベンダーのクライアントはその PPP 経路をまったく使わないことがあり、その場合スクリプトはセッション中ずっと沈黙しうる。そうなるときは、scutil --nc status を問い合わせて状態変化に反応する launchd ジョブが同等の仕組みであり、わずかな遅延を伴う。
どのフックを使うにせよ、冪等にし、自分が作ったルートだけを削除させる。トンネルに言及するすべてのルートを消すスクリプトは、いずれ VPN サーバーへの到達性を保つルートも消し、接続は成功したとたんに落ちる。
プライベート宛先、通常の閲覧、再接続またはスリープの周期を確認して終える。次に、名前解決は別の方針であり、ルートが動くことはプライベート名が解決する証明ではないので、scutil --dns を調べる。