Trouble setting up transparent reverse IPv6 proxy

Started by zbig, February 07, 2025, 01:00:55 PM

Previous topic - Next topic
February 07, 2025, 01:00:55 PM Last Edit: February 07, 2025, 01:13:25 PM by zbig
Hello, I'll try to sum up what I'm trying to achieve:

OPNsense box
  • A service running there needs to receive (and respond to) incoming connections arriving via a WireGuard tunnel. Original source (public) addresses of the connecting clients need to be preserved.
  • WireGuard instance (configured using the official plugin) connects to the "gateway server" (see below).
  • Gateway configured in OPNsense and pointing at "gateway server's" WireGuard peer address
  • Floating firewall rule bound to the WG interface applying policy-based routing by means of having its "reply-to" option set to aforementioned WG gateway

Gateway server
  • An iptables/nftables rule that forwards incoming connections arriving at its public address to OPNsense WireGuard peer address.
    For IPv4: iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.11.0.2

Clients on the internet
Hitting up gateway server's public IP address and get served the OPNsense hosted content

It all works great for IPv4: clients are connecting, the service running on OPNsense is seeing connecting clients' original IP addresses despite the connections getting tunnelled over WG. Happy times.

Not much luck with replicating the same functionality over IPv6, though. I basically tried to replicate everything I've done in parallel but using IPv6, just sharing WireGuard instances with IPv4.

What is working:
  • IPv6 pings between OPNsense box's and the gateway server's WG addresses
  • Server process binding on IPv6 socket, properly responding when being hit from the gateway using OPNsense's WG v6 peer address
  • Client's SYN packets arriving at OPNsense's IPv6 WG peer address when they try to connect to gateway's server's public IPv6 address

Where it fails:
Although the clients' tunnelled incoming connection requests seem to arrive at OPNsense:

12:43:02.036721 IP6 2600:reda::cted:xxxx:xxxx:4c94.35388 > fd16:eb69:819d::2.8080: Flags [S], seq 2816276090, win 64800, options [mss 1440,sackOK,TS val 4265021634 ecr 0,nop,wscale 7], length 0
0x0000:  6001 241e 0028 0633 2600 3c17 0000 0000  `.$..(.3&.<.....
0x0010:  f03c 95ff feeb 4c94 fd16 eb69 819d 0000  .<....L....i....
0x0020:  0000 0000 0000 0002 8a3c 1f90 a7dc f67a  .........<.....z
0x0030:  0000 0000 a002 fd20 55e1 0000 0204 05a0  ........U.......
0x0040:  0402 080a fe37 10c2 0000 0000 0103 0307  .....7..........

they never get SYN ACK'ed on any of OPNsense interfaces, seemingly. As if it wouldn't know what to do with them.

Both WireGuard peers have their AllowedIPs set to 0.0.0.0/0, ::/0. Like I said, I tried to replicate my working IPv4 setup as closely as possible with differences only where needed. As the iptables build I have on the gateway server doesn't seem to support IPv6 DNAT, I used the following nft rule to try to achieve DNAT66 functionality:

table ip6 nat {
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
ip6 daddr 2a01:reda::cted:xxxx:xxxx:e72c tcp dport 443 dnat to fd16:eb69:819d::2
}
}

Any help greatly appreciated.
Cheers

February 08, 2025, 08:22:08 AM #1 Last Edit: February 08, 2025, 08:23:41 AM by zbig
Update:
Got rid of the dodgy IPv6 NATting on the gateway server. Ordered an IPv6 /56 range from its provider instead, subnetted and routed it to my OPNsense in what I now hope to be a proper IPv6-native way. So now my OPNsense WireGuard peer has its own IPv6 GUA, no more funny packet forwarding rules.

Despite all that, in a cruel lack of plot twist, nothing has ultimately changed: the service that listens on OPNsense's WG port only accepts incoming connections that originate from within its own subnet. I can see my firewall allow rule getting triggered in both cases but the incoming connections still only get SYNACK'd if they come from the same network WG peer is. What gives?

February 08, 2025, 04:22:52 PM #2 Last Edit: February 08, 2025, 04:28:54 PM by zbig
Hi Zbig!
Hi myself!

Got it working finally by adding a static route to ::/1 through my WireGuard interface on OPNsense. Turns out policy-based routing ("reply-to" option) doesn't really work well for IPv6, either by design or due to a bug. Might also be that I hit some corner case by not having any IPv6 addresses assigned to any on the interfaces on my OPNsense instance other than on the WG tunnel.

Oh well, at least I was forced to learn some IPv6 networking basics and ended up with a /56 routed range for fun and profit.

BTW, did I mention I also had to add another iptables rule to the v4 portion of my tunnelling solution to  perform MSS clamping so it doesn't try to shove 1480 byte-sized packets into a WireGuard tunnel with its MTU set to 1420? I sure did, I sure did...