WireGuardを介したトラフィックの再送(アプリケーション処理後のoif wg0のdnat設定)

WireGuardを介したトラフィックの再送(アプリケーション処理後のoif wg0のdnat設定)

ターミナルサーバー(サーバーB)の存在を隠すために、異なるDC上の2つのサーバー間の安全な通信チャネルとしてWireGuardを使用しています。私はnftablesをファイアウォール管理ツールとして使用します。

パブリックサーバーAからトラフィックを転送し、IPアドレスを予約します(アプリケーションに必要です)。

ターゲットサーバーBはパケットを受信し、アプリケーションはそれを処理しますが、最終的にサーバーは元のIP(元のパケットの送信者IP)にパケットを返そうとするため、問題になります。

元のIPを偽装するのは簡単な解決策のようですが、これらのパケットをWireGuardトンネルに戻すには、サーバーBに既に存在する元のIPを保存する必要があります。

サーバーBのtcpdump:

1:02:36.675958 wg0   In  IP 1.2.3.4.54617 > 10.0.0.2.21: Flags [S], seq 1265491449, win 64240, options [mss 1452,nop,wscale 8,nop,nop,sackOK], length 0
1:02:36.675980 docker0 Out IP 1.2.3.4.54617 > 172.16.0.2.21: Flags [S], seq 1265491449, win 64240, options [mss 1452,nop,wscale 8,nop,nop,sackOK], length 0
1:02:36.676030 docker0 In  IP 172.16.0.2.21 > 1.2.3.4.54617: Flags [S.], seq 1815055360, ack 1265491450, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
1:02:36.676033 enp41s0 Out IP 10.0.0.2.21 > 1.2.3.4.54617: Flags [S.], seq 1815055360, ack 1265491450, win 64240, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
  • 1.2.3.4 - 元のIP
  • 10.0.0.2 - サーバーBのIPラインバッカー
  • 172.16.0.2 - dockerネットワーク、これがアプリケーションだとしましょう(すべてがうまくいきます)

残念ながら、この問題に対する解決策が見つからなかったので、あなたの助けを求めてください。大丈夫ですか?では、どういう意味ですか?

修正する

HAProxyを使用することにしましたが、パフォーマンスに優れたソリューションではないと思います。だから私はまだこの問題に対する可能な解決策が必要です。

systemd-networkdWireGuardトンネルの設定:

# sudo cat /etc/systemd/network/99-wg0.netdev

[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0

[WireGuard]
ListenPort=51820
PrivateKey=[key]

[WireGuardPeer]
PublicKey=[key]
PresharedKey=[key]
AllowedIPs=0.0.0.0/0
Endpoint=[server A]:51820

# sudo cat /etc/systemd/network/99-wg0.network

[Match]
Name=wg0

[Network]
Address=10.0.0.2/24
Address=fdc9:281f:04d7:9ee9::2/64

# sudo ip rule:

0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

nftables設定:

# sudo nft list ruleset         
                                                                                                                                                               [0]
table inet filter {
        chain allow {
                ct state invalid drop comment "early drop of invalid connections"
                ct state { established, related } accept comment "allow tracked connections"

                ip protocol icmp accept comment "allow icmp"
                meta l4proto ipv6-icmp accept comment "allow icmp v6"

                icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
                icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
        }

        chain wireguard {
                tcp dport 21 accept
        }

        chain input {
                type filter hook input priority filter; policy drop;

                iif "lo" accept comment "allow from loopback"

                tcp dport 22 ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"
                tcp dport 22 accept comment "allow sshd"

                ip6 saddr [server A] udp dport 51820 accept comment "Accept wireguard connection from proxy1.vps-da4c9ada.ovh.zolotomc.ru"

                jump allow comment "allowed traffic for input"

                meta pkttype host limit rate 5/second counter packets 1047 bytes 43403 reject with icmpx admin-prohibited
                reject with icmpx host-unreachable
        }

        chain forward {
                type filter hook forward priority filter; policy drop;

                iif "docker0" accept comment "allow outgoing traffic from docker"

                jump allow comment "allowed traffic for forward"

                iif "wg0" jump wireguard comment "Wireguard chain"

                reject with icmpx host-unreachable
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}

table inet nat {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                iif "wg0" jump wireguard comment "Wireguard chain"
        }

        chain wireguard {
                tcp dport 21 dnat ip to 172.16.0.2
                tcp dport 21 dnat ip6 to fe80::a8d7:f6ff:fe0b:4774
        }

        chain input {
                type nat hook input priority 100; policy accept;
        }

        chain output {
                type nat hook output priority -100; policy accept;
        }

        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                iif "docker0" oif != "docker0" masquerade
        }
}

