nftables, masquerade: 패킷이 잘못된 아웃바운드 인터페이스를 통과함

nftables, masquerade: 패킷이 잘못된 아웃바운드 인터페이스를 통과함

내가 한 일은 매우 간단했습니다. 포트 전달을 사용하고 POSTROUTING 체인에서 가장을 활성화했습니다.

table inet nat {
    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
        ip daddr 198.51.100.105 counter dnat to 10.8.0.105 comment "host.example.com"
    }
    
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        counter masquerade
    }
}

내 컴퓨터에는 다음과 같은 인터페이스가 있습니다.

1: lo
2: ens18
3: ens19
4: wg0

경로는 다음과 같습니다:

default via 203.0.113.1 dev ens18 onlink
10.8.0.0/24 dev wg0 proto kernel scope link src 10.8.0.1
198.51.100.0/24 dev ens19 proto kernel scope link src 198.51.100.3
203.0.113.0/24 dev ens18 proto kernel scope link src 203.0.113.134

규칙은 다음과 같습니다:

0:      from all lookup local
32764:  from all to 198.51.100.0/24 lookup subnets
32765:  from 198.51.100.0/24 lookup subnets
32766:  from all lookup main
32767:  from all lookup default

출력은 ip route get 198.51.100.105다음과 같습니다

local 198.51.100.105 dev lo table local src 198.51.100.3 uid 0
    cache <local>

출력 ip route show table subnets:

default via 198.51.100.1 dev ens19
198.51.100.0/24 dev ens19 scope link src 198.51.100.3

지금 핑할 때사람들외부 VPS 또는 내 집 DSL 주소에서 198.51.100.105ICMP 패킷이 수신되었을 뿐만 아니라 다음 주소에도 응답되었는지 확인할 수 있습니다.

ICMP 요청

root@debian:~# tcpdump -i ens19 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens19, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:35:51.686208 IP 192.0.2.2 > 198.51.100.105: ICMP echo request, id 14855, seq 1, length 64

ICMP 응답

root@debian:~# tcpdump -i ens18 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens18, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:35:38.797502 IP 198.51.100.105 > 192.0.2.2: ICMP echo reply, id 5107, seq 1, length 64

하지만 보시다시피 응답은 잘못된 인터페이스에서 나옵니다. 이어야 하는데 ens19이미 사용 중인데 ens18그 이유를 모르겠습니다.

  1. 경로/규칙을 따르지 않는 이유는 무엇입니까? 서브넷은 198.51.100.0/24아무 관련이 없으며 ens18다른 VLAN도 마찬가지입니다.

  2. nftables는 위장된 패킷에 대해 다른 아웃바운드 인터페이스를 강제할 수 없습니까?

도움을 주셔서 감사합니다.

편집하다: 출력은 다음과 ip -br link; ip -4 -br addr; ip -4 route; ip rule; ip rule show table subnets같습니다.

ip -br link; ip -4 -br addr; ip -4 route; ip rule; ip rule show table subnets
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
ens18            UP             a6:ea:02:1c:XX:XX <BROADCAST,MULTICAST,UP,LOWER_UP>
ens19            UP             ac:71:fe:18:XX:XX <BROADCAST,MULTICAST,UP,LOWER_UP>
wg0              UNKNOWN        <POINTOPOINT,NOARP,UP,LOWER_UP>
lo               UNKNOWN        127.0.0.1/8
ens18            UP             203.0.113.134/24
ens19            UP             198.51.100.3/24 198.51.100.100/24 198.51.100.101/24 198.51.100.102/24 198.51.100.103/24 198.51.100.104/24 198.51.100.105/24
wg0              UNKNOWN        10.8.0.1/24
default via 203.0.113.1 dev ens18 onlink
10.8.0.0/24 dev wg0 proto kernel scope link src 10.8.0.1
198.51.100.0/24 dev ens19 proto kernel scope link src 198.51.100.3
203.0.113.0/24 dev ens18 proto kernel scope link src 203.0.113.134
0:      from all lookup local
32764:  from all to 198.51.100.0/24 lookup subnets
32765:  from 198.51.100.0/24 lookup subnets
32766:  from all lookup main
32767:  from all lookup default
32764:  from all to 198.51.100.0/24 lookup subnets
32765:  from 198.51.100.0/24 lookup subnets

