feat(mesh): add selected Telegram and OpenRouter egress policy
This commit is contained in:
parent
6e88f2c833
commit
56bb3d7f7c
2 changed files with 80 additions and 1 deletions
|
|
@ -22,6 +22,7 @@
|
|||
./tuwunel-admin.nix # matrix admin web UI (systemd, статический бинарник)
|
||||
./pi-web.nix # pi-web (web UI для pi coding agent, systemd + npm global)
|
||||
./mesh-clients.nix # Sol-style client pool (wg-clients1)
|
||||
./mesh-egress.nix # Sol-style selected-destination egress policy
|
||||
];
|
||||
|
||||
# First server-to-server WireGuard backbone link: heather ↔ hyacinth.
|
||||
|
|
@ -57,7 +58,9 @@
|
|||
peers = [
|
||||
{
|
||||
publicKey = "BxTH8+MiA8Hv48UfpWJDVXIhbO5HgO2KTBEXIqqr23k=";
|
||||
allowedIPs = [ "10.99.0.2/32" "10.0.0.2/32" ];
|
||||
# Match all destinations for future policy routes; allowedIPsAsRoutes=false
|
||||
# keeps this from installing a default route automatically.
|
||||
allowedIPs = [ "0.0.0.0/0" ];
|
||||
endpoint = "195.242.119.59:51820";
|
||||
persistentKeepalive = 25;
|
||||
}
|
||||
|
|
|
|||
76
hosts/heather/mesh-egress.nix
Normal file
76
hosts/heather/mesh-egress.nix
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# 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.glibc}/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";
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue