NAT 背後の WireGuard ピアへのポート転送:DNAT と SNAT
DNAT は要求を届けますが、応答は誤ったアドレスから出ていきます。対応する SNAT ルールを追加し、トンネル設定後に両方を配置します。
ブラウザーが https://203.0.113.10:8443 を要求し、プロキシは接続を受け入れ、WireGuard ピア上のサービスは決して応答しません。DNAT ルールは仕事をしており、失敗しているのは応答の側です。パケットを両方の変換に沿って追えば、不足しているルールは明らかになります。修正は正しいフックにおける NAT 文がもう一つであり、新しいトンネルではありません。
一つの要求を最初から最後まで追う
要求はクライアントの公開アドレスを送信元、プロキシのアドレスを宛先としてプロキシの公開インターフェースに到着します。nat PREROUTING で DNAT ルールが宛先をピアのトンネルアドレス 10.90.0.2 に書き換えます。次にルーティング判断がパケットを wg0 から外へ送り、転送経路が完成します。
ピアでは、パケットは何も書き換えていないため元の公開送信元アドレスを運んでいます。サービスは接続を受け入れ、応答を自身のデフォルトゲートウェイ、つまりピアの前にある家庭用ルーターへ送ります。そのルーターは見たことのないフローの状態を持たないため、パケットを破棄するか自身の公開アドレスに変換し、クライアントはそれを受け入れません。
非対称性が問題のすべてです。要求はプロキシを通り、応答は直接行こうとしますが、プロキシの接続追跡は応答がプロキシを通って戻るときにだけ役立ち、送信元アドレスが元のクライアントを指している限り応答はそこへ戻れません。
戻りの変換を追加する
転送されるフローの送信元を ピアへ出る前にプロキシのトンネルアドレスへ書き換えます。するとサービスは 10.90.0.1 へ応答し、プロキシがそれを受け取り、接続追跡が両方の変換を反転させてからパケットがクライアントへ戻ります。
iptables -t nat -A PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
iptables -t nat -A POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1POSTROUTING ルールは意図的に狭くしており、変換後の宛先とトンネルインターフェースに一致するため、たまたま wg0 から出る無関係なトラフィックを書き換えません。広い masquerade ルールならクライアントのアドレスも隠せますが、すべてのフローに対してそうするため、NAT テーブルの監査が難しくなります。
トンネルが存在する場所にルールを置く
トンネルのアドレスやインターフェースを参照するルールは、wg-quick が設定した後に実行する必要があります。インターフェースは PreUp より前に作成されますが、アドレス、MTU、DNS、ルートはその後に適用され、PostUp はそれらがすべて整ってから実行されます。追加は PostUp に、削除は PreDown に置きます。
PostUp = iptables -t nat -A PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
PostUp = iptables -t nat -A POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1
PreDown = iptables -t nat -D PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
PreDown = iptables -t nat -D POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1削除は追加とトークン単位で一致している必要があり、multiport とインターフェースの一致も含みます。一つの一致が異なる削除は何も削除せず、次の再接続でルールの二つ目のコピーが追加されます。数回の再接続後、テーブルには設定行に結び付けにくい重複が残ります。
結果は iptables -t nat -S で再接続後に確認します。同じルールが二回現れるなら、PreDown の行が一致しておらず、クリーンアップは静かに失敗しています。
転送、フィルタリング、そしてもう一つの NAT
IPv4 転送を永続的に有効にし、次にフィルターテーブルで転送フローを許可します。DNAT は FORWARD チェーンの前に起きるため、フィルタールールは公開アドレスではなく変換後の宛先に一致します。UFW がファイアウォールを管理している場合は許可をルートルールとして表現しますが、単純なホストルールでは転送トラフィックをカバーできません。
プロキシのルールが正しくても、別の二つの NAT 層が経路を壊すことがあります。家庭の接続では公開アドレスはルーターのものであり、そのファイアウォールでポートを開けることは、そのポートをプロキシへ送るポート転送エントリを作ることと同じではありません。外から開いて見えるのに何も応答しないポートは、まさにそのマッピングを欠いていることがよくあります。
NAT VPS では、プロバイダーが公開ポートをプライベートアドレスにマッピングします。マッピングは告知するポートと一致している必要があり、不一致は壊れたトンネルとまったく同じに見えます。WireGuard の設定を変更する前に、プロバイダーのマッピングを確認してください。
戻り経路を確認し、コストを受け入れる
プロキシの LAN ではないネットワークから接続し、接続が開いている間に変換の組を調べます:
conntrack -L -p tcp --dport 8080
tcpdump -ni any tcp port 8080conntrack エントリには、元のタプル、変換後のタプル、応答の方向が表示されるはずです。応答がない場合、ピアはトンネル経由で送信しておらず、通常は SNAT ルールが適用されなかったか、ピア自身のファイアウォールがフローを破棄したことを意味します。
PostUp がルールを再適用することを証明するために一度再起動し、その後トンネルを停止して PreDown がそれらを削除したことを確認します。切断後も残る死んだルールこそ、後の変更が予測不能に振る舞う理由です。
最後に、SNAT のコストを受け入れます。サービスはすべての要求をプロキシから来たものとして見るため、アクセスログ、クライアントごとのレート制限、許可アドレス一覧はもはやユーザーを区別しません。ヘアピンのケースでは、プロキシの LAN 上のクライアント向けに独自の送信元変換が必要です。ピアが実際のクライアントアドレスを必要とするなら、代替はクライアントネットワークへの戻りルートであり、手間は増えますが情報は保たれます。