# 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 <<< '' # 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 } ''; }; }