Project Zomboid

Project Zomboid

Not enough ratings
Linux Server: Proxy Server using FOU
By Lu5ck
Setting up a proxy might be a good idea should you need a DDOS capable facing server to protect your home server or to route connections.
   
Award
Favorite
Favorited
Unfavorite
Introductions
You might want to setup proxy because
  • You found a very cheap high end spec VPS but the catch is the network is totally garbage, you also found a cheap traffic VPS with awesome global network within reasonable latency to each other.
  • Your game server, like home server, has no DDOS protection and you want to use a VPS with DDOS protection as the face server.

There are many ways to setup a network tunnel between two machines. Here, we are gonna use IPIP FOU which uses UDP and no encryption. It is a very lightweight point to point tunnel, it is comparatively at least 8 times faster than wireguard. This will allow you to hit 1Gbps provided that you have the network to support it. Encryption is not needed as we are assuming the proxy is just an alter route hop, of course assuming that you are not using proxy server for more than just proxy.
Requirements
  • Two servers
  • FirewallD (I like FirewallD)
  • IPIP enabled kernel
  • FOU enabled kernel
RHEL-based Linux
dnf install firewalld iproute systemctl start firewalld systemctl enable firewalld
Debian-based Linux
apt-get install firewalld iproute2 systemctl start firewalld systemctl enable firewalld
Proxy Server setup
Setup firewalld
tunnel0 will be considered a trusted network since it is our tunnel to the game server
masquerade will allow game server to access the internet
32011 is our FOU port, you can change this if you want
We will forward all incoming connections on port 16261 to 16262 to the tunnel

firewall-cmd --add-interface=tunnel0 --zone=trusted --permanent firewall-cmd --add-masquerade --zone=public --permanent firewall-cmd --zone=public --add-port=32011/udp --permanent firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -p udp -i eth0 --dport 16261:16262 -j DNAT --to-destination 192.168.200.2 firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -p tcp -i eth0 --dport 16261:16262 -j DNAT --to-destination 192.168.200.2 firewall-cmd --reload

You might want to forward ICMP to your tunnel, this might aid MTU discovery, after everything is in working condition
firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -p icmp -i eth0 -j DNAT --to-destination 192.168.200.2

The script
It uses --start and --quit to start or stop the tunnel.
Edit where necessary
#!/bin/bash # Proxy Server local_public_ip="<your current vps IP>" local_internal_ip="192.168.200.1" remote_public_ip="<your target vps IP>" local_udp_port="32011" parsed_args=$(getopt -o sq --long start,quit -n "fou-setup" -- "${@}") if [[ $? -ne 0 ]]; then exit 1 fi eval set -- "${parsed_args}" while [ $# -gt 0 ]; do case "${1}" in -s|--start) varStart="true" shift ;; -q|--quit) varQuit="true" shift ;; --) shift break ;; *) echo "[fou-setup] Invalid argument '${1}'" exit 1 ;; esac done if [[ $# -gt 0 ]]; then echo "[fou-setup] Unexpected arguments: $*" exit 1 fi unset parsed_args if [[ -n ${varStart} && -n ${varQuit} ]]; then echo "[fou-setup] Cannot use both options" exit 1 fi if [[ -n ${varStart} ]]; then sysctl -w net.ipv4.ip_forward=1 modprobe fou modprobe ipip ip fou add port ${local_udp_port} ipproto 4 ip link add name tunnel0 type ipip remote ${remote_public_ip} local ${local_public_ip} ttl 225 encap fou encap-sport ${local_udp_port} encap-dport ${local_udp_port} ip addr add ${local_internal_ip}/30 dev tunnel0 ip link set tunnel0 up ip route flush cache exit 0 fi if [[ -n ${varQuit} ]]; then sysctl -w net.ipv4.ip_forward=0 ip link set tunnel0 down ip link delete tunnel0 ip fou delete port ${local_udp_port} rmmod fou rmmod ipip ip route flush cache exit 0 fi echo "[fou-setup] No option indicated" exit 1#!/bin/bash
Keep script persistent
To keep the tunnel persistent after reboot, we will make a script that run on boot time via crontab
Type
echo $PATH
Then copy that line into crontab
crontab e
It would be something like this
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @reboot sleep 5 && ~/fou-tunnel.sh --start
We add sleep because it might start before network loaded
Game server setup
Setup firewalld
tunnel0 will be considered a trusted network since it is our tunnel to the proxy server
32011 is our FOU port, you can change this if you want

We will route all connections via the tunnel, if you wish only Zomboid to be connected via the tunnel, then you will have to setup "ip netns" which is outside the scope of this guide. You can refer to my wireguard guide's network namespace section for general idea

