2023-10-04 13:40:15 -05:00
|
|
|
+++
|
|
|
|
title = "Tunneling Static Public IPs over WireGuard"
|
|
|
|
description = """\
|
|
|
|
A technique to assign static public IP addresses to a remote machine using WireGuard. \
|
|
|
|
"""
|
|
|
|
date = 2023-10-04
|
|
|
|
[taxonomies]
|
|
|
|
tags = [ "selfhosting", "nix", "privacy", "security", "networks" ]
|
2025-01-15 13:50:07 -06:00
|
|
|
[extra]
|
|
|
|
show_only_description = true
|
2023-10-04 13:40:15 -05:00
|
|
|
+++
|
|
|
|
|
2024-01-15 11:54:00 -06:00
|
|
|
**NOTE: My setup has drifted from this post, but I still find historical value in this
|
|
|
|
description so I am leaving the post up.**
|
|
|
|
|
2023-10-04 13:40:15 -05:00
|
|
|
# Motivation
|
|
|
|
Let's say you have a server somewhere, it has access to the internet, but maybe it:
|
|
|
|
1. is behind a [NAT][0] (or even a [CGNAT][1]); or
|
|
|
|
2. is restricted from incoming or outgoing ports (e.g. 25, 53, 80, 443, etc.); or
|
|
|
|
3. is behind a restrictive firewall; or
|
|
|
|
4. has a dynamic IP public address; or even
|
|
|
|
5. has all of the above (if like me, you're using a residential internet service provider)!
|
|
|
|
|
|
|
|
In any case, you want to self-host some services and provide access to them from the open
|
|
|
|
internet, without fighting with NAT or firewall port forwarding, dynamic DNS, or an inability
|
|
|
|
to receive incoming connections. Wouldn't it be nice if you could just rent a static, public
|
|
|
|
IP address and assign it to your server? We can get close, but we'll need the help of a VPS
|
|
|
|
provider, and some legwork to setup a [WireGuard][3] tunnel and some niche networking settings.
|
|
|
|
|
|
|
|
# Walkthrough
|
|
|
|
The first thing you'll need is a VPS. Even the smallest configurations will do. I will describe
|
|
|
|
the steps to follow if you're using [NixOS][6] on both the VPS and your server, but the steps should
|
|
|
|
translate well to any capable server OS. There are only a few criteria to look for:
|
|
|
|
1. Allows assigning at least two pairs of static, public IP addresses to your server. They will
|
|
|
|
likely give you an entire /48 or /56 of IPv6 space to use, but you will want at least two IPv4
|
|
|
|
addresses.
|
|
|
|
2. Has opened all the ports you want to host on (outgoing SMTP port 25 can be hard to find).
|
|
|
|
3. Has reputable IP addresses so you don't start out on any spam/blocklists (again,
|
|
|
|
particularly important for outgoing SMTP).
|
|
|
|
|
|
|
|
I use [Contabo][2] (not affiliated) and so far, it's been a good experience across all three
|
|
|
|
criteria.
|
|
|
|
|
|
|
|
For simplicity, let's call this VPS your gateway server, and the other your hosting server.
|
|
|
|
|
|
|
|
One of the gateway's IP address pairs it will use for itself. On this IP address, we will
|
|
|
|
host the "server" half of our WireGuard tunnel and likely SSH for administration. You can
|
|
|
|
of course also host any normal services you like. I personally use mine as a backup
|
|
|
|
nameserver. You can configure this pair of IP addresses manually on the gateway's main network
|
|
|
|
interface. On my gateway, I use the following config but yours of course may vary:
|
|
|
|
```nix
|
|
|
|
{...}: {
|
|
|
|
networking = {
|
|
|
|
enableIPv6 = true;
|
|
|
|
interfaces.ens18 = {
|
|
|
|
# Allow default route and nameservers to be autoconfigured.
|
|
|
|
useDHCP = true;
|
|
|
|
tempAddress = "disabled";
|
|
|
|
ipv4.addresses = [
|
|
|
|
{
|
|
|
|
address = "209.126.80.126";
|
|
|
|
prefixLength = 21;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
ipv6.addresses = [
|
|
|
|
{
|
|
|
|
address = "2605:a140:2146:1434:8abc:eb50:da25:5f70";
|
|
|
|
prefixLength = 64;
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
defaultGateway6 = {
|
|
|
|
address = "fe80::1";
|
|
|
|
interface = "ens18";
|
|
|
|
};
|
|
|
|
hosts = {
|
|
|
|
"209.126.80.126" = ["edinburgh.averywinters.org" "edinburgh"];
|
|
|
|
"2605:a140:2146:1434:8abc:eb50:da25:5f70" = ["edinburgh.averywinters.org" "edinburgh"];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The second IP address pair we will not assign to any network interface on the gateway. We
|
|
|
|
will, however, configure the server to use [Proxy ARP][4], proxy [NDP][5] and IP forwarding.
|
|
|
|
This way, the gateway server will responds to ARP and NDP requests for the secondary IP
|
|
|
|
address pair from the upstream router. And, once we configure the WireGuard interface, it
|
|
|
|
will forward those incoming and outgoing packets between the tunnel interface and the gateway's
|
|
|
|
primary interface. The config looks like this for me:
|
|
|
|
```nix
|
|
|
|
{...}: {
|
|
|
|
# Enable forwarding and proxy ARP + NDP so we can route our secondary IPs to
|
|
|
|
# amsterdam over wireguard.
|
|
|
|
boot.kernel.sysctl = {
|
|
|
|
"net.ipv4.conf.all.forwarding" = true;
|
|
|
|
"net.ipv6.conf.all.forwarding" = true;
|
|
|
|
"net.ipv4.conf.all.proxy_arp" = true;
|
|
|
|
"net.ipv6.conf.all.proxy_ndp" = true;
|
|
|
|
};
|
|
|
|
# Proxy ARP and NDP (adding the IPv4 address here is optional,
|
|
|
|
# but I like the consistency).
|
|
|
|
networking.localCommands = ''
|
|
|
|
ip -4 neigh add proxy 154.12.229.99 dev ens18
|
|
|
|
ip -6 neigh add proxy 2605:a140:2146:1434:a2c5:34bb:afcf:101d dev ens18
|
|
|
|
'';
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Next, we will setup the server end of the WireGuard tunnel on our gateway server like so.
|
|
|
|
Once this is up, the gateway server will start forwarding any packets it gets over the
|
|
|
|
tunnel.
|
|
|
|
```nix
|
|
|
|
{config, ...}: {
|
|
|
|
# Route our secondary IP addresses to amsterdam.
|
|
|
|
networking.wg-quick.interfaces.wg-amsterdam = {
|
|
|
|
# The addresses here are our gateway's IP as seen from the hosting server inside
|
|
|
|
# the tunnel. You can pick any private addresses you like.
|
|
|
|
address = ["192.168.88.3/32" "fd3a:e9a5:7a16:3d11:55c8:e71e:74d3:2c3a/128"];
|
|
|
|
listenPort = 51820;
|
|
|
|
privateKeyFile = config.age.secrets.wireguardKey.path;
|
|
|
|
peers = [
|
|
|
|
{
|
|
|
|
publicKey = "zMxFfsMdUEtK/n0+OK+4z0db+FTl/SH1F7EB27Z8jls=";
|
|
|
|
allowedIPs = ["154.12.229.99/32" "2605:a140:2146:1434:a2c5:34bb:afcf:101d/128"];
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Finally, we configure the client end of the WireGuard tunnel on our hosting server. This
|
|
|
|
interface will also be configured as the default route (for anything that isn't the
|
|
|
|
WireGuard traffic itself of course).
|
|
|
|
```nix
|
|
|
|
{...}: {
|
|
|
|
# Use edinburgh's secondary IP addresses as our public IP and default route.
|
|
|
|
networking = {
|
|
|
|
enableIPv6 = true;
|
|
|
|
hosts = {
|
|
|
|
"154.12.229.99" = ["amsterdam.averywinters.org" "amsterdam"];
|
|
|
|
"2605:a140:2146:1434:a2c5:34bb:afcf:101d" = ["amsterdam.averywinters.org" "amsterdam"];
|
|
|
|
};
|
|
|
|
wg-quick.interfaces.wg-edinburgh = {
|
|
|
|
address = ["154.12.229.99/32" "2605:a140:2146:1434:a2c5:34bb:afcf:101d/128"];
|
|
|
|
privateKeyFile = config.age.secrets.wireguardKey.path;
|
|
|
|
peers = [
|
|
|
|
{
|
|
|
|
# edinburgh, but don't use fqdn because DNS isn't working yet
|
|
|
|
endpoint = "209.126.80.126:51820";
|
|
|
|
publicKey = "iH7+ZqiqYJ/HMDshLP8gjdZYd175frk6qi4YBKaw0BA=";
|
|
|
|
# needed to keep the tunnel alive behind a NAT or dynamic IP
|
|
|
|
persistentKeepalive = 25;
|
|
|
|
allowedIPs = ["0.0.0.0/0" "::/0"];
|
|
|
|
}
|
|
|
|
];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Once you've done this, your hosting server should be able to accept incoming traffic
|
|
|
|
on its new IP addresses, and should have all its outgoing traffic originate from its
|
2023-10-04 13:50:12 -05:00
|
|
|
new IP addresses as well! The code for my full server configs can be found [here][7].
|
2023-10-04 13:40:15 -05:00
|
|
|
|
|
|
|
[0]: https://en.wikipedia.org/wiki/Network_address_translation
|
|
|
|
[1]: https://en.wikipedia.org/wiki/Carrier-grade_NAT
|
|
|
|
[2]: https://contabo.com/en/
|
|
|
|
[3]: https://www.wireguard.com
|
|
|
|
[4]: https://en.wikipedia.org/wiki/Proxy_ARP
|
|
|
|
[5]: https://en.wikipedia.org/wiki/Neighbor_Discovery_Protocol
|
2023-10-04 13:50:12 -05:00
|
|
|
[6]: https://nixos.org
|
2025-01-16 15:59:27 -06:00
|
|
|
[7]: https://git.averywinters.org/avery/home
|