diff --git a/lib.nix b/lib.nix index 458eee5..d6b07fe 100644 --- a/lib.nix +++ b/lib.nix @@ -1,19 +1,19 @@ { lib }: let - inherit (builtins) replaceStrings elemAt mapAttrs; + inherit (builtins) length match replaceStrings elemAt mapAttrs head split; inherit (lib) genAttrs mapAttrs' mapAttrsToList filterAttrsRecursive nameValuePair types - mkOption mkOverride mkForce mergeEqualOption; + mkOption mkOverride mkForce mergeEqualOption optional; inherit (lib.flake) defaultSystems; in rec { # Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix - isIPv6 = ip: builtins.length (lib.splitString ":" ip) > 2; + isIPv6 = ip: length (lib.splitString ":" ip) > 2; parseIPPort = ipp: let v6 = isIPv6 ipp; matchIP = if v6 then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)"; - m = builtins.match "${matchIP}:([0-9-]+)" ipp; + m = match "${matchIP}:([0-9-]+)" ipp; checked = v: if m == null then throw "bad ip:ports `${ipp}'" else v; in { @@ -21,6 +21,7 @@ rec { ip = checked (elemAt m 0); ports = checked (replaceStrings ["-"] [":"] (elemAt m 1)); }; + naiveIPv4Gateway = ip: "${head (elemAt (split ''([0-9]+\.[0-9]+\.[0-9]+)\.[0-9]+'' ip) 1)}.1"; attrsToNVList = mapAttrsToList nameValuePair; mkDefaultSystemsPkgs = path: args': genAttrs defaultSystems (system: import path ((args' system) // { inherit system; })); @@ -84,6 +85,15 @@ rec { home-manager = mkOpt' (enum [ "unstable" "stable" ]) "unstable" "Branch of home-manager to use."; }; + networkdAssignment = iface: a: { + matchConfig.Name = iface; + address = [ "${a.ipv4.address}/${toString a.ipv4.mask}" "${a.ipv6.address}/${toString a.ipv6.mask}" ]; + gateway = + (optional (a.ipv4.gateway != null) a.ipv4.gateway) ++ + (optional (a.ipv6.gateway != null) a.ipv6.gateway); + networkConfig.IPv6AcceptRA = a.ipv6.gateway == null; + }; + deploy-rs = with types; let diff --git a/nixos/boxes/colony.nix b/nixos/boxes/colony.nix index 1502006..07d88ea 100644 --- a/nixos/boxes/colony.nix +++ b/nixos/boxes/colony.nix @@ -4,9 +4,17 @@ nixpkgs = "mine"; home-manager = "unstable"; - configuration = { lib, pkgs, modulesPath, config, systems, ... }: + assignments.internal = { + name = "colony"; + altNames = [ "vm" ]; + ipv4.address = "10.100.0.2"; + ipv6.address = "2a0e:97c0:4d1:0::2"; + }; + + configuration = { lib, pkgs, modulesPath, config, systems, assignments, ... }: let inherit (lib) mkIf mapAttrs; + inherit (lib.my) networkdAssignment; wanBDF = if config.my.build.isDevVM then "00:02.0" else "01:00.0"; @@ -50,20 +58,11 @@ systemd = { network = { - netdevs."25-base-bridge".netdevConfig = { + netdevs."25-base".netdevConfig = { Name = "base"; Kind = "bridge"; }; - networks."80-base-bridge" = { - matchConfig = { - Name = "base"; - Driver = "bridge"; - }; - DHCP = "ipv4"; - networkConfig = { - IPv6AcceptRA = true; - }; - }; + networks."80-base" = networkdAssignment "base" assignments.internal; }; services."vm@estuary" = rec { # Bind to the interface, networkd wait-online would deadlock... @@ -93,10 +92,6 @@ server.enable = true; - network = { - ipv6 = "2a0e:97c0:4d1:0::2"; - ipv4 = "10.110.0.2"; - }; firewall = { trustedInterfaces = [ "base" ]; }; diff --git a/nixos/default.nix b/nixos/default.nix index 348f222..7e44e2e 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -2,10 +2,12 @@ let inherit (builtins) attrValues mapAttrs; inherit (lib) substring flatten optional optionals mkDefault mkOption mkOptionType; - inherit (lib.my) homeStateVersion mkOpt' mkBoolOpt' commonOpts inlineModule'; + inherit (lib.my) naiveIPv4Gateway homeStateVersion mkOpt' mkBoolOpt' commonOpts inlineModule'; cfg = config.nixos; + allAssignments = mapAttrs (_: c: c.assignments) cfg.systems; + mkSystem = { name, @@ -37,7 +39,7 @@ let lib = pkgs.lib; # Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports - specialArgs = { inherit inputs; inherit (cfg) systems; }; + specialArgs = { inherit inputs allAssignments; inherit (cfg) systems; }; # `baseModules` informs the manual which modules to document baseModules = @@ -53,6 +55,7 @@ let _module.args = { inherit (cfg) secretsPath; + inherit (config') assignments; pkgs' = allPkgs; }; @@ -96,6 +99,24 @@ let ] ++ defs; }; + assignmentOpts = with lib.types; { name, config, ... }: { + options = { + name = mkOpt' str name "Name of assignment."; + altNames = mkOpt' (listOf str) [ ] "Extra names to assign."; + visible = mkBoolOpt' true "Whether or not this assignment should be visible."; + ipv4 = { + address = mkOpt' str null "IPv4 address."; + mask = mkOpt' ints.u8 24 "Network mask."; + gateway = mkOpt' (nullOr str) (naiveIPv4Gateway config.ipv4.address) "IPv4 gateway."; + }; + ipv6 = { + address = mkOpt' str null "IPv6 address."; + mask = mkOpt' ints.u8 64 "Network mask."; + gateway = mkOpt' (nullOr str) null "IPv6 gateway."; + }; + }; + }; + systemOpts = with lib.types; { name, config, ... }: { options = { inherit (commonOpts) system nixpkgs home-manager; @@ -104,6 +125,10 @@ let # TODO: Currently broken with infinite recursion... docCustom = mkBoolOpt' false "Whether to document nixfiles' custom NixOS modules."; + assignments = mkOpt' (attrsOf (submoduleWith { + modules = [ assignmentOpts { _module.args.name = name; } ]; + })) { } "Network assignments."; + configuration = mkOption { description = "NixOS configuration module."; # Based on the definition of containers..config diff --git a/nixos/modules/network.nix b/nixos/modules/network.nix index c705316..f86d3bc 100644 --- a/nixos/modules/network.nix +++ b/nixos/modules/network.nix @@ -1,18 +1,8 @@ { lib, config, ... }: let inherit (lib) flatten optional mkIf mkDefault mkMerge; - inherit (lib.my) mkOpt' mkBoolOpt'; - - cfg = config.my.network; in { - options = with lib.types; { - my.network = { - ipv4 = mkOpt' str null "Internal network IPv4 address."; - ipv6 = mkOpt' str null "Internal network IPv6 address."; - }; - }; - config = mkMerge [ { networking = { diff --git a/nixos/vms/estuary.nix b/nixos/vms/estuary.nix index 34ea11e..df5c11a 100644 --- a/nixos/vms/estuary.nix +++ b/nixos/vms/estuary.nix @@ -4,9 +4,20 @@ nixpkgs = "mine"; home-manager = "unstable"; - configuration = { lib, pkgs, modulesPath, config, systems, ... }: + assignments.internal = { + name = "estuary.vm"; + altNames = [ "fw" ]; + ipv4 = { + address = "10.100.0.1"; + gateway = null; + }; + ipv6.address = "2a0e:97c0:4d1:0::1"; + }; + + configuration = { lib, pkgs, modulesPath, config, systems, assignments, ... }: let inherit (lib) mkIf mkMerge; + inherit (lib.my) networkdAssignment; in { imports = [ "${modulesPath}/profiles/qemu-guest.nix" ]; @@ -59,14 +70,18 @@ matchConfig.Name = "wan"; DHCP = "ipv4"; }; - "80-base" = { - matchConfig.Name = "base"; - address = with config.my.network; [ "${ipv4}/24" "${ipv6}/64" ]; + "80-base" = (networkdAssignment "base" assignments.internal) // { networkConfig = { - DHCPServer = true; + IPv6AcceptRA = false; IPv6SendRA = true; IPMasquerade = "both"; }; + ipv6SendRAConfig.DNS = [ assignments.internal.ipv6.address ]; + ipv6Prefixes = [ + { + ipv6PrefixConfig.Prefix = "2a0e:97c0:4d1:0::/64"; + } + ]; }; }; }; @@ -74,10 +89,6 @@ my = { server.enable = true; - network = { - ipv6 = "2a0e:97c0:4d1:0::1"; - ipv4 = "10.110.0.1"; - }; firewall = { trustedInterfaces = [ "base" ]; nat = {