Jump to content

This guide uses Ubuntu Server 22.04 as the base however minor tweaks should allow this to apply to other Linux instances as well:

 

Step 1 - Install Wireguard and UFW:

sudo apt install wireguard wireguard-tools ufw

 

 

 

Step 2 - Generate Public/Private Keys:

Option 1:

wg genkey | sudo tee /etc/wireguard/client_private.key | wg pubkey | sudo tee /etc/wireguard/client_public.key

Option 2:

Use this: https://www.wireguardconfig.com/

 

 

 

Step 3 - Setup Wireguard Config File for Server:

Edit WG0.conf file:

sudo nano /etc/wireguard/wg0.conf

Paste in the following or edit addresses to your liking:

[Interface]
Address = 10.10.10.1/24, 2000:10:10::1/64
ListenPort = 51820
PrivateKey = [Private Key for Server]
Table = 1000
PostUp = ip rule add from 10.10.10.0/24 table 1000
PostUp = ip -6 rule add from 2000:10:10::/64 table 1000
PostDown = ip rule del from 10.10.10.0/24 table 1000
PostDown = ip -6 rule del from 2000:10:10::/64 table 1000

[Peer]
PublicKey = [Public Key of Connecting Peer 1]
PresharedKey = [PSK for Peer 1]
AllowedIPs = 10.10.10.2/32, 2001:10:10:10::2/128

[Peer]
PublicKey = [Public Key of Connecting Peer 2]
PresharedKey = [PSK for Peer 2]
AllowedIPs = 10.10.10.3/32, 2001:10:10:10::3/128

Use Ctrl + o and Ctrl + x to save and exit

 

Step 4 - Protect WG Config File:

sudo chmod 600 /etc/wireguard/ -R

 

Step 5 - Setup Forwarding:

sudo nano /etc/sysctl.conf

Add to end of file:

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Reload sysctl to apply options

sudo sysctl -p

 

Step 5 - Setup UFW:

This ensures you retain SSH access and permit Wireguard into the server

sudo ufw allow 22/tcp
sudo ufw allow 51820/udp

Edit the before.rules file:

sudo nano /etc/ufw/before.rules

Add this ALL  to the very end of the file:

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.10.10.0/24 -o vpn-relay -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

Insert this into the before.rules file but before the FIRST commit line:

# allow forwarding for trusted network
-A ufw-before-forward -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward -d 10.10.10.0/24 -j ACCEPT

 

Similar steps for IPv6 as well:

sudo nano /etc/ufw/before6.rules

Put near the bottom but before the first COMMIT line:

# allow forwarding for trusted network
-A ufw6-before-forward -s 2001:10:10:10::/64 -j ACCEPT
-A ufw6-before-forward -d 2001:10:10:10::/64 -j ACCEPT

 

Put at the very end:

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 2001:10:10:10::/64 -o vpn-relay -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

 

 

Example of complete before.rules file:

Spoiler
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
# End required lines


# allow all on loopback
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-output -o lo -j ACCEPT

# quickly process packets for which we already have a connection
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# drop INVALID packets (logs these in loglevel medium and higher)
-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP

# ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT

# ok icmp code for FORWARD
-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT

# allow forwarding for trusted network
-A ufw-before-forward -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward -d 10.10.10.0/24 -j ACCEPT

# allow dhcp client to work
-A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT

#
# ufw-not-local
#
-A ufw-before-input -j ufw-not-local

# if LOCAL, RETURN
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN

# if MULTICAST, RETURN
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN

# if BROADCAST, RETURN
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN

# all other non-local packets are dropped
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
-A ufw-not-local -j DROP

# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 0.0.0.0/0 -d 0.0.0.0/0 -o vpn-relay -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

 

 

Example before6.rules file:

Spoiler
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw6-before-input
#   ufw6-before-output
#   ufw6-before-forward
#

# Don't delete these required lines, otherwise there will be errors
*filter
:ufw6-before-input - [0:0]
:ufw6-before-output - [0:0]
:ufw6-before-forward - [0:0]
# End required lines


# allow all on loopback
-A ufw6-before-input -i lo -j ACCEPT
-A ufw6-before-output -o lo -j ACCEPT

