Wireguard Handshake Issues on Android 4G Network

iptablesnetworkingvpnwireguard

I have a Wireguard server on my home network which works fine on all my devices, including my phone when it's connected on Wi-Fi. The problem comes when I disconnect from the Wi-Fi and go on 4G, now my phone is unable to complete the handshake with my server.

On my router, I have UDP port 51820 forwarded to my Wireguard server.
On my phone, I connect to the VPN using the DNS name (vpn.mydomain.tld:51820)

I've enabled kernel logging for Wireguard to help me troubleshoot this but sadly I haven't been able to find what's wrong with my setup.

Here are the logs on my server that appear when I'm trying to connect from my phone (via 4G)

Mar 23 17:49:36 wireguard kernel: [448095.663902] wireguard: wg0: Keypair 9893 created for peer 16
Mar 23 17:49:45 wireguard kernel: [448104.009541] wireguard: wg0: Receiving handshake initiation from peer 16 (xxx.xxx.xxx.xxx:40061)
Mar 23 17:49:45 wireguard kernel: [448104.009546] wireguard: wg0: Sending handshake response to peer 16 (xxx.xxx.xxx.xxx:40061)
Mar 23 17:49:45 wireguard kernel: [448104.010284] wireguard: wg0: Keypair 9893 destroyed for peer 16
Mar 23 17:49:45 wireguard kernel: [448104.010286] wireguard: wg0: Keypair 9894 created for peer 16
Mar 23 17:49:50 wireguard kernel: [448109.069901] wireguard: wg0: Receiving handshake initiation from peer 16 (xxx.xxx.xxx.xxx:40061)
Mar 23 17:49:50 wireguard kernel: [448109.069903] wireguard: wg0: Sending handshake response to peer 16 (xxx.xxx.xxx.xxx:40061)
Mar 23 17:49:50 wireguard kernel: [448109.070073] wireguard: wg0: Keypair 9894 destroyed for peer 16

On my phone, I see the following:

peer(...) - Sending handshake initiation
peer(...) - Handshake did not complete after 5 seconds, retrying (try 2)
peer(...) - Sending handshake initiation
peer(...) - Handshake did not complete after 5 seconds, retrying (try 2)
peer(...) - Sending handshake initiation

Since it works fine when I'm connected to my home Wi-Fi, I'm at a loss as to what to look for other that port forwarding, but that works fine as far as I can tell.

Here is the wg0.conf on my server:

[Interface]
Address = 10.10.10.3/32
SaveConfig = true
PostUp = iptables -A FORWARD -i %i -j ACCEPT;  iptables -t nat -A POSTROUTING -d 10.10.10.0/24 -o eth0 -j MASQUERADE; iptables -t nat -A POSTROUTING ! -d 10.10.10.0/24 -o pia -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -d 10.10.10.0/24 -o eth0 -j MASQUERADE; iptables -t nat -D POSTROUTING ! -d 10.10.10.0/24 -o pia -j MASQUERADE
ListenPort = 51820
PrivateKey = [removed]

[Peer]
PublicKey = [removed]
AllowedIPs = 10.10.10.245/32
Endpoint = 10.10.10.147:60743

And the connection file on my phone (running Android 11):

[Interface]
Address = 10.10.10.245/24
DNS = 10.10.10.2
PrivateKey = [removed]

[Peer]
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.mydomain.tld:51820
PersistentKeepalive = 25
PublicKey = [removed]

I've made sure the keys are matching but since the connection works fine when connected to my Wi-Fi, I don't think the config file is to blame.

I have UFW installed on the server, with the following config:

To                         Action      From
--                         ------      ----
51820/udp                  ALLOW       Anywhere
22/tcp                     ALLOW       Anywhere
10050                      ALLOW       Anywhere
51820/udp (v6)             ALLOW       Anywhere (v6)
22/tcp (v6)                ALLOW       Anywhere (v6)
10050 (v6)                 ALLOW       Anywhere (v6)

However I disabled it to make sure it wasn't interfering, and it didn't change anything.
At this point I don't know what is wrong nor what to search for to help me in figuring this out so any help will be welcome

EDIT: I kind of figured out where the issue is coming from. Im my wg0.conf, I have the following part in my PostUp: iptables -t nat -A POSTROUTING ! -d 10.10.10.0/24 -o pia -j MASQUERADE
This line is meant to route all outgoing traffic through another tunnel, however, it also has the side effect of making my WireGuard server respond to my external client through another IP (i.e. I'm connecting from my phone on 4G to xxx.xxx.xxx.xxx, and because of that line, it's responding to the connection request via yyy.yyy.yyy.yyy), which is why my client won't connect.
When I remove this routing rule, I can connect just fine from outside my network, but now of course, my traffic isn't re-routed through my second tunnel.
Is there a way I can keep this rule, or at least a similar rule that will route external traffic through the other VPN, whilst responding to my phone's connection request via my eth0 device? Since my phone will be connecting from 4G, I can't expect the IP address it will connect from, sp I can't add a similar routing rule only for my phone.

Best Answer

MASQUERADE/POSTROUTING rules do not change where certain traffics go. Routes do. The problem is that you have a default route (or what's equivalent) that leads traffics into the pia tunnel.

You will need to make use of policy routing for the replying traffics from the wireguard server:

# ip r add 192.168.1.1 dev eth0 table 123
# ip r add default via 192.168.1.1 table 123
# ip rule add iif lo ipproto udp sport 51820 lookup 123

The first command could be optional. Make sure you replace 192.168.1.1 and eth0 with the LAN IP of your router and the interface name of your Ethernet NIC correspondingly. (You can copy them from the output of ip r, i.e. routes in the main table.) The number 123 is arbitrary. iif lo limits the rule to UDP traffics with source port of 51820 from the host itself (but not such traffics from another host).

Related Topic