라인배커

명령 출력 wg:

interface: wg0
  public key: XrSd2TftIpiL3zhXXX=
  private key: (hidden)
  listening port: 51820

peer: gZ89rFX6DvBtdeuYXXX=
  endpoint: 233.252.0.0:39126
  allowed ips: 10.8.0.0/24
  latest handshake: 20 seconds ago
  transfer: 3.42 MiB received, 4.08 MiB sent

명령 출력:systemctl status [email protected]

[email protected] - WireGuard via wg-quick(8) for wg0
     Loaded: loaded (/lib/systemd/system/[email protected]; enabled; preset: enabled)
     Active: active (exited) since Sat 2023-08-05 23:42:48 CEST; 14h ago
       Docs: man:wg-quick(8)
             man:wg(8)
             https://www.wireguard.com/
             https://www.wireguard.com/quickstart/
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
    Process: 690 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
   Main PID: 690 (code=exited, status=0/SUCCESS)
        CPU: 27ms

Aug 05 23:42:48 debian systemd[1]: Starting [email protected] - WireGuard via wg-quick(8) for wg0...
Aug 05 23:42:48 debian wg-quick[690]: [#] ip link add wg0 type wireguard
Aug 05 23:42:48 debian wg-quick[690]: [#] wg setconf wg0 /dev/fd/63
Aug 05 23:42:48 debian wg-quick[690]: [#] ip -4 address add 10.8.0.1/24 dev wg0
Aug 05 23:42:48 debian wg-quick[690]: [#] ip link set mtu 1420 up dev wg0
Aug 05 23:42:48 debian systemd[1]: Finished [email protected] - WireGuard via wg-quick(8) for wg0.

서버:/etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
SaveConfig = true
ListenPort = 51820
PrivateKey = 8BspU4XXX=

[Peer]
PublicKey = gZ89rFX6DvBtdeuYXXX=
AllowedIPs = 10.8.0.0/24
Endpoint = 233.252.0.0:58642

피어: /etc/wireguard/wg0.conf

[Interface]
# Client Private Key
PrivateKey = iO00+qQDXXX=
Address = 10.8.0.107/24

[Peer]
# Server Public Key
PublicKey = XrSd2TftIpiL3zhXXX=
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25
Endpoint = 198.51.100.3:51820

답변1

참고 및 조정 사항(혼란으로 인한 것일 수 있음):

  • WireGuard 피어가 일치하기 위해 10.8.0.107 대신 10.8.0.105를 사용한다고 가정합니다.nftables규칙 세트.
  • 233.252.0.0은 시뮬레이션에서 문제를 일으킬 수 있습니다(특히 다음과 같은 경우).동료) 멀티캐스트 주소이기 때문입니다. 아래에서는 192.0.2.233을 사용하겠습니다(192.0.2.2와 네트워크 관계가 없음).

이 질문은 로컬 트래픽뿐만 아니라 전달을 위해 dnat 규칙을 사용하는 문제를 해결하기 위한 것입니다. 마지막에 수정된 WireGuard 터널 엔벨로프에도 숨겨진 문제가 있습니다.

ping 테스트(사전 라우팅에서 발생하는 실제 dnat 및 응답에서 발생하는 마스킹 해제 포함)를 통한 라우팅 스택의 동작은 커널에 어떤 경로를 쿼리할지 쿼리하는 다음 두 명령으로 요약할 수 있습니다. 사용:

# ip route get from 192.0.2.2 iif ens19 to 10.8.0.105
10.8.0.105 from 192.0.2.2 dev wg0 
    cache iif ens19 
# ip route get from 10.8.0.105 iif wg0 to 192.0.2.2
192.0.2.2 from 10.8.0.105 via 203.0.113.1 dev ens18 
    cache iif wg0 

여기에서 응답이 잘못된 인터페이스를 사용하고 있음을 알 수 있습니다. 주소가 다시 작성됩니다.연결하다항목 내용: 위에 표시되지 않더라도 여전히 198.51.100.105입니다.

이는 누락된 규칙으로 인해 발생합니다: any from (returns)작업 그룹 0테이블을 사용해야 한다서브넷. 다음으로 수정됨:

ip rule add iif wg0 lookup subnets

이것은 또한 문제를 해결했습니다.rp_filter=1RTNETLINK answers: Invalid cross-device link위의 첫 번째 경로 테스트는 일반적으로 추가되어야 함에도 불구하고 실패합니다.작업 그룹 0경로도 이 표에 있습니다.

이제 주어진:

# ip route get from 10.8.0.105 iif wg0 to 192.0.2.2
192.0.2.2 from 10.8.0.105 via 198.51.100.1 dev ens19 table subnets 
    cache iif wg0 

이제 핑 테스트가 제대로 작동합니다.


추가적인 숨겨진 WireGuard 봉투 라우팅 문제가 있습니다.

콤비네이션:

  • 활성화되지 않음엄격한 역방향 경로 전달(RFC 3704)
  • 피어가 먼저 서버에 접속하도록 합니다(마지막에 있는 추가 질문 참조).
  • (적어도) 커널 구현은 원래 접촉했던 것과 동일한 소스를 사용하여 응답해야 한다고 결정합니다.

WireGuard가 작동하도록 허용합니다 ping 10.8.0.1.동료응답이 수신되고 이후의 WireGuard 트래픽은 동일한 봉투 주소를 계속 사용하도록 허용됩니다.

로컬(라우팅되지 않은) 흐름의 소스 주소가 선언되지 않은 경우 라우팅 스택은 지정된 경로에 어떤 주소를 사용해야 하는지 파악해야 합니다. 이는 소켓이 일반적으로 바인딩되지 않은 상태로 유지되는 UDP의 경우 특히 중요합니다(즉, 소스가 0.0.0.0, 즉 INADDR_ANY임). TCP 서버의 경우 나중에 생성된 중복 소켓이 accept(2)더 이상 0.0.0.0에 바인딩되지 않고 올바른 주소에 바인딩되므로 이는 문제가 되지 않습니다. 그런 다음 이 주소를 라우팅 스택에 제공합니다. 여기서 WireGuard는 UDP 및 INADDR_ANY를 사용합니다. 특히 198.51.100.3에 바인딩되지 않습니다. 이는 소스 0.0.0.0으로 나타나고 나가는 소스 IP 주소의 확인을 커널의 라우팅 스택에 남겨둔다는 의미입니다.

만약에섬기는 사람WireGuard는 항상 첫 번째 패킷을 시작합니다(대신동료이렇게 하면 198.51.100.3 대신 203.0.113.134가 사용됩니다. 라우팅 스택에는 특정 항목이 없습니다.IP 규칙0.0.0.0의 경우:IP 규칙 32765: from 198.51.100.0/24 lookup subnets일치하는 항목이 없으며 특수 정책 라우팅이 적용되지 않습니다. 마지막으로 UDP 패킷은 ens18을 사용하여 203.0.113.134로 나갑니다.

커널 구현은 적어도 쿼리된 것과 동일한 주소를 계속 사용하는 것 같습니다. 이는 신뢰할 수 없으며 UDP 서비스를 사용한 멀티호밍에는 특별한 지원이 필요합니다(예:IP_PKTINFO) 따라서 응용 프로그램에서.

WireGuard가 추구하는 결과:

# ip route get from 198.51.100.105 to 192.0.2.233
192.0.2.233 from 198.51.100.105 via 198.51.100.1 dev ens19 table subnets uid 0 
    cache 

적어도 트래픽을 가장 먼저 시작한 실제 결과라면 다음과 같습니다.

# ip route get from 0.0.0.0 to 192.0.2.233
192.0.2.233 via 203.0.113.1 dev ens18 src 203.0.113.134 uid 0 
    cache 

WireGuard 터널 멀티호밍 라우팅 자체를 실제로 수정하려면 L4별 프로토콜 라우팅 규칙을 사용할 수 있습니다.

ip rule add iif lo ipproto udp sport 51820 lookup subnets

( iif lo로컬에서 시작된(전달되지 않은) 트래픽을 나타내는 특수 구문이며 인터페이스 독립적입니다 lo.)

주다:

# ip route get from 0.0.0.0 ipproto udp sport 51820 to 192.0.2.233
192.0.2.233 via 198.51.100.1 dev ens19 table subnets src 198.51.100.3 uid 0 
    cache 

INADDR_ANY를 소스로 사용함에도 불구하고 이제 UDP 소스 포트 51820이 선택됩니다.서브넷라우팅 테이블.

관련 정보