We will mark and route direct connection back to source interface aka eth0, this will retain direct connection to your server. However, it will not allow players to directly connect to PZ as PZ does not inherently support multiple interfaces. PZ uses UDP which is connectionless thus impossible to track and forcefully redirect back to the source interface. PZ also does not reuse destination IP as source IP thus tracker has no way to identify if that UDP is meant for source interface or not.
firewall-cmd --add-interface=tunnel0 --zone=trusted --permanent firewall-cmd --zone=public --add-port=32011/udp --permanent firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 0 -i eth0 -m conntrack --ctstate NEW -p udp '!' --dport 32011 -j CONNMARK --set-mark 10 firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 0 -i eth0 -m conntrack --ctstate NEW -p tcp -j CONNMARK --set-mark 10 firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 0 -i eth0 -m conntrack --ctstate NEW -p icmp -j CONNMARK --set-mark 10 firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 1 -m mark --mark 10 -j CONNMARK --save-mark firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 0 -j CONNMARK --restore-mark firewall-cmd --reload

Add route table
Use your preferred editor
nano /etc/iproute2/rt_tables
Add the following
200 eth0 201 tunnel

The script
Edit where necessary
You can use "ip route" to see your gateway IP
You might notice a MTU line in the code, as of writing, 41.78 does not support MTU discovery thus depending on destination, the MTU can varies thus there will be packets losses. The MTU number here might not work for you, it simply work for my users base. If you uses a network that manually does their own MTU discovery and define their own iptables, then you don't need that line either.
You can use this code to manually determine MTU where 8972 is your defined MTU
ping REMOTE_HOSTNAME -c 10 -M do -s 8972
While it is totally possible to manually set MTU for every geolocation via iptables, it is extremely tedious thus I just set the most bottomline among my users base.
#!/bin/bash # Game Server local_public_ip="<your current vps ip>" local_public_gateway_ip="<your current vps gateway>" local_internal_ip="192.168.200.1" remote_public_ip="<your target vps ip>" local_udp_port="32011" parsed_args=$(getopt -o sq --long start,quit -n "fou-setup" -- "${@}") if [[ $? -ne 0 ]]; then exit 1 fi eval set -- "${parsed_args}" while [ $# -gt 0 ]; do case "${1}" in -s|--start) varStart="true" shift ;; -q|--quit) varQuit="true" shift ;; --) shift break ;; *) echo "[fou-setup] Invalid argument '${1}'" exit 1 ;; esac done if [[ $# -gt 0 ]]; then echo "[fou-setup] Unexpected arguments: $*" exit 1 fi unset parsed_args if [[ -n ${varStart} && -n ${varQuit} ]]; then echo "[fou-setup] Cannot use both options" exit 1 fi if [[ -n ${varStart} ]]; then modprobe fou modprobe ipip ip fou add port ${local_udp_port} ipproto 4 ip link add name tunnel0 type ipip remote ${remote_public_ip} local ${local_public_ip} ttl 225 encap fou encap-sport ${local_udp_port} encap-dport ${local_udp_port} ip addr add ${local_internal_ip}/30 dev tunnel0 ip link set tunnel0 up ip link set dev tunnel0 mtu 1454 ip route add default via ${local_public_gateway_ip} dev eth0 metric 100 table eth0 ip rule add fwmark 10 table eth0 ip rule add to ${remote_public_ip}/32 table eth0 ip rule add from ${local_public_ip}/32 table eth0 ip route delete default via ${local_public_gateway_ip} ip route add default via ${local_internal_ip} dev tunnel0 metric 100 ip route flush cache exit 0 fi if [[ -n ${varQuit} ]]; then ip link set tunnel0 down ip link delete tunnel0 ip fou delete port ${local_udp_port} rmmod fou rmmod ipip ip route delete default via ${local_public_gateway_ip} dev eth0 metric 100 table eth0 ip rule delete fwmark 10 table eth0 ip rule delete to ${remote_public_ip}/32 table eth0 ip rule delete from ${local_public_ip}/32 table eth0 ip route delete default via ${local_internal_ip} dev tunnel0 metric 100 ip route add default via ${local_public_gateway_ip} dev eth0 ip route flush cache exit 0 fi echo "[fou-setup] No option indicated" exit 1
Keep script persistent
To keep the tunnel persistent after reboot, we will make a script that run on boot time via crontab
Type
echo $PATH
Then copy that line into crontab
crontab e
It would be something like this
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @reboot sleep 5 && ~/fou-tunnel.sh --start
We add sleep because it might start before network loaded

Also, if you are using device name other than eth0, you can also add an alt name like this. Replace "ens3" accordingly.
@reboot ip link property add dev ens3 altname eth0