diff --git a/ops/nixos/swann/default.nix b/ops/nixos/swann/default.nix index 7209c2f2c0..8d59a7a52a 100644 --- a/ops/nixos/swann/default.nix +++ b/ops/nixos/swann/default.nix @@ -5,815 +5,822 @@ { depot, lib, pkgs, config, ... }: let inherit (depot.ops) secrets; + inherit (lib) mkMerge mkForce; in { imports = [ # We include this just so it sets some sysctls and firewall settings. ../lib/bgp.nix ]; - boot.initrd.availableKernelModules = [ - "sd_mod" - "ahci" - "usb_storage" - "usbhid" - ]; - boot.kernelParams = [ "mitigations=off" ]; + config = mkMerge [ { + boot.initrd.availableKernelModules = [ + "sd_mod" + "ahci" + "usb_storage" + "usbhid" + ]; + boot.kernelParams = [ "mitigations=off" ]; - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/fc964ef6-e3d0-4472-bc0e-f96f977ebf11"; - fsType = "ext4"; - }; - "/boot" = { - device = "/dev/disk/by-uuid/AB36-5BE4"; - fsType = "vfat"; - }; - }; - - nix.settings.max-jobs = lib.mkDefault 4; - - # Use the systemd-boot EFI boot loader. - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - # Networking! - networking = { - # Routing tables: - # bgp (150) -- contains default routes over WG tunnels - # wg-vm (151) -- contains default routes over WG tunnels - # wg-ee (152) -- contains default routes over WG tunnels - # wg-gnet (153) -- contains default routes over WG tunnels - # ee (201) -- table contains a default route via EE - # vm (202) -- table contains a default route via VM - # gnet (203) -- table contains a default route via gnetwork - # main (254) -- basically empty - - hostName = "swann"; # Define your hostname. - domain = "int.as205479.net"; - nameservers = ["8.8.8.8" "8.8.4.4"]; - useNetworkd = true; - interfaces = { - lo = { - ipv4.addresses = [ - { address = "127.0.0.1"; prefixLength = 8; } - { address = "92.118.30.254"; prefixLength = 32; } - { address = "92.118.30.253"; prefixLength = 32; } - ]; + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/fc964ef6-e3d0-4472-bc0e-f96f977ebf11"; + fsType = "ext4"; }; - en-virginmedia = { - useDHCP = true; - macAddress = "e4:3a:6e:16:07:61"; - }; - en-gnet = { - useDHCP = true; - # Additional options configured in networkd. - }; - en-ee = { - useDHCP = true; - # Additional options configured in networkd. - }; - en-general = { - ipv4.addresses = [ - { address = "192.168.1.1"; prefixLength = 23; } - { address = "92.118.30.17"; prefixLength = 28; } - ]; - ipv6.addresses = [ - { address = "2a09:a443::1"; prefixLength = 64; } - { address = "2a09:a443:1::1"; prefixLength = 48; } - ]; - }; - vl-eduroam = { - ipv4.addresses = [ - { address = "192.168.10.1"; prefixLength = 24; } - ]; - ipv6.addresses = [ - { address = "2a09:a443:2::1"; prefixLength = 64; } - { address = "2a09:a443:3::1"; prefixLength = 48; } - ]; - }; - }; - vlans = { - vl-eduroam = { - id = 100; - interface = "en-general"; + "/boot" = { + device = "/dev/disk/by-uuid/AB36-5BE4"; + fsType = "vfat"; }; }; - localCommands = '' - ip -4 route flush table 151 >/dev/null 2>&1 || true - ip -4 route add 92.118.30.0/31 dev wg-tuvok-vm table 151 - ip -4 route add default via 92.118.30.1 dev wg-tuvok-vm table 151 - ip -6 route flush table 151 >/dev/null 2>&1 || true - ip -6 route add 2a09:a442::1:0/112 dev wg-tuvok-vm table 151 - ip -6 route add default via 2a09:a442::1:2 dev wg-tuvok-vm table 151 + nix.settings.max-jobs = lib.mkDefault 4; - ip -4 route flush table 152 >/dev/null 2>&1 || true - ip -4 route add 92.118.30.2/31 dev wg-tuvok-ee table 152 - ip -4 route add default via 92.118.30.3 dev wg-tuvok-ee table 152 - ip -6 route flush table 152 >/dev/null 2>&1 || true - ip -6 route add 2a09:a442::2:0/112 dev wg-tuvok-ee table 152 - ip -6 route add default via 2a09:a442::2:2 dev wg-tuvok-ee table 152 + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; - ip -4 route flush table 153 >/dev/null 2>&1 || true - ip -4 route add 92.118.30.4/31 dev wg-tuvok-gnet table 153 - ip -4 route add default via 92.118.30.5 dev wg-tuvok-gnet table 153 - ip -6 route flush table 153 >/dev/null 2>&1 || true - ip -6 route add 2a09:a442::3:0/112 dev wg-tuvok-gnet table 153 - ip -6 route add default via 2a09:a442::3:2 dev wg-tuvok-gnet table 153 - ''; - }; - systemd.network = let - hexToInt = h: (builtins.fromTOML "h = ${h}").h; - physicalNetwork = rtID: wireguardFwmark: extraRules: { - dhcpV4Config.RouteTable = rtID; - routingPolicyRules = [{ + # Networking! + networking = { + # Routing tables: + # bgp (150) -- contains default routes over WG tunnels + # wg-vm (151) -- contains default routes over WG tunnels + # wg-ee (152) -- contains default routes over WG tunnels + # wg-gnet (153) -- contains default routes over WG tunnels + # ee (201) -- table contains a default route via EE + # vm (202) -- table contains a default route via VM + # gnet (203) -- table contains a default route via gnetwork + # main (254) -- basically empty + + hostName = "swann"; # Define your hostname. + domain = "int.as205479.net"; + nameservers = ["8.8.8.8" "8.8.4.4"]; + useNetworkd = true; + interfaces = { + lo = { + ipv4.addresses = [ + { address = "127.0.0.1"; prefixLength = 8; } + { address = "92.118.30.254"; prefixLength = 32; } + { address = "92.118.30.253"; prefixLength = 32; } + ]; + }; + en-virginmedia = { + useDHCP = true; + macAddress = "e4:3a:6e:16:07:61"; + }; + en-gnet = { + useDHCP = true; + ipv4.addresses = [ + { address = "192.168.201.2"; prefixLength = 24; } + ]; + # Additional options configured in networkd. + }; + en-ee = { + useDHCP = true; + ipv4.addresses = [ + { address = "192.168.200.2"; prefixLength = 24; } + ]; + # Additional options configured in networkd. + }; + en-general = { + ipv4.addresses = [ + { address = "192.168.1.1"; prefixLength = 23; } + { address = "92.118.30.17"; prefixLength = 28; } + ]; + ipv6.addresses = [ + { address = "2a09:a443::1"; prefixLength = 64; } + { address = "2a09:a443:1::1"; prefixLength = 48; } + ]; + }; + vl-eduroam = { + ipv4.addresses = [ + { address = "192.168.10.1"; prefixLength = 24; } + ]; + ipv6.addresses = [ + { address = "2a09:a443:2::1"; prefixLength = 64; } + { address = "2a09:a443:3::1"; prefixLength = 48; } + ]; + }; + }; + vlans = { + vl-eduroam = { + id = 100; + interface = "en-general"; + }; + }; + }; + systemd.network = let + hexToInt = h: (builtins.fromTOML "h = ${h}").h; + physicalNetwork = rtID: wireguardFwmark: extraRules: { + dhcpV4Config.RouteTable = rtID; + routingPolicyRules = [{ + routingPolicyRuleConfig = { + Family = "both"; + FirewallMark = hexToInt wireguardFwmark; + Priority = 10000; + Table = rtID; + }; + }] ++ extraRules; + }; + wireguardNetwork = { linkName, relativePriority, rtID, v4Linknet, v6Linknet }: { + matchConfig.Name = linkName; + + routes = let + replaceV4Octet = v4: fn: let + pieces = builtins.match ''(([0-9]+\.){3})([0-9]+)'' v4; + in + "${builtins.elemAt pieces 0}${toString (fn (lib.toInt (builtins.elemAt pieces 2)))}"; + replaceV6Octet = v6: fn: let + pieces = builtins.match ''^(([0-9a-f]+:+)+)([0-9a-f]*)$'' v6; + in + "${builtins.elemAt pieces 0}${lib.toHexString (fn (hexToInt "0x${builtins.elemAt pieces 2}"))}"; + in [ + { + routeConfig = { + Destination = "${v4Linknet}/31"; + Table = rtID; + }; + } + { + routeConfig = { + Gateway = replaceV4Octet v4Linknet (n: n + 1); + Table = rtID; + }; + } + + { + routeConfig = { + Destination = "${replaceV6Octet v6Linknet (n: n - 1)}/112"; + Table = rtID; + }; + } + { + routeConfig = { + Gateway = replaceV6Octet v6Linknet (n: n + 1); + Table = rtID; + }; + } + ]; + + networkConfig = { + Address = [ + "${v4Linknet}/31" + "${v6Linknet}/112" + ]; + }; + + routingPolicyRules = [ + (tailscaleRule (relativePriority + 5000) rtID) + + # Allow picking destination by source IP. + { + routingPolicyRuleConfig = { + Family = "ipv4"; + From = v4Linknet; + Priority = 10010; + Table = rtID; + }; + } + { + routingPolicyRuleConfig = { + Family = "ipv6"; + From = v6Linknet; + Priority = 10010; + Table = rtID; + }; + } + + { + # Catch-all mop-up rule at the end. + routingPolicyRuleConfig = { + Family = "both"; + Priority = relativePriority + 10090; + Table = rtID; + }; + } + ]; + }; + tailscaleRule = priority: table: { + # Route Tailscale (fwmark 0x80000) via Wireguard first. routingPolicyRuleConfig = { Family = "both"; - FirewallMark = hexToInt wireguardFwmark; - Priority = 10000; - Table = rtID; + FirewallMark = hexToInt "0x80000"; + Priority = priority; + Table = table; }; - }] ++ extraRules; - }; - wireguardNetwork = { linkName, relativePriority, rtID, v4Linknet, v6Linknet }: { - matchConfig.Name = linkName; + }; + in let + routeTables = { + bgp = 150; + wg-vm = 151; + wg-ee = 152; + wg-gnet = 153; + ee = 201; + vm = 202; + gnet = 203; + }; + in { + enable = true; + config.routeTables = routeTables; + networks."50-wg-tuvok-vm" = wireguardNetwork { + linkName = "wg-tuvok-vm"; + relativePriority = 2; + rtID = routeTables.wg-vm; + v4Linknet = "92.118.30.0"; + v6Linknet = "2a09:a442::1:1"; + }; + networks."50-wg-tuvok-ee" = wireguardNetwork { + linkName = "wg-tuvok-ee"; + relativePriority = 3; + rtID = routeTables.wg-ee; + v4Linknet = "92.118.30.2"; + v6Linknet = "2a09:a442::2:1"; + }; + networks."50-wg-tuvok-gnet" = wireguardNetwork { + linkName = "wg-tuvok-gnet"; + relativePriority = 1; + rtID = routeTables.wg-gnet; + v4Linknet = "92.118.30.4"; + v6Linknet = "2a09:a442::3:1"; + }; + networks."40-lo" = { + routingPolicyRules = let + viaMain = priority: to: { + routingPolicyRuleConfig = { + To = to; + Table = "main"; + Priority = priority; + }; + }; + blackhole = fwmark: { + routingPolicyRuleConfig = { + Family = "both"; + FirewallMark = hexToInt fwmark; + Priority = 10001; + Type = "unreachable"; + }; + }; + in [ + (tailscaleRule 5000 150) - routes = let - replaceV4Octet = v4: fn: let - pieces = builtins.match ''(([0-9]+\.){3})([0-9]+)'' v4; - in - "${builtins.elemAt pieces 0}${toString (fn (lib.toInt (builtins.elemAt pieces 2)))}"; - replaceV6Octet = v6: fn: let - pieces = builtins.match ''^(([0-9a-f]+:+)+)([0-9a-f]*)$'' v6; - in - "${builtins.elemAt pieces 0}${lib.toHexString (fn (hexToInt "0x${builtins.elemAt pieces 2}"))}"; - in [ - { - routeConfig = { - Destination = "${v4Linknet}/31"; - Table = rtID; - }; - } - { - routeConfig = { - Gateway = replaceV4Octet v4Linknet (n: n + 1); - Table = rtID; - }; - } + # Blackhole connections that should be routed over individual interfaces. + (blackhole "0xdead") + (blackhole "0xbeef") + (blackhole "0xcafe") - { - routeConfig = { - Destination = "${replaceV6Octet v6Linknet (n: n - 1)}/112"; - Table = rtID; - }; - } - { - routeConfig = { - Gateway = replaceV6Octet v6Linknet (n: n + 1); - Table = rtID; - }; - } - ]; + # RFC 1918 via main table. + (viaMain 10020 "192.168.0.0/16") + (viaMain 10021 "10.0.0.0/8") + (viaMain 10022 "172.16.0.0/12") + # and the linknets. + (viaMain 10023 "92.118.30.0/24") + (viaMain 10024 "2a09:a442::1:0/112") + (viaMain 10025 "2a09:a442::2:0/112") + (viaMain 10026 "2a09:a442::3:0/112") - networkConfig = { - Address = [ - "${v4Linknet}/31" - "${v6Linknet}/112" + { + # Catch-all "go via WG" + routingPolicyRuleConfig = { + Family = "both"; + Priority = 10080; + Table = routeTables.bgp; + }; + } ]; }; - - routingPolicyRules = [ - (tailscaleRule (relativePriority + 5000) rtID) - - # Allow picking destination by source IP. - { - routingPolicyRuleConfig = { - Family = "ipv4"; - From = v4Linknet; - Priority = 10010; - Table = rtID; - }; - } - { - routingPolicyRuleConfig = { - Family = "ipv6"; - From = v6Linknet; - Priority = 10010; - Table = rtID; - }; - } - - { - # Catch-all mop-up rule at the end. - routingPolicyRuleConfig = { - Family = "both"; - Priority = relativePriority + 10090; - Table = rtID; - }; - } - ]; - }; - tailscaleRule = priority: table: { - # Route Tailscale (fwmark 0x80000) via Wireguard first. - routingPolicyRuleConfig = { - Family = "both"; - FirewallMark = hexToInt "0x80000"; - Priority = priority; - Table = table; - }; - }; - in { - enable = true; - networks."50-wg-tuvok-vm" = wireguardNetwork { - linkName = "wg-tuvok-vm"; - relativePriority = 2; - rtID = 151; - v4Linknet = "92.118.30.0"; - v6Linknet = "2a09:a442::1:1"; - }; - networks."50-wg-tuvok-ee" = wireguardNetwork { - linkName = "wg-tuvok-ee"; - relativePriority = 3; - rtID = 152; - v4Linknet = "92.118.30.2"; - v6Linknet = "2a09:a442::2:1"; - }; - networks."50-wg-tuvok-gnet" = wireguardNetwork { - linkName = "wg-tuvok-gnet"; - relativePriority = 1; - rtID = 153; - v4Linknet = "92.118.30.4"; - v6Linknet = "2a09:a442::3:1"; - }; - networks."40-lo" = { - routingPolicyRules = let - viaMain = priority: to: { - routingPolicyRuleConfig = { - To = to; - Table = "main"; - Priority = priority; - }; + networks."40-en-ee" = (physicalNetwork routeTables.ee "0xdead" [{ + routingPolicyRuleConfig = { + # add-on.ee.co.uk goes via EE. + To = "82.192.97.153/32"; + Table = routeTables.ee; + Priority = 10031; }; - blackhole = fwmark: { - routingPolicyRuleConfig = { - Family = "both"; + } { + routingPolicyRuleConfig = { + # as does anything from 192.168.200.0/24. + From = "192.168.200.0/24"; + Table = routeTables.ee; + Priority = 10031; + }; + }]) // { + linkConfig.RequiredForOnline = "no"; + }; + networks."40-en-virginmedia" = (physicalNetwork routeTables.vm "0xbeef" []) // { + linkConfig.RequiredForOnline = "no"; + }; + networks."40-en-gnet" = (physicalNetwork routeTables.gnet "0xcafe" []); + + netdevs = let + wireguard = { name, listenPort, privateKey, endpoint, publicKey, fwmark }: { + netdevConfig = { + Name = name; + Kind = "wireguard"; + Description = "WireGuard tunnel ${name}"; + }; + wireguardConfig = { + ListenPort = listenPort; + PrivateKeyFile = pkgs.writeText "${name}" privateKey; + # TODO: PrivateKeyFile FirewallMark = hexToInt fwmark; - Priority = 10001; - Type = "unreachable"; + RouteTable = "off"; }; + wireguardPeers = [{ + wireguardPeerConfig = { + Endpoint = endpoint; + PublicKey = publicKey; + AllowedIPs = [ + "0.0.0.0/0" + "::/0" + ]; + }; + }]; }; - in [ - (tailscaleRule 5000 150) - - # Blackhole connections that should be routed over individual interfaces. - (blackhole "0xdead") - (blackhole "0xbeef") - (blackhole "0xcafe") - - # RFC 1918 via main table. - (viaMain 10020 "192.168.0.0/16") - (viaMain 10021 "10.0.0.0/8") - (viaMain 10022 "172.16.0.0/12") - # and the linknets. - (viaMain 10023 "92.118.30.0/24") - (viaMain 10024 "2a09:a442::1:0/112") - (viaMain 10025 "2a09:a442::2:0/112") - (viaMain 10026 "2a09:a442::3:0/112") - - { - # Catch-all "go via WG" - routingPolicyRuleConfig = { - Family = "both"; - Priority = 10080; - Table = 150; - }; - } - ]; - }; - networks."40-en-ee" = (physicalNetwork 201 "0xdead" [{ - routingPolicyRuleConfig = { - # add-on.ee.co.uk goes via EE. - To = "82.192.97.153/32"; - Table = 201; - Priority = 10031; - }; - } { - routingPolicyRuleConfig = { - # as does anything from 192.168.200.0/24. - From = "192.168.200.0/24"; - Table = 201; - Priority = 10031; - }; - }]) // { - linkConfig.RequiredForOnline = "no"; - }; - networks."40-en-virginmedia" = (physicalNetwork 202 "0xbeef" []) // { - linkConfig.RequiredForOnline = "no"; - }; - networks."40-en-gnet" = (physicalNetwork 203 "0xcafe" []); - - netdevs = let - wireguard = { name, listenPort, privateKey, endpoint, publicKey, fwmark }: { - netdevConfig = { - Name = name; - Kind = "wireguard"; - Description = "WireGuard tunnel ${name}"; + tuvokWireguard = args: wireguard (args // { + privateKey = secrets.wireguard.tuvok-swann.swann.privateKey; + publicKey = secrets.wireguard.tuvok-swann.tuvok.publicKey; + }); + in { + "40-wg-tuvok-vm" = tuvokWireguard { + name = "wg-tuvok-vm"; + listenPort = 51820; + endpoint = "92.118.28.252:51820"; + fwmark = "0xbeef"; }; - wireguardConfig = { - ListenPort = listenPort; - PrivateKeyFile = pkgs.writeText "${name}" privateKey; - # TODO: PrivateKeyFile - FirewallMark = hexToInt fwmark; - RouteTable = "off"; + "40-wg-tuvok-ee" = tuvokWireguard { + name = "wg-tuvok-ee"; + listenPort = 51821; + endpoint = "[2a09:a441::f00f]:51821"; + fwmark = "0xdead"; + }; + "40-wg-tuvok-gnet" = tuvokWireguard { + name = "wg-tuvok-gnet"; + listenPort = 51822; + endpoint = "92.118.28.252:51822"; + fwmark = "0xcafe"; }; - wireguardPeers = [{ - wireguardPeerConfig = { - Endpoint = endpoint; - PublicKey = publicKey; - AllowedIPs = [ - "0.0.0.0/0" - "::/0" - ]; - }; - }]; - }; - tuvokWireguard = args: wireguard (args // { - privateKey = secrets.wireguard.tuvok-swann.swann.privateKey; - publicKey = secrets.wireguard.tuvok-swann.tuvok.publicKey; - }); - in { - "40-wg-tuvok-vm" = tuvokWireguard { - name = "wg-tuvok-vm"; - listenPort = 51820; - endpoint = "92.118.28.252:51820"; - fwmark = "0xbeef"; - }; - "40-wg-tuvok-ee" = tuvokWireguard { - name = "wg-tuvok-ee"; - listenPort = 51821; - endpoint = "[2a09:a441::f00f]:51821"; - fwmark = "0xdead"; - }; - "40-wg-tuvok-gnet" = tuvokWireguard { - name = "wg-tuvok-gnet"; - listenPort = 51822; - endpoint = "92.118.28.252:51822"; - fwmark = "0xcafe"; }; }; - }; - my.ip.tailscale = "100.102.224.95"; - services.udev.extraRules = '' - ATTR{address}=="e4:3a:6e:16:07:62", DRIVERS=="?*", NAME="en-virginmedia" - ATTR{address}=="e4:3a:6e:16:07:63", DRIVERS=="?*", NAME="en-ee" - ATTR{address}=="e4:3a:6e:16:07:64", DRIVERS=="?*", NAME="en-gnet" - ATTR{address}=="e4:3a:6e:16:07:67", DRIVERS=="?*", NAME="en-general" - ''; - boot.kernel.sysctl = { - "net.ipv4.ip_forward" = "1"; - "net.ipv6.conf.default.forwarding" = "1"; - "net.ipv6.conf.all.forwarding" = "1"; - "net.ipv6.conf.en-virginmedia.accept_ra" = "2"; - "net.ipv6.conf.en-ee.accept_ra" = "2"; - "net.ipv6.conf.en-gnet.accept_ra" = "2"; - }; - networking.nat = { - enable = true; - internalInterfaces = ["en-general"]; - externalInterface = "en-virginmedia"; - extraCommands = '' - # Send PS5 RTMP to totoro instead. - # See DHCP static lease. - iptables -w -t nat -A nixos-nat-pre --src 92.118.30.18 -p tcp --dport 1935 -j DNAT --to-destination 192.168.1.40 - - # NAT packets going over EE plain. - iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o en-ee -j MASQUERADE - - # NAT packets going over GNetwork plain. - iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o en-gnet -j MASQUERADE - - # SNAT packets we're sending over tunnels. - iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-vm -j SNAT --to-source 92.118.30.254 - iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-ee -j SNAT --to-source 92.118.30.254 - iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-gnet -j SNAT --to-source 92.118.30.254 - - # eduroam - # > mark incoming eduroam packets - iptables -w -t nat -A nixos-nat-pre -i vl-eduroam -j MARK --set-mark 2 - # > NAT packets going out directly. - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-virginmedia -j MASQUERADE - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-ee -j MASQUERADE - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-gnet -j MASQUERADE - # > NAT packets going over tunnels. - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-vm -j SNAT --to-source 92.118.30.253 - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-ee -j SNAT --to-source 92.118.30.253 - iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-gnet -j SNAT --to-source 92.118.30.253 + my.ip.tailscale = "100.102.224.95"; + services.udev.extraRules = '' + ATTR{address}=="e4:3a:6e:16:07:62", DRIVERS=="?*", NAME="en-virginmedia" + ATTR{address}=="e4:3a:6e:16:07:63", DRIVERS=="?*", NAME="en-ee" + ATTR{address}=="e4:3a:6e:16:07:64", DRIVERS=="?*", NAME="en-gnet" + ATTR{address}=="e4:3a:6e:16:07:67", DRIVERS=="?*", NAME="en-general" ''; - }; - services.dhcpd4 = { - enable = true; - interfaces = ["en-general" "vl-eduroam"]; - authoritative = true; - extraConfig = '' - shared-network int { - default-lease-time 600; - max-lease-time 3600; - option interface-mtu 1420; # Wireguard + boot.kernel.sysctl = { + "net.ipv4.ip_forward" = "1"; + "net.ipv6.conf.default.forwarding" = "1"; + "net.ipv6.conf.all.forwarding" = "1"; + "net.ipv6.conf.en-virginmedia.accept_ra" = "2"; + "net.ipv6.conf.en-ee.accept_ra" = "2"; + "net.ipv6.conf.en-gnet.accept_ra" = "2"; + }; + networking.nat = { + enable = true; + internalInterfaces = ["en-general"]; + externalInterface = "en-virginmedia"; + extraCommands = '' + # Send PS5 RTMP to totoro instead. + # See DHCP static lease. + iptables -w -t nat -A nixos-nat-pre --src 92.118.30.18 -p tcp --dport 1935 -j DNAT --to-destination 192.168.1.40 - subnet 192.168.1.0 netmask 255.255.255.0 { + # NAT packets going over EE plain. + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o en-ee -j MASQUERADE + + # NAT packets going over GNetwork plain. + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o en-gnet -j MASQUERADE + + # SNAT packets we're sending over tunnels. + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-vm -j SNAT --to-source 92.118.30.254 + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-ee -j SNAT --to-source 92.118.30.254 + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 -o wg-tuvok-gnet -j SNAT --to-source 92.118.30.254 + + # eduroam + # > mark incoming eduroam packets + iptables -w -t nat -A nixos-nat-pre -i vl-eduroam -j MARK --set-mark 2 + # > NAT packets going out directly. + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-virginmedia -j MASQUERADE + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-ee -j MASQUERADE + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o en-gnet -j MASQUERADE + # > NAT packets going over tunnels. + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-vm -j SNAT --to-source 92.118.30.253 + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-ee -j SNAT --to-source 92.118.30.253 + iptables -w -t nat -A nixos-nat-post -m mark --mark 2 -o wg-tuvok-gnet -j SNAT --to-source 92.118.30.253 + ''; + }; + services.dhcpd4 = { + enable = true; + interfaces = ["en-general" "vl-eduroam"]; + authoritative = true; + extraConfig = '' + shared-network int { + default-lease-time 3600; + max-lease-time 86400; + option interface-mtu 1420; # Wireguard + + subnet 192.168.1.0 netmask 255.255.255.0 { + option subnet-mask 255.255.255.0; + option routers 192.168.1.1; + option domain-name-servers 192.168.1.1; + option domain-name "house.as205479.net"; + + range 192.168.1.100 192.168.1.200; + } + + subnet 92.118.30.16 netmask 255.255.255.240 { + option subnet-mask 255.255.255.240; + option routers 92.118.30.17; + option domain-name-servers 92.118.30.17; + option domain-name "house-ext.as205479.net"; + } + } + + subnet 192.168.10.0 netmask 255.255.255.0 { option subnet-mask 255.255.255.0; - option routers 192.168.1.1; - option domain-name-servers 192.168.1.1; - option domain-name "house.as205479.net"; + option routers 192.168.10.1; + option domain-name-servers 192.168.10.1; + option domain-name "eduroam.as205479.net"; + default-lease-time 600; + max-lease-time 3600; + option interface-mtu 1420; # Wireguard - range 192.168.1.100 192.168.1.200; + range 192.168.10.100 192.168.10.200; } - - subnet 92.118.30.16 netmask 255.255.255.240 { - option subnet-mask 255.255.255.240; - option routers 92.118.30.17; - option domain-name-servers 92.118.30.17; - option domain-name "house-ext.as205479.net"; + ''; + machines = [ + { + hostName = "totoro"; + ethernetAddress = "40:8d:5c:1f:e8:68"; + ipAddress = "192.168.1.40"; } - } + { + hostName = "totoro-pfsense"; + ethernetAddress = "52:54:00:cf:cd:94"; + ipAddress = "192.168.1.41"; + } + { + hostName = "kvm"; + ethernetAddress = "00:0d:5d:1b:14:ba"; + ipAddress = "192.168.1.50"; + } + { + hostName = "printer-xerox"; + ethernetAddress = "9c:93:4e:ad:1f:7b"; + ipAddress = "192.168.1.51"; + } + { + hostName = "ps5"; + ethernetAddress = "bc:33:29:26:01:5c"; + # This is used for DNAT on RTMP, above. + ipAddress = "92.118.30.18"; + } + ]; + }; + systemd.services.dhcpd4 = { + wants = [ "systemd-networkd-wait-online.service" ]; + after = [ "systemd-networkd-wait-online.service" ]; + }; - subnet 192.168.10.0 netmask 255.255.255.0 { - option subnet-mask 255.255.255.0; - option routers 192.168.10.1; - option domain-name-servers 192.168.10.1; - option domain-name "eduroam.as205479.net"; - default-lease-time 600; - max-lease-time 3600; - option interface-mtu 1420; # Wireguard + services.unifi = { + enable = true; + openFirewall = false; + unifiPackage = depot.pkgs.unifi; + }; + services.prometheus.exporters.unifi-poller = { + enable = true; + controllers = [{ + url = "https://localhost:8443"; + verify_ssl = false; + user = "unifipoller"; + pass = pkgs.writeTextFile { name = "unifipoller-password"; text = "unifipoller"; }; + }]; + }; - range 192.168.10.100 192.168.10.200; - } - ''; - machines = [ - { - hostName = "totoro"; - ethernetAddress = "40:8d:5c:1f:e8:68"; - ipAddress = "192.168.1.40"; - } - { - hostName = "totoro-pfsense"; - ethernetAddress = "52:54:00:cf:cd:94"; - ipAddress = "192.168.1.41"; - } - { - hostName = "kvm"; - ethernetAddress = "00:0d:5d:1b:14:ba"; - ipAddress = "192.168.1.50"; - } - { - hostName = "printer-xerox"; - ethernetAddress = "9c:93:4e:ad:1f:7b"; - ipAddress = "192.168.1.51"; - } - { - hostName = "ps5"; - ethernetAddress = "bc:33:29:26:01:5c"; - # This is used for DNAT on RTMP, above. - ipAddress = "92.118.30.18"; - } + networking.firewall = { + interfaces.en-general = { + allowedTCPPorts = [ + 8080 6789 # Unifi + 53 # DNS + ]; + allowedUDPPorts = [ + 3478 10001 # Unifi + 53 # DNS + ]; + }; + interfaces.vl-eduroam = { + allowedTCPPorts = [ + 53 # DNS + ]; + allowedUDPPorts = [ + 53 # DNS + ]; + }; + interfaces.en-virginmedia = { + allowedUDPPorts = [ + 51820 + ]; + }; + interfaces.en-ee = { + allowedUDPPorts = [ + 51821 + ]; + }; + interfaces.en-gnet = { + allowedUDPPorts = [ + 51822 + ]; + }; + interfaces.wg-tuvok-ee = { + allowedUDPPorts = [ + 3784 # BFD + ]; + }; + interfaces.wg-tuvok-vm = { + allowedUDPPorts = [ + 3784 # BFD + ]; + }; + interfaces.wg-tuvok-gnet = { + allowedUDPPorts = [ + 3784 # BFD + ]; + }; + extraCommands = '' + ip46tables -F FORWARD + + ip46tables -N ts-forward || true + ip46tables -A FORWARD -j ts-forward + + iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360 + ip6tables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360 + + ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-ee -j ACCEPT + ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-vm -j ACCEPT + ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-gnet -j ACCEPT + ip46tables -A FORWARD -i vl-eduroam -m state --state NEW,RELATED -j REJECT + ''; + }; + + environment.systemPackages = with pkgs; [ + ethtool ]; - }; - systemd.services.dhcpd4 = { - wants = [ "systemd-networkd-wait-online.service" ]; - after = [ "systemd-networkd-wait-online.service" ]; - }; - services.unifi = { - enable = true; - openFirewall = false; - unifiPackage = depot.pkgs.unifi; - }; - services.prometheus.exporters.unifi-poller = { - enable = true; - controllers = [{ - url = "https://localhost:8443"; - verify_ssl = false; - user = "unifipoller"; - pass = pkgs.writeTextFile { name = "unifipoller-password"; text = "unifipoller"; }; - }]; - }; - - networking.firewall = { - interfaces.en-general = { - allowedTCPPorts = [ - 8080 6789 # Unifi - 53 # DNS - ]; - allowedUDPPorts = [ - 3478 10001 # Unifi - 53 # DNS - ]; - }; - interfaces.vl-eduroam = { - allowedTCPPorts = [ - 53 # DNS - ]; - allowedUDPPorts = [ - 53 # DNS - ]; - }; - interfaces.en-virginmedia = { - allowedUDPPorts = [ - 51820 - ]; - }; - interfaces.en-ee = { - allowedUDPPorts = [ - 51821 - ]; - }; - interfaces.en-gnet = { - allowedUDPPorts = [ - 51822 - ]; - }; - interfaces.wg-tuvok-ee = { - allowedUDPPorts = [ - 3784 # BFD - ]; - }; - interfaces.wg-tuvok-vm = { - allowedUDPPorts = [ - 3784 # BFD - ]; - }; - interfaces.wg-tuvok-gnet = { - allowedUDPPorts = [ - 3784 # BFD - ]; - }; - extraCommands = '' - ip46tables -F FORWARD - - ip46tables -N ts-forward || true - ip46tables -A FORWARD -j ts-forward - - iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360 - ip6tables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360 - - ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-ee -j ACCEPT - ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-vm -j ACCEPT - ip46tables -A FORWARD -i vl-eduroam -o wg-tuvok-gnet -j ACCEPT - ip46tables -A FORWARD -i vl-eduroam -m state --state NEW,RELATED -j REJECT - ''; - }; - - environment.systemPackages = with pkgs; [ - ethtool - ]; - - services.coredns = { - enable = true; - config = '' - .:53 { - bind 192.168.1.1 92.118.30.17 192.168.10.1 127.0.0.253 2a09:a443::1 2a09:a443:1::1 2a09:a443:2::1 2a09:a443:3::1 - acl { - allow net 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.0/8 100.64.0.0/10 2a09:a443::/32 92.118.30.0/24 - block + services.coredns = { + enable = true; + config = '' + .:53 { + bind 192.168.1.1 92.118.30.17 192.168.10.1 127.0.0.253 2a09:a443::1 2a09:a443:1::1 2a09:a443:2::1 2a09:a443:3::1 + acl { + allow net 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.0/8 100.64.0.0/10 2a09:a443::/32 92.118.30.0/24 + block + } + hosts /dev/null { + #216.239.38.120 stadia.google.com stadia.com + fallthrough + } + loadbalance + forward . tls://8.8.8.8 tls://8.8.4.4 { + tls_servername dns.google + } + cache { + success 4096 + denial 1024 + prefetch 512 + } + prometheus :9153 + errors + log } - hosts /dev/null { - #216.239.38.120 stadia.google.com stadia.com - fallthrough - } - loadbalance - forward . tls://8.8.8.8 tls://8.8.4.4 { - tls_servername dns.google - } - cache { - success 4096 - denial 1024 - prefetch 512 - } - prometheus :9153 - errors - log - } - ''; - }; - systemd.services.coredns = { - wants = [ "systemd-networkd-wait-online.service" ]; - after = [ "systemd-networkd-wait-online.service" ]; - }; - my.prometheus.additionalExporterPorts.coredns = 9153; - networking.resolvconf.extraConfig = '' - name_servers='127.0.0.253' - ''; - - services.prometheus.exporters.smokeping = { - enable = true; - hosts = [ - "8.8.8.8" # Google Public DNS - "2001:4860:4860::8888" - "youtube.com" "ads.google.com" "google.com" - - "1.1.1.1" # Cloudflare DNS - "2606:4700:4700::1111" - - "twitter.com" - - "store.steampowered.com" - "api.steampowered.com" - - "euw1.api.riotgames.com" # League of Legends EUW - - "eu.battle.net" - "185.60.112.157" "185.60.112.158" # Diablo 3/HotS/Hearthstone - "185.60.114.159" # Overwatch - - # BFOB TS - "2a01:a500:85:3::2" - "37.9.61.53" - - # lukegb01.ring.nlnog.net - "2a09:a441::13" - "92.118.28.13" - ]; - }; - - services.bird2 = { - enable = true; - config = '' - router id 92.118.30.254; - - protocol kernel { - kernel table 150; - metric 0; - ipv4 { - import none; - export all; - }; - }; - protocol kernel { - kernel table 150; - metric 0; - ipv6 { - import none; - export all; - }; - }; - protocol device {}; - - protocol static export4 { - ipv4 {}; - route 0.0.0.0/0 via 92.118.30.1 bfd { - # Virgin Media - preference = 100; - }; - route 0.0.0.0/0 via 92.118.30.3 bfd { - # EE - preference = 10; - }; - route 0.0.0.0/0 via 92.118.30.5 bfd { - # GNetwork - preference = 200; - }; - }; - protocol static export6 { - ipv6 {}; - route ::/0 via 2a09:a442::1:2 bfd { - # Virgin Media - preference = 100; - krt_prefsrc = 2a09:a443::1; - }; - route ::/0 via 2a09:a442::2:2 bfd { - # EE - preference = 10; - krt_prefsrc = 2a09:a443::1; - }; - route ::/0 via 2a09:a442::3:2 bfd { - # GNetwork - preference = 200; - krt_prefsrc = 2a09:a443::1; - }; - - # Covering route... - route 2a09:a443::/64 via "en-general"; - route 2a09:a443:1::/48 via "en-general"; - route 2a09:a443:2::/64 via "vl-eduroam"; - route 2a09:a443:3::/48 via "vl-eduroam"; - route 2a09:a443::/32 unreachable; - }; - - protocol bfd { - interface "*" { - min rx interval 10ms; - min tx interval 50ms; - idle tx interval 1s; - multiplier 20; - }; - neighbor 92.118.30.1; - neighbor 2a09:a442::1:2; - neighbor 92.118.30.3; - neighbor 2a09:a442::2:2; - neighbor 92.118.30.5; - neighbor 2a09:a442::3:2; - }; - ''; - }; - - services.radvd = { - enable = true; - config = '' - interface en-general { - AdvSendAdvert on; - AdvLinkMTU 1420; # Wireguard - AdvManagedFlag on; - - RDNSS 2a09:a443::1 {}; - DNSSL house.as205479.net {}; - - prefix 2a09:a443::/64 { - AdvOnLink on; - AdvAutonomous on; - }; - prefix 2a09:a443:1::/48 { - AdvOnLink on; - AdvAutonomous off; - }; - }; - interface vl-eduroam { - AdvSendAdvert on; - AdvLinkMTU 1420; # Wireguard - AdvManagedFlag on; - - RDNSS 2a09:a443:2::1 {}; - DNSSL eduroam.as205479.net {}; - - prefix 2a09:a443:2::/64 { - AdvOnLink on; - AdvAutonomous on; - }; - prefix 2a09:a443:3::/48 { - AdvOnLink on; - AdvAutonomous off; - }; - }; - ''; - }; - services.dhcpd6 = { - enable = true; - interfaces = ["en-general" "vl-eduroam"]; - authoritative = true; - extraConfig = '' - subnet6 2a09:a443:1::/48 { - range6 2a09:a443:1:1::/64; - range6 2a09:a443:1:2::/64 temporary; - prefix6 2a09:a443:1:1000:: 2a09:a443:1:ff00:: /56; - - option dhcp6.name-servers 2a09:a443:1::1; - option dhcp6.domain-search "house.as205479.net"; - } - subnet6 2a09:a443:3::/48 { - range6 2a09:a443:3:1::/64; - range6 2a09:a443:3:2::/64 temporary; - prefix6 2a09:a443:3:1000:: 2a09:a443:3:ff00:: /56; - - option dhcp6.name-servers 2a09:a443:3::1; - option dhcp6.domain-search "eduroam.as205479.net"; - } - ''; - }; - systemd.services.dhcpd6 = { - wants = [ "systemd-networkd-wait-online.service" ]; - after = [ "systemd-networkd-wait-online.service" ]; - }; - - systemd.services.prometheus-bird-exporter.serviceConfig.ExecStart = lib.mkForce '' - ${depot.pkgs.prometheus-bird-exporter-lfty}/bin/bird_exporter \ - -web.listen-address 0.0.0.0:9324 \ - -bird.socket /var/run/bird.ctl \ - -bird.v2=true \ - -format.new=true - ''; - - systemd.services.ee-scrape-data = let - scriptFile = ./ee-scrape-data.py; - python = pkgs.python3.withPackages (pm: with pm; [ - requests - beautifulsoup4 - html5lib - ]); - in { - enable = true; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${python}/bin/python ${scriptFile} /run/prometheus-textfile-exports/ee-scrape-data.prom"; + ''; }; - }; - systemd.timers.ee-scrape-data = { - enable = true; - wantedBy = [ "multi-user.target" ]; - timerConfig = { - OnBootSec = "2m"; - OnUnitInactiveSec = "1m"; - RandomizedDelaySec = "20"; + systemd.services.coredns = { + wants = [ "systemd-networkd-wait-online.service" ]; + after = [ "systemd-networkd-wait-online.service" ]; }; - }; + my.prometheus.additionalExporterPorts.coredns = 9153; + networking.resolvconf.extraConfig = '' + name_servers='127.0.0.253' + ''; - system.stateVersion = "21.03"; + services.prometheus.exporters.smokeping = { + enable = true; + hosts = [ + "8.8.8.8" # Google Public DNS + "2001:4860:4860::8888" + "youtube.com" "ads.google.com" "google.com" + + "1.1.1.1" # Cloudflare DNS + "2606:4700:4700::1111" + + "twitter.com" + + "store.steampowered.com" + "api.steampowered.com" + + "euw1.api.riotgames.com" # League of Legends EUW + + "eu.battle.net" + "185.60.112.157" "185.60.112.158" # Diablo 3/HotS/Hearthstone + "185.60.114.159" # Overwatch + + # BFOB TS + "2a01:a500:85:3::2" + "37.9.61.53" + + # lukegb01.ring.nlnog.net + "2a09:a441::13" + "92.118.28.13" + ]; + }; + + services.bird2 = { + enable = true; + config = '' + router id 92.118.30.254; + + protocol kernel { + kernel table 150; + metric 0; + ipv4 { + import none; + export all; + }; + }; + protocol kernel { + kernel table 150; + metric 0; + ipv6 { + import none; + export all; + }; + }; + protocol device {}; + + protocol static export4 { + ipv4 {}; + route 0.0.0.0/0 via 92.118.30.1 bfd { + # Virgin Media + preference = 100; + }; + route 0.0.0.0/0 via 92.118.30.3 bfd { + # EE + preference = 10; + }; + route 0.0.0.0/0 via 92.118.30.5 bfd { + # GNetwork + preference = 200; + }; + }; + protocol static export6 { + ipv6 {}; + route ::/0 via 2a09:a442::1:2 bfd { + # Virgin Media + preference = 100; + krt_prefsrc = 2a09:a443::1; + }; + route ::/0 via 2a09:a442::2:2 bfd { + # EE + preference = 10; + krt_prefsrc = 2a09:a443::1; + }; + route ::/0 via 2a09:a442::3:2 bfd { + # GNetwork + preference = 200; + krt_prefsrc = 2a09:a443::1; + }; + + # Covering route... + route 2a09:a443::/64 via "en-general"; + route 2a09:a443:1::/48 via "en-general"; + route 2a09:a443:2::/64 via "vl-eduroam"; + route 2a09:a443:3::/48 via "vl-eduroam"; + route 2a09:a443::/32 unreachable; + }; + + protocol bfd { + interface "*" { + min rx interval 10ms; + min tx interval 50ms; + idle tx interval 1s; + multiplier 20; + }; + neighbor 92.118.30.1; + neighbor 2a09:a442::1:2; + neighbor 92.118.30.3; + neighbor 2a09:a442::2:2; + neighbor 92.118.30.5; + neighbor 2a09:a442::3:2; + }; + ''; + }; + + services.radvd = { + enable = true; + config = '' + interface en-general { + AdvSendAdvert on; + AdvLinkMTU 1420; # Wireguard + AdvManagedFlag on; + + RDNSS 2a09:a443::1 {}; + DNSSL house.as205479.net {}; + + prefix 2a09:a443::/64 { + AdvOnLink on; + AdvAutonomous on; + }; + prefix 2a09:a443:1::/48 { + AdvOnLink on; + AdvAutonomous off; + }; + }; + interface vl-eduroam { + AdvSendAdvert on; + AdvLinkMTU 1420; # Wireguard + AdvManagedFlag on; + + RDNSS 2a09:a443:2::1 {}; + DNSSL eduroam.as205479.net {}; + + prefix 2a09:a443:2::/64 { + AdvOnLink on; + AdvAutonomous on; + }; + prefix 2a09:a443:3::/48 { + AdvOnLink on; + AdvAutonomous off; + }; + }; + ''; + }; + services.dhcpd6 = { + enable = true; + interfaces = ["en-general" "vl-eduroam"]; + authoritative = true; + extraConfig = '' + subnet6 2a09:a443:1::/48 { + range6 2a09:a443:1:1::/64; + range6 2a09:a443:1:2::/64 temporary; + prefix6 2a09:a443:1:1000:: 2a09:a443:1:ff00:: /56; + + option dhcp6.name-servers 2a09:a443:1::1; + option dhcp6.domain-search "house.as205479.net"; + } + subnet6 2a09:a443:3::/48 { + range6 2a09:a443:3:1::/64; + range6 2a09:a443:3:2::/64 temporary; + prefix6 2a09:a443:3:1000:: 2a09:a443:3:ff00:: /56; + + option dhcp6.name-servers 2a09:a443:3::1; + option dhcp6.domain-search "eduroam.as205479.net"; + } + ''; + }; + systemd.services.dhcpd6 = { + wants = [ "systemd-networkd-wait-online.service" ]; + after = [ "systemd-networkd-wait-online.service" ]; + }; + + systemd.services.prometheus-bird-exporter.serviceConfig.ExecStart = lib.mkForce '' + ${depot.pkgs.prometheus-bird-exporter-lfty}/bin/bird_exporter \ + -web.listen-address 0.0.0.0:9324 \ + -bird.socket /var/run/bird.ctl \ + -bird.v2=true \ + -format.new=true + ''; + + systemd.services.ee-scrape-data = let + scriptFile = ./ee-scrape-data.py; + python = pkgs.python3.withPackages (pm: with pm; [ + requests + beautifulsoup4 + html5lib + ]); + in { + enable = true; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${python}/bin/python ${scriptFile} /run/prometheus-textfile-exports/ee-scrape-data.prom"; + }; + }; + systemd.timers.ee-scrape-data = { + enable = true; + wantedBy = [ "multi-user.target" ]; + timerConfig = { + OnBootSec = "2m"; + OnUnitInactiveSec = "1m"; + RandomizedDelaySec = "20"; + }; + }; + + system.stateVersion = "21.03"; + } { + # Minimize writes to storage. + boot.tmpOnTmpfs = true; + services.journald.extraConfig = '' + Storage=volatile + ''; + systemd.services.tailscaled.environment.TS_LOGS_DIR = "/var/run/tailscale"; + + services.unifi.enable = mkForce false; + my.vault.enable = mkForce false; + } ]; }