From 86bc53e0a731e2d14e11004ef124779e1d06ff35 Mon Sep 17 00:00:00 2001 From: Avery Winters Date: Wed, 4 Oct 2023 13:40:15 -0500 Subject: [PATCH] Add post about wireguard IP tunneling --- content/2023-09-14-security-infrastructure.md | 10 +- content/2023-10-04-wireguard-public-ip.md | 166 ++++++++++++++++++ 2 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 content/2023-10-04-wireguard-public-ip.md diff --git a/content/2023-09-14-security-infrastructure.md b/content/2023-09-14-security-infrastructure.md index be00f2e..2f20e81 100644 --- a/content/2023-09-14-security-infrastructure.md +++ b/content/2023-09-14-security-infrastructure.md @@ -10,8 +10,6 @@ updated = 2023-10-04 tags = [ "selfhosting", "nix", "privacy", "security", "networks" ] +++ -# Security & Infrastructure - **Note**: This post was updated to reflect a change in the number of servers 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 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 are allowed using a [TSIG][1] key to allow [ACME DNS-01 challenges][2] for issuing TLS certificates. -## TLS/HTTPS +# TLS/HTTPS `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 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 [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 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 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 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 diff --git a/content/2023-10-04-wireguard-public-ip.md b/content/2023-10-04-wireguard-public-ip.md new file mode 100644 index 0000000..8cf3cc1 --- /dev/null +++ b/content/2023-10-04-wireguard-public-ip.md @@ -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 \ No newline at end of file