14d9bba4eb
Clients were handed both routers' real addresses as resolvers, so a downed router meant per-query resolver timeouts. Serve pdns-recursor on the VRRP VIPs (with non-local bind so the backup can pre-bind them) and advertise the VIP via kea and radvd, so DNS follows the master. untrusted advertises Cloudflare over v6 to match its v4 config. ipsec started before the WAN's public IP was up: stream's wan carries a static modem address that satisfies wait-online before the DHCP lease, so libreswan loaded its mesh conns (left=<public IP>) unoriented and never initiated. Gate stream's wan-online.target on the DHCP default route instead, and mkForce ipsec onto wan-online.target only (dropping the strongswan/libreswan multi-user.target pull-in) so the gate actually holds on both boxes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
227 lines
6.7 KiB
Nix
227 lines
6.7 KiB
Nix
{
|
|
imports = [ (import ./routing-common 1) ];
|
|
|
|
config.nixos.systems.stream = {
|
|
system = "x86_64-linux";
|
|
nixpkgs = "mine";
|
|
home-manager = "mine";
|
|
|
|
configuration = { lib, pkgs, config, assignments, ... }:
|
|
let
|
|
inherit (lib) mkMerge;
|
|
inherit (lib.my) networkdAssignment;
|
|
inherit (lib.my.c) networkd;
|
|
in
|
|
{
|
|
imports = [ ./routing-common/mstpd.nix ];
|
|
|
|
config = {
|
|
boot = {
|
|
kernelModules = [ "kvm-intel" ];
|
|
kernelParams = [ "intel_iommu=on" ];
|
|
initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci" ];
|
|
};
|
|
|
|
hardware = {
|
|
enableRedistributableFirmware = true;
|
|
cpu = {
|
|
intel.updateMicrocode = true;
|
|
};
|
|
};
|
|
|
|
fileSystems = {
|
|
"/boot" = {
|
|
device = "/dev/disk/by-partuuid/fe081885-9157-46b5-be70-46ac6fcb4069";
|
|
fsType = "vfat";
|
|
};
|
|
"/nix" = {
|
|
device = "/dev/disk/by-partuuid/a195e55e-397f-440d-a190-59ffa63cdb3f";
|
|
fsType = "ext4";
|
|
};
|
|
"/persist" = {
|
|
device = "/dev/disk/by-partuuid/ad71fafd-2d26-49c8-b0cb-794a28e0beb7";
|
|
fsType = "ext4";
|
|
neededForBoot = true;
|
|
};
|
|
};
|
|
|
|
services = {
|
|
mjpg-streamer = {
|
|
enable = false;
|
|
inputPlugin = "input_uvc.so";
|
|
outputPlugin = "output_http.so -w @www@ -n -p 5050";
|
|
};
|
|
octoprint = {
|
|
enable = false;
|
|
host = "::";
|
|
extraConfig = {
|
|
plugins = {
|
|
classicwebcam = {
|
|
snapshot = "/webcam/?action=snapshot";
|
|
stream = "/webcam/?action=stream";
|
|
streamRatio = "4:3";
|
|
};
|
|
};
|
|
serial = {
|
|
port = "/dev/ttyACM0";
|
|
baudrate = 115200;
|
|
};
|
|
temperature.profiles = [
|
|
{
|
|
bed = 60;
|
|
extruder = 215;
|
|
name = "PLA";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
# wan carries a permanent static modem-management address (assignments.modem)
|
|
# alongside the DHCP public IP, so wait-online@wan reports "online" as soon as
|
|
# the static address is up - before the DHCP lease arrives. ipsec's left= is the
|
|
# public IP, so gating on wait-online lets it start unoriented and never connect.
|
|
# Gate instead on the DHCP default route, which only exists once the public lease
|
|
# is up (the static modem address has no gateway).
|
|
systemd.services.wan-wait-online = {
|
|
description = "Wait for the wan default route (public DHCP lease)";
|
|
after = [ "systemd-networkd.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
TimeoutStartSec = "300";
|
|
};
|
|
script = ''
|
|
until [ -n "$(${pkgs.iproute2}/bin/ip -4 route show default dev wan)" ]; do
|
|
sleep 1
|
|
done
|
|
'';
|
|
};
|
|
systemd.targets.wan-online = {
|
|
requires = [ "wan-wait-online.service" ];
|
|
after = [ "wan-wait-online.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
};
|
|
|
|
systemd.network = {
|
|
netdevs = {
|
|
"25-lan" = {
|
|
netdevConfig = {
|
|
Name = "lan";
|
|
Kind = "bridge";
|
|
};
|
|
extraConfig = ''
|
|
[Bridge]
|
|
STP=true
|
|
'';
|
|
};
|
|
};
|
|
links = {
|
|
"10-wan" = {
|
|
matchConfig = {
|
|
# Matching against MAC address seems to break VLAN interfaces
|
|
# (since they share the same MAC address)
|
|
Driver = "igc";
|
|
PermanentMACAddress = "00:f0:cb:ee:ca:dd";
|
|
};
|
|
linkConfig = {
|
|
Name = "wan";
|
|
RxBufferSize = 4096;
|
|
TxBufferSize = 4096;
|
|
};
|
|
};
|
|
"10-lan-jim" = {
|
|
matchConfig = {
|
|
Driver = "igc";
|
|
PermanentMACAddress = "00:f0:cb:ee:ca:de";
|
|
};
|
|
linkConfig = {
|
|
Name = "lan-jim";
|
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
|
};
|
|
};
|
|
"10-et2" = {
|
|
matchConfig = {
|
|
Driver = "igc";
|
|
PermanentMACAddress = "00:f0:cb:ee:ca:df";
|
|
};
|
|
linkConfig.Name = "et2";
|
|
};
|
|
|
|
"10-lan-dave" = {
|
|
matchConfig = {
|
|
Driver = "mlx4_en";
|
|
PermanentMACAddress = "00:02:c9:d5:b1:d6";
|
|
};
|
|
linkConfig = {
|
|
Name = "lan-dave";
|
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
|
};
|
|
};
|
|
"10-et5" = {
|
|
matchConfig = {
|
|
Driver = "mlx4_en";
|
|
PermanentMACAddress = "00:02:c9:d5:b1:d7";
|
|
};
|
|
linkConfig.Name = "et5";
|
|
};
|
|
};
|
|
networks = {
|
|
"50-lan-jim" = {
|
|
matchConfig.Name = "lan-jim";
|
|
networkConfig.Bridge = "lan";
|
|
};
|
|
"50-lan-dave" = {
|
|
matchConfig.Name = "lan-dave";
|
|
networkConfig.Bridge = "lan";
|
|
};
|
|
|
|
"50-wan-ifb" = {
|
|
matchConfig.Name = "wan-ifb";
|
|
networkConfig = networkd.noL3;
|
|
extraConfig = ''
|
|
[CAKE]
|
|
Bandwidth=490M
|
|
RTTSec=50ms
|
|
PriorityQueueingPreset=besteffort
|
|
# DOCSIS preset
|
|
OverheadBytes=18
|
|
MPUBytes=64
|
|
CompensationMode=none
|
|
'';
|
|
};
|
|
"50-wan" = mkMerge [
|
|
(networkdAssignment "wan" assignments.modem)
|
|
{
|
|
matchConfig.Name = "wan";
|
|
DHCP = "ipv4";
|
|
dns = [ "127.0.0.1" "::1" ];
|
|
dhcpV4Config.UseDNS = false;
|
|
|
|
qdiscConfig = {
|
|
Parent = "ingress";
|
|
Handle = "0xffff";
|
|
};
|
|
extraConfig = ''
|
|
[CAKE]
|
|
Parent=root
|
|
Bandwidth=48M
|
|
RTTSec=50ms
|
|
'';
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
my = {
|
|
secrets = {
|
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
|
};
|
|
server.enable = true;
|
|
# deploy.node.hostname = "192.168.68.2";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|