# drop packets with RH0 headers
-A ufw6-before-input -m rt --rt-type 0 -j DROP
-A ufw6-before-forward -m rt --rt-type 0 -j DROP
-A ufw6-before-output -m rt --rt-type 0 -j DROP

# quickly process packets for which we already have a connection
-A ufw6-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw6-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw6-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# multicast ping replies are part of the ok icmp codes for INPUT (rfc4890,
# 4.4.1 and 4.4.2), but don't have an associated connection and are otherwise
# be marked INVALID, so allow here instead.
-A ufw6-before-input -p icmpv6 --icmpv6-type echo-reply -j ACCEPT

# drop INVALID packets (logs these in loglevel medium and higher)
-A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-logging-deny
-A ufw6-before-input -m conntrack --ctstate INVALID -j DROP

# ok icmp codes for INPUT (rfc4890, 4.4.1 and 4.4.2)
-A ufw6-before-input -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
# codes 0 and 1
-A ufw6-before-input -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
# codes 0-2 (echo-reply needs to be before INVALID, see above)
-A ufw6-before-input -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
# IND solicitation
-A ufw6-before-input -p icmpv6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT
# IND advertisement
-A ufw6-before-input -p icmpv6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT
# MLD query
-A ufw6-before-input -p icmpv6 --icmpv6-type 130 -s fe80::/10 -j ACCEPT
# MLD report
-A ufw6-before-input -p icmpv6 --icmpv6-type 131 -s fe80::/10 -j ACCEPT
# MLD done
-A ufw6-before-input -p icmpv6 --icmpv6-type 132 -s fe80::/10 -j ACCEPT
# MLD report v2
-A ufw6-before-input -p icmpv6 --icmpv6-type 143 -s fe80::/10 -j ACCEPT
# SEND certificate path solicitation
-A ufw6-before-input -p icmpv6 --icmpv6-type 148 -m hl --hl-eq 255 -j ACCEPT
# SEND certificate path advertisement
-A ufw6-before-input -p icmpv6 --icmpv6-type 149 -m hl --hl-eq 255 -j ACCEPT
# MR advertisement
-A ufw6-before-input -p icmpv6 --icmpv6-type 151 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT
# MR solicitation
-A ufw6-before-input -p icmpv6 --icmpv6-type 152 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT
# MR termination
-A ufw6-before-input -p icmpv6 --icmpv6-type 153 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT

# ok icmp codes for OUTPUT (rfc4890, 4.4.1 and 4.4.2)
-A ufw6-before-output -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
# codes 0 and 1
-A ufw6-before-output -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
# codes 0-2
-A ufw6-before-output -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type echo-reply -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
-A ufw6-before-output -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
# IND solicitation
-A ufw6-before-output -p icmpv6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT
# IND advertisement
-A ufw6-before-output -p icmpv6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT
# MLD query
-A ufw6-before-output -p icmpv6 --icmpv6-type 130 -s fe80::/10 -j ACCEPT
# MLD report
-A ufw6-before-output -p icmpv6 --icmpv6-type 131 -s fe80::/10 -j ACCEPT
# MLD done
-A ufw6-before-output -p icmpv6 --icmpv6-type 132 -s fe80::/10 -j ACCEPT
# MLD report v2
-A ufw6-before-output -p icmpv6 --icmpv6-type 143 -s fe80::/10 -j ACCEPT
# SEND certificate path solicitation
-A ufw6-before-output -p icmpv6 --icmpv6-type 148 -m hl --hl-eq 255 -j ACCEPT
# SEND certificate path advertisement
-A ufw6-before-output -p icmpv6 --icmpv6-type 149 -m hl --hl-eq 255 -j ACCEPT
# MR advertisement
-A ufw6-before-output -p icmpv6 --icmpv6-type 151 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT
# MR solicitation
-A ufw6-before-output -p icmpv6 --icmpv6-type 152 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT
# MR termination
-A ufw6-before-output -p icmpv6 --icmpv6-type 153 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT

