nix-config/hosts/heather/mesh-egress.nix

76 lines
2.4 KiB
Nix

# Static, Sol-style destination policy for selected traffic via the EU peer.
# This is policy routing, not application learning: prefixes are declared here,
# and OpenRouter addresses are refreshed from DNS on a timer.
{ config, pkgs, ... }:
let
ip = "${pkgs.iproute2}/bin/ip";
getent = "${pkgs.getent}/bin/getent";
routeScript = pkgs.writeShellScript "mesh-egress-routes" ''
set -eu
${ip} link show wg-mesh >/dev/null
# Rebuild the destination table, while leaving the normal main table alone.
${ip} route flush table 1101 2>/dev/null || true
${ip} rule del pref 1101 2>/dev/null || true
${ip} rule add pref 1101 lookup 1101
add_route() {
prefix="$1"
${ip} route replace "$prefix" dev wg-mesh table 1101
}
# Telegram DC ranges (static destination policy, as in Sol's prefix lists).
for prefix in \
91.105.192.0/23 \
91.108.0.0/22 \
91.108.4.0/22 \
91.108.8.0/22 \
91.108.12.0/22 \
91.108.16.0/22 \
91.108.20.0/22 \
91.108.56.0/22 \
95.161.64.0/20 \
149.154.160.0/20; do
add_route "$prefix"
done
# OpenRouter is behind a CDN; refresh its current IPv4 addresses. We do
# not route all Cloudflare, only the names used by the API/web endpoint.
for name in openrouter.ai www.openrouter.ai api.openrouter.ai; do
${getent} ahostsv4 "$name" 2>/dev/null | ${pkgs.gawk}/bin/awk '{print $1}' | sort -u | while read -r addr; do
[ -n "$addr" ] || continue
add_route "$addr/32"
done
done
'';
in {
# Clients whose selected destinations match table 1101 are forwarded to
# hyacinth. Other traffic keeps Heather's normal route.
networking.nat = {
enable = true;
internalInterfaces = [ "wg-clients1" ];
externalInterface = "enp7s0";
};
systemd.services.mesh-egress-routes = {
description = "Install Sol-style selected destination routes via hyacinth";
wantedBy = [ "network-online.target" ];
after = [ "network-online.target" "wireguard-wg-mesh.service" ];
wants = [ "wireguard-wg-mesh.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = routeScript;
};
};
systemd.timers.mesh-egress-routes = {
description = "Refresh selected mesh egress destinations";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "30s";
OnUnitActiveSec = "5min";
Unit = "mesh-egress-routes.service";
};
};
}