router: route imgur via WireGuard tunnel

This commit is contained in:
rope 2026-07-30 19:24:56 +01:00
parent d8d8400519
commit bfdd0ebc44
2 changed files with 75 additions and 0 deletions

View file

@ -38,6 +38,7 @@
./services/shelfarr.nix ./services/shelfarr.nix
./services/adguard.nix ./services/adguard.nix
./services/router.nix ./services/router.nix
./services/imgur-vpn.nix
./services/crowdsec.nix ./services/crowdsec.nix
./services/service-health.nix ./services/service-health.nix
./services/sabnzbd.nix ./services/sabnzbd.nix

74
services/imgur-vpn.nix Normal file
View file

@ -0,0 +1,74 @@
# services/imgur-vpn.nix — route imgur out a WireGuard tunnel, network-wide
#
# imgur geo-blocks UK IPs, so LAN clients need a non-UK exit for imgur only.
# imgur.com / i.imgur.com / api.imgur.com all resolve to two Fastly anycast
# /24s; we send just those prefixes down the tunnel and leave everything else
# on eno1.
#
# ponytail: static prefixes, not a DNS-driven nftset. Other Fastly customers
# sharing these /24s ride the tunnel too — harmless, just a longer path. If
# that ever matters, add dnsmasq `nftset=` + fwmark policy routing instead.
#
# Setup (one-off, on the mediaserver):
# 1. Fill in peerPublicKey / peerEndpoint / tunnelAddress below from your
# VPN provider's WireGuard config (pick a non-UK exit).
# 2. Write the private key, no trailing newline, readable by systemd-network:
# install -m600 -o systemd-network -g systemd-network \
# /dev/stdin /var/secrets/wg-imgur.key <<< '<PRIVATE_KEY>'
# 3. Rebuild. Verify: curl --interface wg-imgur https://api.imgur.com/
{ config, lib, ... }:
let
# --- provider details: fill these in ---
peerPublicKey = "@PEER_PUBLIC_KEY@";
peerEndpoint = "@HOST_OR_IP@:51820";
tunnelAddress = "@TUNNEL_IP@/32"; # address the provider assigned us
# imgur's Fastly anycast prefixes.
imgurNets = [ "199.232.192.0/24" "199.232.196.0/24" ];
in
{
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
systemd.network = {
netdevs."30-wg-imgur" = {
netdevConfig = {
Name = "wg-imgur";
Kind = "wireguard";
};
# Key read at runtime — keeps flake eval pure (hosts build from Forgejo).
wireguardConfig.PrivateKeyFile = "/var/secrets/wg-imgur.key";
wireguardPeers = [{
PublicKey = peerPublicKey;
Endpoint = peerEndpoint;
# Doubles as the tunnel's crypto-routing scope: only imgur goes in.
AllowedIPs = imgurNets;
PersistentKeepalive = 25;
}];
};
networks."30-wg-imgur" = {
matchConfig.Name = "wg-imgur";
address = [ tunnelAddress ];
# Plain main-table routes: destination-based, so no fwmark/ip-rule
# machinery and no rp_filter breakage on the reply path.
routes = map (n: { Destination = n; }) imgurNets;
linkConfig.RequiredForOnline = "no";
};
};
# LAN sources must be NAT'd to the tunnel address, and TCP MSS clamped —
# the tunnel's 1420 MTU otherwise black-holes full-size segments.
networking.nftables.tables.router-nat.content = lib.mkAfter ''
chain wg_imgur_postrouting {
type nat hook postrouting priority 101; policy accept;
oifname "wg-imgur" masquerade
}
'';
networking.nftables.tables.filter.content = lib.mkAfter ''
chain wg_imgur_mss {
type filter hook forward priority 0; policy accept;
oifname "wg-imgur" tcp flags syn tcp option maxseg size set rt mtu
}
'';
};
}