サーバーは新しいアドレスへ移り、クライアントは今も接続し、送ったものはどれもインターネットに届かない。この分離が手がかりだ。待ち受けソケットとポート転送は移転を生き延びたのでトンネルは立ち上がるのに、転送パケットに適用される送信元変換は今もホストが持たないアドレスを指している。書き換えられたパケットへの応答は、もう存在しないマシンへ向かう。

接続と転送は別の場所で壊れる

確立はクライアントが既に知っている端点に依存する。公開アドレス、その手前のポート転送、内側の待ち受けだ。転送トラフィックは、パケットがトンネルに入った後に下される二つ目の判断に依存する。ホストが送信元を書き換えて、応答が戻り道を見つけられるようにするのだ。

アドレスの変更が壊すのは二つ目の判断だけで、だから症状は「接続はできるがインターネットに出られない」と読める。これは、クライアント側のいつもの確認が健全に見える理由でもある。ハンドシェイクは完了し、クライアントはアドレスを受け取り、サーバーのトンネルアドレスへの ping すら返ることがある。障害は反対側にあるのにクライアントで何時間も費やされる。だからルール表が最初に見る場所になる。

編集する前にルールとカウンターを読む

カウンター付きで変換ルールを一覧し、設定された送信元をホストが実際に持つアドレスと比べる。死んだアドレスへ書き換えられたルールは、通常は行きの経路にトラフィックを示す。パケットがそこに届いて変換されるからだ。戻りの経路には何も示さない。

sudo iptables -t nat -L POSTROUTING -n -v --line-numbers
ip -4 addr show

そこにいる間に三つの項目を確認する。送信元一致のプール、ルールが指定していれば出口インターフェース、そしてトンネル間トラフィックの書き換えを防ぐ除外だ。この除外は編集の途中で失われやすく、失うとピア間のトラフィックが動いているように見えるのに、その応答は奇妙な経路を通る。

カウンターがゼロのままなら、パケットはこのルールに届いておらず、アドレスは今の問題ではない。チェーンのもっと前にある競合ルールを探す。元を編集せずに置き換えを足したときに起こる。ルール番号を一分前に控えた出力と比べる。順序は設定の一部であり、見た目の細部ではない。

送信元を置き換えるか、指定をやめる

ホストが安定したアドレスを持つなら、もう一つ足すのではなくルールをその場で置き換える。ルール番号による置き換えは、チェーンの残りが依存する順序を保つ。

sudo iptables -t nat -R POSTROUTING 1 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to-source 10.151.0.40

アドレスが動的に割り当てられるなら、送信元をまったく指定せず、出口インターフェースからカーネルに現在のものを選ばせる。この形は次の移転を編集なしで生き延びる。どのアドレスを使ったかが見えなくなる代償はある。

sudo iptables -t nat -R POSTROUTING 1 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -o eth0 -j MASQUERADE

IPv4 転送が管理された設定で今も有効か確認し、そのうえでディストリビューション自身の仕組みでルールを永続化する。実行中のカーネルだけで行った変更は次の再起動で戻る。たいていは、誰も変更と結び付けない保守時間帯に起きる。

置き換えを誤る二つのやり方

プールをサーバーの LAN アドレスではなくトンネルアドレスへ変換すると、応答はまっすぐトンネルへ戻り、決して要求していないクライアントへ向けてルーティングされる。接続はルーティングループのように止まる。実際にループだからだ。

古い変換の上に二つ目を積むのがもう一つの誤りだ。最初に一致したルールが勝つので、古い項目は削除されるまでパケットを処理し続け、新しいルールのカウンターはゼロのまま、全員が置き換えを責める。新しいものを足す前に重複を削除する。そのうえで一覧をもう一度読み、プールに一致する変換がちょうど一つであることを確認する。

トンネルが IPv6 も運ぶなら、もう一方のスタックで同じ見直しを繰り返す。変換も経路付きプレフィックスもない IPv6 のデフォルトルートは同じくらい静かに失敗し、その上はすべて同じに見えるので忘れやすい。

内向きのサービスには逆方向の変換が要る

VPN クライアント上のサービスを外から到達可能にするには、送信元変換ではなく宛先変換が要る。公開ポートは入る前にクライアントのトンネルアドレスへ書き換えられる。そのうえで転送経路には、そのトラフィックを許可する明示的なルールが要る。

sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.9.8.2:8080

トンネル自体を運ぶポートは決して変換しない。その書き換えは、サービスを到達可能に保つ接続を断ち切る。トンネルのポートは宛先ルールから完全に外す。最後に両端から検証する。新しいルールのカウンター、トンネルデバイスでの短いキャプチャ、そして公開アドレスを試す前にトンネル内のアドレスへの一回の要求だ。

関連記事