How to make a Google Cloud VM forward Minecraft traffic to an OpenVPN client?

I have an OpenVPN network currently set up in "tap" mode, with the Google VM as the server and a Raspberry Pi 3 client running a Minecraft server.

Users are currently able to connect to the Minecraft server by running an OpenVPN client on their machines and typing the Pi's OpenVPN IP into the Minecraft client (say, 10.8.0.2).

However, I want to be able to let users access the Minecraft server using the Google VM's external IP (without the need to install OpenVPN). Users can't use the Pi's address directly because of ISP double-layer NAT.

I've tried following this answer. Specifically I executed:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 25565 -j DNAT --to-destination 10.8.0.2:25565
iptables -A FORWARD -p tcp -d 10.8.0.2 --dport 25565 -j ACCEPT
iptables -A POSTROUTING -t nat -s 10.8.0.2 -o eth0 -j MASQUERADE

VM's interfaces are eth0 and tap0. IP forwarding is enabled in the console.

The server firewall is set to allow traffic from 0.0.0.0/0 TCP:25565 and UDP:25565

However, when trying to connect using the Google VM's external IP, I get "connection timed out". NMAP tells me that port 25565 is "filtered"

Additional info, iptables-save:

# Generated by iptables-save v1.6.0 on Thu Sep 26 11:42:02 2019
*nat
:PREROUTING ACCEPT [106:35257]
:INPUT ACCEPT [75:15902]
:OUTPUT ACCEPT [111:6692]
:POSTROUTING ACCEPT [6:328]
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -i eth0 -p udp -m udp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -i eth0 -p udp -m udp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -d 35.197.47.71/32 -p tcp -m tcp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -d 35.197.47.71/32 -p udp -m udp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25565 -j DNAT --to-destination 10.8.0.3:25565
-A POSTROUTING -s 10.0.0.0/8 -o eth0 -j MASQUERADE
-A POSTROUTING -s 10.0.0.0/8 -o eth0 -j MASQUERADE
-A POSTROUTING -s 10.8.0.3/32 -o eth0 -j MASQUERADE
-A POSTROUTING -s 10.8.0.2/32 -o eth0 -j MASQUERADE
COMMIT
# Completed on Thu Sep 26 11:42:02 2019
# Generated by iptables-save v1.6.0 on Thu Sep 26 11:42:02 2019
*filter
:INPUT ACCEPT [1822:367429]
:FORWARD ACCEPT [13:700]
:OUTPUT ACCEPT [1610:231716]
-A FORWARD -d 10.8.0.2/32 -p tcp -m tcp --dport 25565 -j ACCEPT
COMMIT
# Completed on Thu Sep 26 11:42:02 2019

--list-rules:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A FORWARD -d 10.8.0.3/32 -p tcp -m tcp --dport 25565 -j ACCEPT

I think the problem might be, you are not masquerading the packets that are routed to the pi and not all traffics from the pi are made to be routed into the tunnel.

This means while packets from the outside world can reach the pi but when the pi replies to them, the response packets are not routed to the Google VM first (but are probably directly sent to the clients (as in, via its Internet gateway), which hence won't be recognized by the clients).

Also if the pi has firewall filtering incoming packets from the tunnel (such that only packets from other VPN hosts are accepted), packets that are not masqueraded as from the VM will be filtered.

Therefore, you might need:

iptables -t nat -A POSTROUTING -d 10.8.0.x -o tap0 -j MASQUERADE