heather: replace personal mesh with guarded AstrNet stub
This commit is contained in:
parent
4efbdc9bda
commit
2ad5653fda
4 changed files with 203 additions and 175 deletions
199
hosts/heather/astrnet.nix
Normal file
199
hosts/heather/astrnet.nix
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
# astrnet.nix — AstrNet stub peer "michaotic". Generated by astrnet-join.sh.
|
||||||
|
#
|
||||||
|
# What this host is to the mesh: identity 10.0.0.9 / 2a05:f480:2400:1cf7::9,
|
||||||
|
# ASN 4200000009, two WireGuard legs that THIS host dials:
|
||||||
|
# wg-mic-der -> alderamin (Helsinki) primary, pair 38
|
||||||
|
# wg-mic-bd8 -> bd8816 (Moscow) backup, pair 39 (paths come prepended)
|
||||||
|
# It announces only its identity and accepts only mesh address space; it
|
||||||
|
# never becomes transit and never takes a default route from the mesh.
|
||||||
|
#
|
||||||
|
# All values are assigned by the AstrNet admin. Do not renumber anything
|
||||||
|
# here without them: the far ends are configured to match exactly.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
keyFile = "/var/lib/astrnet/wg.key";
|
||||||
|
id4 = "10.0.0.9";
|
||||||
|
id6 = "2a05:f480:2400:1cf7::9";
|
||||||
|
|
||||||
|
mkLeg = { listenPort, ips, publicKey, endpoint }: {
|
||||||
|
inherit ips listenPort;
|
||||||
|
privateKeyFile = keyFile;
|
||||||
|
mtu = 1380;
|
||||||
|
# Routing is BGP's job. allowedIPs 0/0 only PERMITS any source through
|
||||||
|
# the tunnel; with routes off, it installs nothing. Turning routes on
|
||||||
|
# would hijack this host's default route.
|
||||||
|
allowedIPsAsRoutes = false;
|
||||||
|
peers = [{
|
||||||
|
inherit publicKey endpoint;
|
||||||
|
allowedIPs = [ "0.0.0.0/0" "::/0" ];
|
||||||
|
persistentKeepalive = 25;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
networking.wireguard.interfaces = {
|
||||||
|
# The fe80:: link-locals are required: without one FRR's IPv6 BGP
|
||||||
|
# next-hop selection fails and the v6 session never leaves Connect.
|
||||||
|
wg-mic-der = mkLeg {
|
||||||
|
listenPort = 52038;
|
||||||
|
ips = [ "10.99.0.154/30" "fd53:4023:2326:99::9a/126" "fe80::2/64" ];
|
||||||
|
publicKey = "a4FyXuoI16TxZxv407lSzJV2YFJdq6FgRAtGu+oMzEY=";
|
||||||
|
endpoint = "31.77.105.157:52038";
|
||||||
|
};
|
||||||
|
wg-mic-bd8 = mkLeg {
|
||||||
|
listenPort = 52039;
|
||||||
|
ips = [ "10.99.0.158/30" "fd53:4023:2326:99::9e/126" "fe80::2/64" ];
|
||||||
|
publicKey = "QIvX+avvJqaHxuksGWb6Hx8fv7R6VI5wpaGqLme6Fgw=";
|
||||||
|
endpoint = "195.133.60.217:52039";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Heather's public firewall is disabled (the cloud SG is the WAN barrier).
|
||||||
|
# Explicitly restrict traffic FROM AstrNet before normal Docker/host rules:
|
||||||
|
# BGP from the two expected neighbors, ping to this mesh ID; no transit.
|
||||||
|
# Existing outbound sessions may receive established replies.
|
||||||
|
systemd.services.astrnet-guard = {
|
||||||
|
description = "Restrict AstrNet tunnel ingress";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
requiredBy = [ "wireguard-wg-mic-der.service" "wireguard-wg-mic-bd8.service" ];
|
||||||
|
before = [ "wireguard-wg-mic-der.service" "wireguard-wg-mic-bd8.service" ];
|
||||||
|
path = [ pkgs.nftables ];
|
||||||
|
serviceConfig = { Type = "oneshot"; RemainAfterExit = true; };
|
||||||
|
script = ''
|
||||||
|
nft list table inet astrnet_guard >/dev/null 2>&1 && nft delete table inet astrnet_guard
|
||||||
|
nft -f - <<'RULES'
|
||||||
|
table inet astrnet_guard {
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority -150; policy accept;
|
||||||
|
iifname { "wg-mic-der", "wg-mic-bd8" } ct state established,related accept
|
||||||
|
iifname "wg-mic-der" ip saddr 10.99.0.153 tcp dport 179 accept
|
||||||
|
iifname "wg-mic-bd8" ip saddr 10.99.0.157 tcp dport 179 accept
|
||||||
|
iifname "wg-mic-der" ip6 saddr fd53:4023:2326:99::99 tcp dport 179 accept
|
||||||
|
iifname "wg-mic-bd8" ip6 saddr fd53:4023:2326:99::9d tcp dport 179 accept
|
||||||
|
iifname { "wg-mic-der", "wg-mic-bd8" } ip daddr 10.0.0.9 icmp type echo-request accept
|
||||||
|
iifname { "wg-mic-der", "wg-mic-bd8" } drop
|
||||||
|
}
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority -150; policy accept;
|
||||||
|
iifname { "wg-mic-der", "wg-mic-bd8" } drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RULES
|
||||||
|
'';
|
||||||
|
preStop = "nft delete table inet astrnet_guard || true";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Mesh identity on a dummy interface. It must exist before FRR installs
|
||||||
|
# routes, because those routes are pinned to it as their source address.
|
||||||
|
systemd.services.astrnet-mesh0 = {
|
||||||
|
description = "AstrNet mesh identity (mesh0)";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "frr.service" ];
|
||||||
|
requiredBy = [ "frr.service" ];
|
||||||
|
path = [ pkgs.iproute2 ];
|
||||||
|
serviceConfig = { Type = "oneshot"; RemainAfterExit = true; };
|
||||||
|
script = ''
|
||||||
|
ip link show mesh0 >/dev/null 2>&1 || ip link add mesh0 type dummy
|
||||||
|
ip link set mesh0 up
|
||||||
|
ip addr replace ${id4}/32 dev mesh0
|
||||||
|
ip -6 addr replace ${id6}/128 dev mesh0
|
||||||
|
'';
|
||||||
|
preStop = "ip link del mesh0 || true";
|
||||||
|
};
|
||||||
|
|
||||||
|
# BGP only inside the tunnels, never on the public interface.
|
||||||
|
networking.firewall.interfaces."wg-mic-der".allowedTCPPorts = [ 179 ];
|
||||||
|
networking.firewall.interfaces."wg-mic-bd8".allowedTCPPorts = [ 179 ];
|
||||||
|
# Two legs means replies can arrive on the leg that is not the best route
|
||||||
|
# back. Strict reverse-path filtering drops those; loose keeps the spoof
|
||||||
|
# check (a route must exist) without caring which leg it points at.
|
||||||
|
networking.firewall.checkReversePath = lib.mkDefault "loose";
|
||||||
|
|
||||||
|
networking.networkmanager.unmanaged =
|
||||||
|
lib.mkIf config.networking.networkmanager.enable
|
||||||
|
[ "interface-name:wg-mic-*" "interface-name:mesh0" ];
|
||||||
|
|
||||||
|
services.frr = {
|
||||||
|
bgpd.enable = true;
|
||||||
|
config = ''
|
||||||
|
ip prefix-list OWN-V4 seq 5 permit ${id4}/32
|
||||||
|
ipv6 prefix-list OWN-V6 seq 5 permit ${id6}/128
|
||||||
|
!
|
||||||
|
! Accept mesh space only: never a default, never public prefixes. The
|
||||||
|
! mesh carries anycast /32s for public services (DNS resolvers etc.);
|
||||||
|
! accepting those would steer this host's own traffic into AstrNet.
|
||||||
|
! It also keeps the tunnel endpoints themselves out of the tunnels.
|
||||||
|
! Narrow to AstrNet address space; do not accept Yandex Cloud 10.130/24.
|
||||||
|
ip prefix-list MESH-V4 seq 5 permit 10.0.0.0/24 ge 24 le 32
|
||||||
|
ip prefix-list MESH-V4 seq 10 permit 10.0.1.0/24 ge 24 le 32
|
||||||
|
ip prefix-list MESH-V4 seq 15 permit 10.0.2.0/24 ge 24 le 32
|
||||||
|
ip prefix-list MESH-V4 seq 20 permit 10.99.0.0/22 ge 22 le 32
|
||||||
|
ipv6 prefix-list MESH-V6 seq 5 permit 2a05:f480:2400:1cf7::/64 le 128
|
||||||
|
ipv6 prefix-list MESH-V6 seq 10 permit fd53:4023:2326::/48 le 128
|
||||||
|
!
|
||||||
|
route-map MESH-IN-V4 permit 10
|
||||||
|
match ip address prefix-list MESH-V4
|
||||||
|
exit
|
||||||
|
route-map MESH-IN-V6 permit 10
|
||||||
|
match ipv6 address prefix-list MESH-V6
|
||||||
|
exit
|
||||||
|
route-map MESH-OUT-V4 permit 10
|
||||||
|
match ip address prefix-list OWN-V4
|
||||||
|
exit
|
||||||
|
route-map MESH-OUT-V6 permit 10
|
||||||
|
match ipv6 address prefix-list OWN-V6
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
! Source mesh traffic from the identity, not from whichever tunnel /30
|
||||||
|
! it leaves by; otherwise replies break whenever the path changes.
|
||||||
|
route-map SET-MESH-SRC permit 10
|
||||||
|
set src ${id4}
|
||||||
|
exit
|
||||||
|
route-map SET-MESH-SRC-V6 permit 10
|
||||||
|
set src ${id6}
|
||||||
|
exit
|
||||||
|
ip protocol bgp route-map SET-MESH-SRC
|
||||||
|
ipv6 protocol bgp route-map SET-MESH-SRC-V6
|
||||||
|
!
|
||||||
|
router bgp 4200000009
|
||||||
|
bgp router-id ${id4}
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
no bgp default ipv4-unicast
|
||||||
|
bgp bestpath as-path multipath-relax
|
||||||
|
neighbor 10.99.0.153 remote-as 4200000012
|
||||||
|
neighbor 10.99.0.153 description alderamin-v4-wg-mic-der
|
||||||
|
neighbor 10.99.0.153 update-source 10.99.0.154
|
||||||
|
neighbor 10.99.0.157 remote-as 4200000005
|
||||||
|
neighbor 10.99.0.157 description bd8816-v4-wg-mic-bd8
|
||||||
|
neighbor 10.99.0.157 update-source 10.99.0.158
|
||||||
|
neighbor fd53:4023:2326:99::99 remote-as 4200000012
|
||||||
|
neighbor fd53:4023:2326:99::99 description alderamin-v6-wg-mic-der
|
||||||
|
neighbor fd53:4023:2326:99::99 interface wg-mic-der
|
||||||
|
neighbor fd53:4023:2326:99::99 update-source fd53:4023:2326:99::9a
|
||||||
|
neighbor fd53:4023:2326:99::9d remote-as 4200000005
|
||||||
|
neighbor fd53:4023:2326:99::9d description bd8816-v6-wg-mic-bd8
|
||||||
|
neighbor fd53:4023:2326:99::9d interface wg-mic-bd8
|
||||||
|
neighbor fd53:4023:2326:99::9d update-source fd53:4023:2326:99::9e
|
||||||
|
!
|
||||||
|
address-family ipv4 unicast
|
||||||
|
network ${id4}/32
|
||||||
|
neighbor 10.99.0.153 activate
|
||||||
|
neighbor 10.99.0.153 route-map MESH-IN-V4 in
|
||||||
|
neighbor 10.99.0.153 route-map MESH-OUT-V4 out
|
||||||
|
neighbor 10.99.0.157 activate
|
||||||
|
neighbor 10.99.0.157 route-map MESH-IN-V4 in
|
||||||
|
neighbor 10.99.0.157 route-map MESH-OUT-V4 out
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
address-family ipv6 unicast
|
||||||
|
network ${id6}/128
|
||||||
|
neighbor fd53:4023:2326:99::99 activate
|
||||||
|
neighbor fd53:4023:2326:99::99 route-map MESH-IN-V6 in
|
||||||
|
neighbor fd53:4023:2326:99::99 route-map MESH-OUT-V6 out
|
||||||
|
neighbor fd53:4023:2326:99::9d activate
|
||||||
|
neighbor fd53:4023:2326:99::9d route-map MESH-IN-V6 in
|
||||||
|
neighbor fd53:4023:2326:99::9d route-map MESH-OUT-V6 out
|
||||||
|
exit-address-family
|
||||||
|
exit
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -23,8 +23,7 @@
|
||||||
./paseo.nix # paseo daemon (web UI для CLI-агентов, systemd + npm global)
|
./paseo.nix # paseo daemon (web UI для CLI-агентов, systemd + npm global)
|
||||||
./nick.nix # Nick timer prototype (systemd, localhost:8787)
|
./nick.nix # Nick timer prototype (systemd, localhost:8787)
|
||||||
./jupyter.nix # JupyterLab (systemd, localhost:8890, лимиты)
|
./jupyter.nix # JupyterLab (systemd, localhost:8890, лимиты)
|
||||||
./mesh-clients.nix # Sol-style client pool (wg-clients1)
|
./astrnet.nix # AstrNet (Sol): two WireGuard legs, FRR/BGP, ingress guard
|
||||||
./mesh-egress.nix # Sol-style selected-destination egress policy
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# Heather has 8 GiB RAM and runs several containers. Keep a 4 GiB
|
# Heather has 8 GiB RAM and runs several containers. Keep a 4 GiB
|
||||||
|
|
@ -36,10 +35,8 @@
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
# First server-to-server WireGuard backbone link: heather ↔ hyacinth.
|
# WireGuard tooling is also used by AstrNet. Keep this package and the
|
||||||
# The private key is generated on the live host and stays outside the flake.
|
# diagnostics tools available in non-login SSH sessions.
|
||||||
# Basic diagnostics/admin tools must be available even in non-login SSH
|
|
||||||
# sessions (and not depend on a stale Home Manager profile).
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
wireguard-tools
|
wireguard-tools
|
||||||
tmux
|
tmux
|
||||||
|
|
@ -48,49 +45,7 @@
|
||||||
python3
|
python3
|
||||||
];
|
];
|
||||||
|
|
||||||
# NixOS uses scripted networking here (not systemd-networkd), so create the
|
# Docker requires forwarding; AstrNet ingress is blocked by astrnet-guard.
|
||||||
# dummy identity interface through a small idempotent systemd unit.
|
|
||||||
systemd.services.mesh-identity = {
|
|
||||||
description = "Create mesh identity interface";
|
|
||||||
wantedBy = [ "network.target" ];
|
|
||||||
after = [ "wireguard-wg-mesh.service" "network-addresses-wg-mesh.service" ];
|
|
||||||
wants = [ "wireguard-wg-mesh.service" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
ExecStart = pkgs.writeShellScript "mesh-identity-up" ''
|
|
||||||
set -eu
|
|
||||||
${pkgs.iproute2}/bin/ip link show mesh0 >/dev/null 2>&1 || ${pkgs.iproute2}/bin/ip link add mesh0 type dummy
|
|
||||||
${pkgs.iproute2}/bin/ip addr replace 10.0.0.1/32 dev mesh0
|
|
||||||
${pkgs.iproute2}/bin/ip link set mesh0 up
|
|
||||||
'';
|
|
||||||
ExecStop = "${pkgs.iproute2}/bin/ip link del mesh0";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.wireguard.interfaces.wg-mesh = {
|
|
||||||
ips = [ "10.99.0.1/30" ];
|
|
||||||
listenPort = 51820;
|
|
||||||
privateKeyFile = "/var/lib/wireguard/wg-mesh.key";
|
|
||||||
# Keep WG as transport/crypto ACL; install the identity route explicitly below.
|
|
||||||
allowedIPsAsRoutes = false;
|
|
||||||
peers = [
|
|
||||||
{
|
|
||||||
publicKey = "BxTH8+MiA8Hv48UfpWJDVXIhbO5HgO2KTBEXIqqr23k=";
|
|
||||||
# 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;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.interfaces.wg-mesh.ipv4.routes = [
|
|
||||||
{ address = "10.0.0.2"; prefixLength = 32; }
|
|
||||||
];
|
|
||||||
|
|
||||||
# Needed later when this node becomes a transit/exit peer.
|
|
||||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||||
|
|
||||||
# Arion-проект как systemd-сервис: arion-compose.nix → docker-compose → up.
|
# Arion-проект как systemd-сервис: arion-compose.nix → docker-compose → up.
|
||||||
|
|
@ -122,10 +77,8 @@
|
||||||
22070 # strelaysrv (status)
|
22070 # strelaysrv (status)
|
||||||
3478 # coturn TURN (tcp+udp)
|
3478 # coturn TURN (tcp+udp)
|
||||||
8443 # xray VLESS+Reality (публичный прокси)
|
8443 # xray VLESS+Reality (публичный прокси)
|
||||||
51820 # WireGuard mesh backbone
|
|
||||||
];
|
];
|
||||||
firewall.allowedUDPPorts = [
|
firewall.allowedUDPPorts = [
|
||||||
51820 # WireGuard mesh backbone
|
|
||||||
22000 # syncthing sync (QUIC)
|
22000 # syncthing sync (QUIC)
|
||||||
10000 # jitsi jvb (RTP media)
|
10000 # jitsi jvb (RTP media)
|
||||||
3478 # coturn TURN
|
3478 # coturn TURN
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
# Sol-style client pool for personal mesh.
|
|
||||||
# Backbone peers use wg-mesh; phones/laptops use this separate interface.
|
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
|
||||||
environment.systemPackages = [ pkgs.wireguard-tools ];
|
|
||||||
|
|
||||||
networking.wireguard.interfaces.wg-clients1 = {
|
|
||||||
ips = [ "10.0.1.1/24" ];
|
|
||||||
listenPort = 51821;
|
|
||||||
privateKeyFile = "/var/lib/wireguard/wg-clients1.key";
|
|
||||||
allowedIPsAsRoutes = false;
|
|
||||||
peers = [
|
|
||||||
{
|
|
||||||
# lilac (Pixel 9 Pro), generated on poppy; private key is not in git.
|
|
||||||
publicKey = "RAvcRTdFBcvAGZWRm6YBMjRHRGj0iMNxsE0MBPGUPjM=";
|
|
||||||
allowedIPs = [ "10.0.1.10/32" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedUDPPorts = [ 51821 ];
|
|
||||||
|
|
||||||
# allowedIPsAsRoutes=false keeps peer declarations as crypto ACLs; install
|
|
||||||
# the client identity route explicitly, as in the backbone design.
|
|
||||||
networking.interfaces.wg-clients1.ipv4.routes = [
|
|
||||||
{ address = "10.0.1.10"; prefixLength = 32; }
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d /var/lib/wireguard 0700 root root -"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Generate the server key on first activation, outside the Nix store.
|
|
||||||
systemd.services.wireguard-key-wg-clients1 = {
|
|
||||||
description = "Generate wg-clients1 private key if absent";
|
|
||||||
wantedBy = [ "wireguard-wg-clients1.service" ];
|
|
||||||
before = [ "wireguard-wg-clients1.service" ];
|
|
||||||
serviceConfig.Type = "oneshot";
|
|
||||||
script = ''
|
|
||||||
install -d -m 700 /var/lib/wireguard
|
|
||||||
if [ ! -s /var/lib/wireguard/wg-clients1.key ]; then
|
|
||||||
umask 077
|
|
||||||
${pkgs.wireguard-tools}/bin/wg genkey > /var/lib/wireguard/wg-clients1.key
|
|
||||||
chmod 600 /var/lib/wireguard/wg-clients1.key
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
# 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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue