From ee96713a506f9068b7258400ec3d00ed98455ea9 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 18:14:31 +1000 Subject: [PATCH] feat(networking): declare static IPs for all NixOS-managed hosts in flake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds static IP configuration to every NixOS host in the flake that has a fixed LAN address, and centralises all network primitives (IPs, gateway, prefix length, interface names) in variables.nix so there is one place to update if any of them change. variables.nix additions: - lanGateway / lanPrefixLength — LAN gateway and /24 prefix, replacing every hardcoded 192.168.2.254 / 24 across host files - lxcLanInterface / vmLanInterface / vmStorageInterface — NIC names for LXC containers (eth0), Proxmox VMs (ens18), and the HA storage NIC (ens19), used as attribute keys so changing the name is a one-line edit - haStoragePrefixLength — /29 for the storage subnet, mirrors haStorageCidr - Per-host IP variables: nixCacheIp (.224), tailscaleRouterIp (.222), torRelayIp (.221), serverIp (.226), dockerIp (.225) host.nix changes: - tailscale-router, tor-relay, nix-cache, pxe-boot: useDHCP = false, static address on eth0 (lxcLanInterface), struct-form defaultGateway (required when using systemd-networkd which LXC containers use) - server, docker: useDHCP = false, static address on ens18 (vmLanInterface), struct-form defaultGateway (works for both scripted networking and networkd) - ha-server-1, ha-server-2: replace hardcoded 192.168.2.254 / 24 / 29 with the new variables; no functional change for these hosts modules/build-types/pxe-boot.nix: - Domain-controller kickstart template: replace hardcoded 192.168.2.138 and 192.168.2.254 with vars.domainControllerIp / vars.lanGateway / vars.lanPrefixLength / vars.homeDomain so the template stays correct if the DC IP or domain is ever changed again Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01ULXzafSDwGhmFGnn3LtDSQ --- hosts/docker/host.nix | 15 ++++++++++++--- hosts/ha-server-1/host.nix | 10 +++++----- hosts/ha-server-2/host.nix | 10 +++++----- hosts/nix-cache/host.nix | 11 ++++++++++- hosts/pxe-boot/host.nix | 13 +++++++++++-- hosts/server/host.nix | 13 +++++++++++-- hosts/tailscale-router/host.nix | 13 +++++++++++-- hosts/tor-relay/host.nix | 17 +++++++++++++---- modules/build-types/pxe-boot.nix | 12 ++++++------ variables.nix | 17 ++++++++++++++--- 10 files changed, 98 insertions(+), 33 deletions(-) diff --git a/hosts/docker/host.nix b/hosts/docker/host.nix index f625e1e..4f9fda4 100644 --- a/hosts/docker/host.nix +++ b/hosts/docker/host.nix @@ -1,8 +1,17 @@ -_: +{ vars, ... }: { - networking.hostName = "docker"; - networking.hostId = "007f0200"; + networking = { + hostName = "docker"; + hostId = "007f0200"; + useDHCP = false; + interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ + address = vars.dockerIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; boot.zfs.forceImportRoot = false; # Preserved from the pre-refactor `docker` target — stateVersion must never diff --git a/hosts/ha-server-1/host.nix b/hosts/ha-server-1/host.nix index 455fb30..6afb486 100644 --- a/hosts/ha-server-1/host.nix +++ b/hosts/ha-server-1/host.nix @@ -11,15 +11,15 @@ hostName = vars.haServer1Host; hostId = "3a4b5c6d"; useDHCP = false; - interfaces.ens18.ipv4.addresses = [{ + interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.haServer1Ip; - prefixLength = 24; + prefixLength = vars.lanPrefixLength; }]; - interfaces.ens19.ipv4.addresses = [{ + interfaces.${vars.vmStorageInterface}.ipv4.addresses = [{ address = vars.haServer1StorageIp; - prefixLength = 29; + prefixLength = vars.haStoragePrefixLength; }]; - defaultGateway = "192.168.2.254"; + defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/hosts/ha-server-2/host.nix b/hosts/ha-server-2/host.nix index 2034fef..2f798f4 100644 --- a/hosts/ha-server-2/host.nix +++ b/hosts/ha-server-2/host.nix @@ -11,15 +11,15 @@ hostName = vars.haServer2Host; hostId = "7e8f9a0b"; useDHCP = false; - interfaces.ens18.ipv4.addresses = [{ + interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.haServer2Ip; - prefixLength = 24; + prefixLength = vars.lanPrefixLength; }]; - interfaces.ens19.ipv4.addresses = [{ + interfaces.${vars.vmStorageInterface}.ipv4.addresses = [{ address = vars.haServer2StorageIp; - prefixLength = 29; + prefixLength = vars.haStoragePrefixLength; }]; - defaultGateway = "192.168.2.254"; + defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/hosts/nix-cache/host.nix b/hosts/nix-cache/host.nix index 44146eb..89f6c55 100644 --- a/hosts/nix-cache/host.nix +++ b/hosts/nix-cache/host.nix @@ -8,7 +8,16 @@ }) ]; - networking.hostName = vars.nixCacheHost; + networking = { + hostName = vars.nixCacheHost; + useDHCP = false; + interfaces.${vars.lxcLanInterface}.ipv4.addresses = [{ + address = vars.nixCacheIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.lxcLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; services.beszel.agent.environment = { #DOCKER_HOST = "tcp://docker-socket-proxy:2375"; diff --git a/hosts/pxe-boot/host.nix b/hosts/pxe-boot/host.nix index 99378bb..639ecd2 100644 --- a/hosts/pxe-boot/host.nix +++ b/hosts/pxe-boot/host.nix @@ -1,7 +1,16 @@ -_: +{ vars, ... }: { - networking.hostName = "pxe-boot"; + networking = { + hostName = "pxe-boot"; + useDHCP = false; + interfaces.${vars.lxcLanInterface}.ipv4.addresses = [{ + address = vars.pxeServerIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.lxcLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; # Preserved from the pre-refactor `pxe-boot` target — stateVersion must # never be bumped on an already-installed machine. diff --git a/hosts/server/host.nix b/hosts/server/host.nix index ec43972..38544d5 100644 --- a/hosts/server/host.nix +++ b/hosts/server/host.nix @@ -8,8 +8,17 @@ }) ]; - networking.hostName = vars.nfsServerHost; - networking.hostId = "6689f93e"; + networking = { + hostName = vars.nfsServerHost; + hostId = "6689f93e"; + useDHCP = false; + interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ + address = vars.serverIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; services.beszel.agent.environment = { #DOCKER_HOST = "tcp://docker-socket-proxy:2375"; diff --git a/hosts/tailscale-router/host.nix b/hosts/tailscale-router/host.nix index d5b09f1..6198bf4 100644 --- a/hosts/tailscale-router/host.nix +++ b/hosts/tailscale-router/host.nix @@ -1,4 +1,4 @@ -_: +{ vars, ... }: { imports = [ @@ -8,7 +8,16 @@ _: }) ]; - networking.hostName = "tailscale-router"; + networking = { + hostName = "tailscale-router"; + useDHCP = false; + interfaces.${vars.lxcLanInterface}.ipv4.addresses = [{ + address = vars.tailscaleRouterIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.lxcLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; services.beszel.agent.environment = { KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFPR9kwtC4TAeTRu46A7+opZsYpxqkRJ+x/ZyB2GWCeG"; diff --git a/hosts/tor-relay/host.nix b/hosts/tor-relay/host.nix index bac4552..8cdcad3 100644 --- a/hosts/tor-relay/host.nix +++ b/hosts/tor-relay/host.nix @@ -1,4 +1,4 @@ -{ ... }: +{ vars, ... }: { imports = [ @@ -8,10 +8,19 @@ }) ]; - networking.hostName = "tor-relay"; + networking = { + hostName = "tor-relay"; + useDHCP = false; + interfaces.${vars.lxcLanInterface}.ipv4.addresses = [{ + address = vars.torRelayIp; + prefixLength = vars.lanPrefixLength; + }]; + defaultGateway = { address = vars.lanGateway; interface = vars.lxcLanInterface; }; + nameservers = [ vars.domainControllerIp ]; + }; - # No networking.hostId: only ZFS-touching hosts (server, docker) need one - # for pool-import safety, and this host does neither. + # No networking.hostId: only ZFS-touching hosts need one for pool-import + # safety, and this host does neither. services.beszel.agent.environment = { KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFPR9kwtC4TAeTRu46A7+opZsYpxqkRJ+x/ZyB2GWCeG"; diff --git a/modules/build-types/pxe-boot.nix b/modules/build-types/pxe-boot.nix index 16a43b5..034ddc2 100644 --- a/modules/build-types/pxe-boot.nix +++ b/modules/build-types/pxe-boot.nix @@ -82,7 +82,7 @@ let rockyFreeIpaKs = pkgs.writeText "rocky-freeipa.ks" '' #version=RHEL9 # Unattended Rocky Linux 9 + FreeIPA install - # Target: domain-controller.sweet.home 192.168.2.138 + # Target: domain-controller.${vars.homeDomain} ${vars.domainControllerIp} url --url=${rockyMirror}/BaseOS/${rockyArch}/os/ repo --name=appstream --baseurl=${rockyMirror}/AppStream/${rockyArch}/os/ @@ -136,10 +136,10 @@ let [ipv4] method=manual - addresses=192.168.2.138/24 - gateway=192.168.2.254 - dns=192.168.2.253; - dns-search=sweet.home; + addresses=${vars.domainControllerIp}/${toString vars.lanPrefixLength} + gateway=${vars.lanGateway} + dns=${vars.domainControllerIp}; + dns-search=${vars.homeDomain}; [ipv6] method=auto @@ -148,7 +148,7 @@ let # -- /etc/hosts: FQDN must resolve to the real IP (not loopback) for IPA -- sed -i '/domain-controller/d' /etc/hosts - echo '192.168.2.138 domain-controller.sweet.home domain-controller' >> /etc/hosts + echo '${vars.domainControllerIp} domain-controller.${vars.homeDomain} domain-controller' >> /etc/hosts # -- Generate IPA passwords and store securely -- DM_PASS=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24) diff --git a/variables.nix b/variables.nix index a16d792..ec50ec2 100644 --- a/variables.nix +++ b/variables.nix @@ -4,9 +4,19 @@ homeDomain = "sweet.home"; # base LAN domain for service subdomains (pve., docker.) tailnetDomain = "tail13f623.ts.net"; # Tailscale MagicDNS suffix lanCidr = "192.168.2.0/24"; # LAN subnet - pxeServerIp = "192.168.2.223"; # pxe-boot host's LAN IP - pbsIp = "192.168.2.244"; # Proxmox Backup Server LAN IP - domainControllerIp = "192.168.2.253"; # FreeIPA domain controller / primary DNS + lanGateway = "192.168.2.254"; # LAN default gateway (router) + lanPrefixLength = 24; # LAN subnet prefix length (/24 = 255.255.255.0) + lxcLanInterface = "eth0"; # LAN NIC name in LXC containers (set by Proxmox --net0 name=eth0) + vmLanInterface = "ens18"; # LAN NIC name in Proxmox VMs (virtio, first NIC) + vmStorageInterface = "ens19"; # storage NIC name in HA server VMs (virtio, second NIC on vmbr1) + pxeServerIp = "192.168.2.223"; # pxe-boot LXC container LAN IP + nixCacheIp = "192.168.2.224"; # nix-cache LXC container LAN IP + tailscaleRouterIp = "192.168.2.222"; # tailscale-router LXC container LAN IP + torRelayIp = "192.168.2.221"; # tor-relay LXC container LAN IP + serverIp = "192.168.2.226"; # server (NFS/ZFS) Proxmox VM LAN IP + dockerIp = "192.168.2.225"; # docker Proxmox VM LAN IP + pbsIp = "192.168.2.244"; # Proxmox Backup Server LAN IP (not NixOS-managed) + domainControllerIp = "192.168.2.253"; # FreeIPA domain controller / primary DNS (not NixOS-managed) # Cross-host references (LAN hostnames/users other hosts reach over the network) nixCacheHost = "nix-cache"; # substituter/remote-builder hostname @@ -83,6 +93,7 @@ haServer1StorageIp = "192.168.4.228"; # storage-net IP, node 1 (vmbr1 / ens19) haServer2StorageIp = "192.168.4.227"; # storage-net IP, node 2 (vmbr1 / ens19) haStorageCidr = "192.168.4.0/29"; # storage subnet — internal to pve1 only + haStoragePrefixLength = 29; # storage subnet prefix length (/29) haStorageRoot = "/srv/ha-data"; # XFS-over-DRBD mount point on the Active node haIscsiIqn = "iqn.2026-01.home.sweet:ha-storage";