# ok icmp codes for FORWARD (rfc4890, 4.3.1)
-A ufw6-before-forward -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
-A ufw6-before-forward -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
# codes 0 and 1
-A ufw6-before-forward -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
# codes 0-2
-A ufw6-before-forward -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
-A ufw6-before-forward -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-A ufw6-before-forward -p icmpv6 --icmpv6-type echo-reply -j ACCEPT
# ok icmp codes for FORWARD (rfc4890, 4.3.2)
# Home Agent Address Discovery Reques
-A ufw6-before-input -p icmpv6 --icmpv6-type 144 -j ACCEPT
# Home Agent Address Discovery Reply
-A ufw6-before-input -p icmpv6 --icmpv6-type 145 -j ACCEPT
# Mobile Prefix Solicitation
-A ufw6-before-input -p icmpv6 --icmpv6-type 146 -j ACCEPT
# Mobile Prefix Advertisement
-A ufw6-before-input -p icmpv6 --icmpv6-type 147 -j ACCEPT

# allow dhcp client to work
-A ufw6-before-input -p udp -s fe80::/10 --sport 547 -d fe80::/10 --dport 546 -j ACCEPT

# allow MULTICAST mDNS for service discovery
-A ufw6-before-input -p udp -d ff02::fb --dport 5353 -j ACCEPT

# allow MULTICAST UPnP for service discovery
-A ufw6-before-input -p udp -d ff02::f --dport 1900 -j ACCEPT

# allow forwarding for trusted networks
-A ufw6-before-forward -s 2001:10:10:10::/64 -j ACCEPT
-A ufw6-before-forward -d 2001:10:10:10::/64 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 2001:10:10:10::/64 -o vpn-relay -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

 

 

 

Step 6 - Enable and Restart UFW:

sudo ufw enable
sudo systemctl restart ufw

 

 

Step 7 - Enable and Start Wireguard:

sudo systemctl start wg-quick@wg0.service
sudo systemctl enable wg-quick@wg0.service
systemctl status wg-quick@wg0.service

Can also verify with 'sudo wg' or 'sudo wg show'

 

 

 

Step 8 - Setup the Relay:

sudo nano /etc/wireguard/vpn-relay.conf

Add the lines to the config and modify as needed, Address will be the address for the local server (A) when connected to remote server (B)

[Interface]
Address = 20.10.10.200/32, 2001:20:10:10::200/64
PrivateKey = [Private Key of local server]
Table = 1000
PostUp = iptables -A FORWARD -i vpn-relay -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -d 0.0.0.0/0 -o vpn-relay -j MASQUERADE
PostUp = ip6tables -A FORWARD -i vpn-relay -j ACCEPT; ip6tables -t nat -A POSTROUTING -o vpn-relay -j MASQUERADE
PostDown = iptables -D FORWARD -i vpn-relay -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s 10.10.10.0/24 -d 0.0.0.0/0 -o vpn-relay -j MASQUERADE
PostDown = ip6tables -D FORWARD -i vpn-relay -j ACCEPT; ip6tables -t nat -D POSTROUTING -o vpn-relay -j MASQUERADE

[Peer]
#Server B
PublicKey = [Public Key of Server B]
PresharedKey = [PreShared Key with Server B]
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 12.34.56.78:51820
PersistentKeepalive = 25

 

 

 

Step 9 - Enable and Bring up Relay interface:

sudo systemctl start wg-quick@vpn-relay.service
sudo systemctl enable wg-quick@pn-relay.service
systemctl status wg-quick@pn-relay.service

 

 

Step 10 - Additional Setup:

Now configure your client and exit server (Server B) to peer to the server.

That should do it, now when you connect a Client to Server A all traffic should end up exiting Server B and appear to be coming from Server B

You could even setup multiple relays in between and just peer from A to B and then B to C, etc. following the above steps.

 

 

 

Reference Material:

https://www.linuxbabe.com/ubuntu/wireguard-vpn-server-ubuntu

https://www.linuxbabe.com/vpn/wireguard-vpn-relay

Current Network Layout:

Current Build Log/PC:

Storage Server Setup:

 

Prior Build Log/PC:

Link to comment
https://linustechtips.com/topic/1575232-wireguard-relay-node-setup/
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×