使ってみようパケットメタ情報の設定ただし、ポートを新しいランダムポートに再割り当てします。しかし、不可能または複雑すぎると思い始めました。nftablesルール。

答え1

正しく応答するために必要な情報:「初期パケットはどのインターフェイスから来ましたか?」ネットワークインタフェースの応答に別のパケットが表示されると失われます。これを覚えて応答に再利用する方法が必要です。ここにいる:つながる現在追跡されているすべての接続のリストを覚えておいてください。保管も可能で、ストリームあたりマーク、それから呼び出しコマック。人々は価値に意味を与えることができます。

これにより、アプリケーションを使用できます。ポリシールーティング個々のパケットではなくストリーム全体に適用されます。

ブログLinux以上!そこに説明されています:ネットフィルターコマック

アイデアは、フローがWireGuardインターフェイスを使用するときに情報を記憶し、応答パケットが同じフローに関連付けられ、通常のパスではなくWireGuardインターフェイスを介して再ルーティングされるようにすることです。これは独立機関を通じて処理することができます。nftablesテーブルと関連ルーティングテーブル/ルールを使用して、パケットの通常の運命を変更します。応答を選択するには、0xf00「このフローのソース」を意味するタグ値wg0とルーティングテーブル1000を使用します。wg0

応答パケットの一般的なルーティングを処理するのに十分なルーティングテーブルとルーティングルールを準備します。

ip route add default dev wg0 table 1000
ip rule add fwmark 0xf00 lookup 1000

IPv6も使用されている場合(ただし、OPの設定でAllowedIPsにIPv6が欠落している場合)、次の手順も実行します。

ip -6 route add default dev wg0 table 1000
ip -6 rule add fwmark 0xf00 lookup 1000

使用例systemd-networkd:

[Match]
Name=wg0

[Network]
Address=10.0.0.2/24
Address=fdc9:281f:04d7:9ee9::2/64

[Route]
Gateway=0.0.0.0
Table=1000

[RoutingPolicyRule]
Table=1000
FirewallMark=0xf00

そして、次のテーブルを追加してください。

replywg0.nft:

table inet replywg0         # for idempotency
delete table inet replywg0  # for idempotency

table inet replywg0 {
    chain prerouting {
        type filter hook prerouting priority -150; policy accept;
        iif wg0 ct mark set 0xf00
        iif != wg0 ct mark 0xf00 meta mark set 0xf00
    }

    chain output {
        type route hook output priority -150; policy accept;
        ct mark 0xf00 meta mark set 0xf00
    }
}

負荷:

nft -f replywg0.nft

注意と警告:

  • これバッグタグはルーティングに影響を与える唯一のタグであり、から来るときに設定されず、設定のみですwg0コマック設定:そうでない場合は、表1000で定義されている単一パスを選択し、ローカルシステムまたはDockerコンテナに移動し続けるのではなく、ソースからパケットを再ルーティングします。ルーティングテーブル1000に私が知らないすべての関連する追加のパスが含まれている場合、この特別な処理は必要ありません。

  • 出力チェーンがDockerトラフィックにのみ適している場合、出力チェーンは完全にオプションです。次の場合にのみ便利です。地元の応答トラフィック:受信したトラフィックワークグループ0Dockerに送信されず、ローカルシステムに送信されます。代わりに、ここではtype route代わりに使用されるので、type filter新しいルート検索はまだ可能です。とにかく、一部のUDPサービスはここで正しく応答しません。 IPソースアドレスが間違っている可能性があります(で設定されたアドレスenp41s0)、いくつかの追加の不完全なNATバンドルが必要です(例:inet nat出力の中の最もよいテーブル)。

  • 後でフラグに関する他の要件がある場合は注意してください(WireGuard自体にもフラグを設定するオプションがあります)。正しく処理しないと、競合が発生する可能性があります。

  • FTP(OP設定に表示されます)は特別です。追加のデータ接続を使用し、必要になる場合があります。ALG(通常サーバーの場合はパッシブモードです)。暗号化されていない限り、Linuxのカーネルモジュールとして扱うことができます。nf_conntrack_ftp+nf_nat_ftp以下の設定で十分です。iptablesと接続追跡ヘルパーの安全な使用そして(少し違う)同等物がありますnftables設定。 RELATEDトラフィック(データトラフィック)はコマック、このブログで説明されている設定は、この回答の設定でも機能します。トグルスイッチnf_conntrack_helper~であるカーネル>= 6.0から完全に削除されましたしたがって、これは必須設定になります。

  • 私は仮定するrp_filter値は 1 ではありません。そうでなければ、wg0最初は何も起こらないかもしれないからです。 1に設定すると、さまざまな場所に追加の設定が必要です。

関連情報