Add post about wireguard IP tunneling
This commit is contained in:
parent
f8651c959f
commit
86bc53e0a7
2 changed files with 170 additions and 6 deletions
|
@ -10,8 +10,6 @@ updated = 2023-10-04
|
||||||
tags = [ "selfhosting", "nix", "privacy", "security", "networks" ]
|
tags = [ "selfhosting", "nix", "privacy", "security", "networks" ]
|
||||||
+++
|
+++
|
||||||
|
|
||||||
# Security & Infrastructure
|
|
||||||
|
|
||||||
**Note**: This post was updated to reflect a change in the number of servers
|
**Note**: This post was updated to reflect a change in the number of servers
|
||||||
I use to host everything.
|
I use to host everything.
|
||||||
|
|
||||||
|
@ -33,12 +31,12 @@ nameserver in case my home network is down.
|
||||||
The goal with all of this is to have some basic redundancy, while keeping
|
The goal with all of this is to have some basic redundancy, while keeping
|
||||||
sensitive keys and all personal data safely on my physical server.
|
sensitive keys and all personal data safely on my physical server.
|
||||||
|
|
||||||
## DNSSEC
|
# DNSSEC
|
||||||
`amsterdam` holds a [combined signing key][13] for the zone. Dynamic updates
|
`amsterdam` holds a [combined signing key][13] for the zone. Dynamic updates
|
||||||
are allowed using a [TSIG][1] key to allow [ACME DNS-01 challenges][2] for
|
are allowed using a [TSIG][1] key to allow [ACME DNS-01 challenges][2] for
|
||||||
issuing TLS certificates.
|
issuing TLS certificates.
|
||||||
|
|
||||||
## TLS/HTTPS
|
# TLS/HTTPS
|
||||||
`amsterdam` holds a [Let's Encrypt][3] wildcard TLS certificate for the domain,
|
`amsterdam` holds a [Let's Encrypt][3] wildcard TLS certificate for the domain,
|
||||||
which is used to protect web services. The DNS zone contains a [CAA][4] record
|
which is used to protect web services. The DNS zone contains a [CAA][4] record
|
||||||
specifying that only Let's Encrypt may issue certificates for the domain, and
|
specifying that only Let's Encrypt may issue certificates for the domain, and
|
||||||
|
@ -46,13 +44,13 @@ only using ACME DNS-01 challenges. All TLS-capable services have TLSA records
|
||||||
associated with them for [DANE-EE][5] support. Finally, all web services use
|
associated with them for [DANE-EE][5] support. Finally, all web services use
|
||||||
[HTTPS][6] records and [HSTS preload][7] headers to advertise support for HTTPS.
|
[HTTPS][6] records and [HSTS preload][7] headers to advertise support for HTTPS.
|
||||||
|
|
||||||
## Email
|
# Email
|
||||||
`amsterdam` holds [DKIM][8] keys for the domain, which is published in DNS
|
`amsterdam` holds [DKIM][8] keys for the domain, which is published in DNS
|
||||||
alongside [SPF][9] and [DMARC][10] records together protect against spoofing
|
alongside [SPF][9] and [DMARC][10] records together protect against spoofing
|
||||||
the domain. [MTA-STS][11] and DANE-EE are used to advertise TLS support for
|
the domain. [MTA-STS][11] and DANE-EE are used to advertise TLS support for
|
||||||
incoming mail. Outgoing mail requires that the receiving server support TLS.
|
incoming mail. Outgoing mail requires that the receiving server support TLS.
|
||||||
|
|
||||||
## WireGuard
|
# WireGuard
|
||||||
Both servers hold [WireGuard][14] keys for their end of the tunnels. The tunnel
|
Both servers hold [WireGuard][14] keys for their end of the tunnels. The tunnel
|
||||||
being encrypted and authenticated isn't actually important for my purposes. This
|
being encrypted and authenticated isn't actually important for my purposes. This
|
||||||
could just as easily use another tunneling protocol like [GRE][12], but I find
|
could just as easily use another tunneling protocol like [GRE][12], but I find
|
||||||
|
|
166
content/2023-10-04-wireguard-public-ip.md
Normal file
166
content/2023-10-04-wireguard-public-ip.md
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
+++
|
||||||
|
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" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|
# 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
|
||||||
|
new IP addresses as well!
|
||||||
|
|
||||||
|
[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
|
||||||
|
[6]: https://nixos.org
|
Loading…
Reference in a new issue