WireGuard Site-to-Site
Accessing a subnet that is behind a WireGuard client using a site-to-site setup
Problem Summary
We want to access a local subnet remotely, but it is behind a NAT firewall and we can't setup port forwarding. Outgoing connections work, but all incoming connections get DROPPED by the ISP's routing policy.
Solution Summary
We'll create a site-to-site connection with WireGuard allowing us to access the local subnet on a remote device (smartphone, in this example) by connecting through a cloud server in the middle.
Working Example
First let's define our three hosts. They all have WireGuard installed.
A
the Linux machine on the local subnet, behind the NAT/firewall
B
the Linux cloud server (VPS, like an Amazon EC2 instance)
C
a third WireGuard client; a smartphone in this example
Host 'A'
The Host A's /etc/wireguard/wg0-client.conf
:
[Interface]
Address = 10.200.200.5/24
PrivateKey = <HOST 'A' PRIVATE-KEY>
ListenPort = 27836 # optional; will be randomly assigned otherwise
DNS = 1.1.1.1 # or your own DNS server if you're running one
[Peer]
PublicKey = <PUBLIC KEY OF HOST 'B'>
Endpoint = host-b-fqdn.tld:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25 # to keep connections alive across NAT
Here's what we need to add to Host A's iptables
rules, expressed as the commands you would use to ADD them:
# iptables -A FORWARD -i wg0-client -j ACCEPT
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Finally, we need to make sure IP forwarding is enabled in Host A's kernel:
$ sysctl net.ipv4.ip_forward=1
Host 'B'
Host B's /etc/wireguard/wg0.conf
:
[Interface]
Address = 10.200.200.1/24
PrivateKey = <HOST 'B' PRIVATE KEY>
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE
# This is the peer that is on the private subnet that we want to access.
#
# Notice the AllowedIPs... without this part, WireGuard will drop the
# packets destined for the HOST 'A' subnet. AllowedIPs is acting like
# a routing table and ACL here.
[Peer]
PublicKey = <HOST 'A' PUBLIC KEY>
AllowedIPs = 10.200.200.5/32, 100.10.202.0/24
# The smartphone
[Peer]
PublicKey = <HOST 'C' PUBLIC KEY>
AllowedIPs = 10.200.200.3/32
# An additional peer...
[Peer]
PublicKey = <Additional peer pubkey>
AllowedIPs = 10.200.200.4/32
Like we did with Host A, IP forwarding must also be enabled on Host B:
$ sysctl net.ipv4.ip_forward=1
Host C
Host C's configuration file:
[Interface]
PrivateKey = <HOST 'C' PRIVATE KEY>
Address = 10.200.200.3/24
DNS = 1.1.1.1
[Peer]
PublicKey = <HOST 'B' PUBLIC KEY>
AllowedIPs = 0.0.0.0/0
Endpoint = host-b-fqdn.tld:51820
PersistentKeepalive = 25
You're finished.
Make sure WireGuard is running on both HOSTS A and B, and then on the smartphone (HOST C), after connecting to HOST B with WireGuard you should be able to ping 10.200.200.5
.
Hi insdavm,
First of al thanks for your help! i have the feeling that i'm very close to get this working but I can't figure out what i'm missing...
1. Are the two RPi's on the same local subnet?
Yes they are. Although the final goal is to have the RPi's in different local subnets, for practical reasons the testing is now in the same subnet. I assume this does not make a difference, or does it? Both RPi's reach out independent to the EC2 node, so I would think it does not matter if they are in the same or different local subnet. Or is there something I overlook?
2. What IP are you trying to connect to (i.e., tunnel IP, local subnet IP)?
I try to connect via the tunnel IP's. The local IP's are 192.168.68 and 192.168.109. When using the local addresses, ping and ssh does work in both direcions.
The tunnel IP's are 100.64.0.101 and 100.64.0.102 (the EC2 node acts as wg server and has IP address 100.64.0.1)
Ping works between all 3 nodes.
ssh only from 100.64.0.1 to both 100.64.0.101 and 100.64.0.102
ssh does not work from 100.64.0.101 to 100.64.0.102
As mentioned in my first message, running an http server on 100.64.0.101 and chatting to it with telnet on 100.64.0.102 (and the other way around) does work...
3. Can you confirm SSH is listening on 0.0.0.0:22 on both RPi's? (sudo ss -tunlp)
this is confirmed:
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=564,fd=3))
Here how the configuration of the nodes look:
pi@rp1: ~ sudo wg show
interface: wg0
public key: vL2byDvX281fra3Xe9JSTu3CjHypL1UzHNfLPDCkI2A=
private key: (hidden)
listening port: 51505
fwmark: 0xca6c
peer: NuHDg0RykQ6hdevLxBaSlCuTuSD1QlxKiG2qmzUZywM=
endpoint: "external-ip-address-of-EC2-node":42840
allowed ips: 0.0.0.0/0, ::/0
latest handshake: 18 seconds ago
transfer: 3.20 KiB received, 6.59 KiB sent
persistent keepalive: every 25 seconds
pi@rp2:~ $ sudo wg show
interface: wg0
public key: N//AIetJgc6W6AmU24sJeuewB2ZKuJbFX8VqzcbkYjA=
private key: (hidden)
listening port: 40374
fwmark: 0xca6c
peer: NuHDg0RykQ6hdevLxBaSlCuTuSD1QlxKiG2qmzUZywM=
endpoint:"external-ip-address-of-EC2-node":42840
allowed ips: 0.0.0.0/0, ::/0
latest handshake: 35 seconds ago
transfer: 34.13 MiB received, 66.13 MiB sent
persistent keepalive: every 25 seconds
ubuntu@my_ec2:~$ sudo wg show
interface: wg0
public key: NuHDg0RykQ6hdevLxBaSlCuTuSD1QlxKiG2qmzUZywM=
private key: (hidden)
listening port: 42840
peer: qd+nyR96UNNQXuWqoYhMJ3QBSwhwyBAnaKLOzzNj2xM=
endpoint: "my-external-ISP-ip-address":48311
allowed ips: 100.64.0.102/32
latest handshake: 1 minute, 21 seconds ago
transfer: 360.70 KiB received, 103.08 KiB sent
persistent keepalive: every 25 seconds
peer: vL2byDvX281fra3Xe9JSTu3CjHypL1UzHNfLPDCkI2A=
endpoint: "my-external-ISP-ip-address":48221
allowed ips: 100.64.0.101/32
latest handshake: 1 minute, 22 seconds ago
transfer: 313.04 KiB received, 128.68 KiB sent
persistent keepalive: every 25 seconds