diff --git a/SwarselSystems.org b/SwarselSystems.org index 48bf906..870a41c 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -689,6 +689,22 @@ Concerning the =flake = _:= part: inherit (inputs.nixpkgs) lib; in rec { + cidrToSubnetMask = cidr: + let + prefixLength = lib.toInt (lib.last (lib.splitString "/" cidr)); + bits = lib.genList (i: if i < prefixLength then 1 else 0) 32; + octets = lib.genList + (i: + let + octetBits = lib.sublist (i * 8) 8 bits; + octetValue = lib.foldl (acc: bit: acc * 2 + bit) 0 octetBits; + in + octetValue + ) 4; + subnetMask = lib.concatStringsSep "." (map toString octets); + in + subnetMask; + mkIfElseList = p: yes: no: lib.mkMerge [ (lib.mkIf p yes) (lib.mkIf (!p) no) @@ -2533,7 +2549,11 @@ This is my main server that I run at home. It handles most tasks that require bi loader.efi.canTouchEfiVariables = true; }; - globals.hosts.${config.node.name}.ipv4 = config.repo.secrets.local.ipv4; + # globals.hosts.${config.node.name}.ipv4 = config.repo.secrets.local.ipv4; + # globals.networks.home.hosts.${config.node.name} = { + # ipv4 = config.repo.secrets.local.home-ipv4; + # mac = config.repo.secrets.local.home-mac; + # }; networking = { inherit (config.repo.secrets.local) hostId; @@ -2673,8 +2693,6 @@ This is my main server that I run at home. It handles most tasks that require bi loader.efi.canTouchEfiVariables = true; }; - # globals.hosts.${config.node.name}.ipv4 = config.repo.secrets.local.ipv4; - networking = { inherit (config.repo.secrets.local) hostId; hostName = configName; @@ -3837,36 +3855,32 @@ TODO: cleanup this mess { self, config, pkgs, lib, ... }: let pubKeys = lib.filesystem.listFilesRecursive "${self}/secrets/keys/ssh"; - in - { - - config = { - home-manager.users.root.home = { - stateVersion = "23.05"; - file = { + stateVersion = lib.mkDefault "23.05"; + homeFiles = { ".bash_history" = { text = '' swarsel-install -n hotel ''; }; }; + in + { + + config = { + home-manager.users.root.home = { + inherit stateVersion; + file = homeFiles; }; home-manager.users.swarsel = { home = { username = "swarsel"; homeDirectory = lib.mkDefault "/home/swarsel"; - stateVersion = lib.mkDefault "23.05"; + inherit stateVersion; keyboard.layout = "us"; sessionVariables = { FLAKE = "/home/swarsel/.dotfiles"; }; - file = { - ".bash_history" = { - text = '' - swarsel-install -n hotel - ''; - }; - }; + file = homeFiles; }; }; @@ -3884,10 +3898,6 @@ TODO: cleanup this mess nix = { channel.enable = false; package = pkgs.nixVersions.nix_2_28; - # extraOptions = '' - # plugin-files = ${pkgs.dev.nix-plugins}/lib/nix/plugins - # extra-builtins-file = ${../nix/extra-builtins.nix} - # ''; extraOptions = '' plugin-files = ${pkgs.nix-plugins.overrideAttrs (o: { buildInputs = [config.nix.package pkgs.boost]; @@ -3939,6 +3949,7 @@ TODO: cleanup this mess environment.etc."issue".text = '' ~SwarselSystems~ IP of primary interface: \4 + These IPs were also found: \4{eth0} \4{eth1} \4{eth2} \4{eth3} \4{wlan0} The Password for all users & root is 'setup'. Install the system remotely by running 'bootstrap -n -d ' on a machine with deployed secrets. Alternatively, run 'swarsel-install -n ' for a local install. For your convenience, an example call is in the bash history (press up on the keyboard to access). @@ -3949,6 +3960,7 @@ TODO: cleanup this mess wireless.enable = false; # dhcpcd.runHook = "${pkgs.utillinux}/bin/agetty --reload"; networkmanager.enable = true; + usePredictableInterfaceNames = false; }; services.getty.autologinUser = lib.mkForce "root"; @@ -3975,6 +3987,8 @@ TODO: cleanup this mess programs.bash.shellAliases = { "swarsel-install" = "nix run github:Swarsel/.dotfiles#swarsel-install --"; + "swarsel-net-manufacturer" = "lspci -nn | grep -i 'network\|ethernet'"; + "swarsel-kernel-module" = "lspci -k -d"; }; system.activationScripts.cache = { @@ -4363,6 +4377,91 @@ in mkOption types ; + + networkOptions = netSubmod: { + cidrv4 = mkOption { + type = types.nullOr types.net.cidrv4; + description = "The CIDRv4 of this network"; + default = null; + }; + + subnetMask4 = mkOption { + type = types.nullOr types.net.cidrv4; + description = "The dotted decimal form of the subnet mask of this network"; + readOnly = true; + default = lib.swarselsystems.cidrToSubnetMask netSubmod.cidrv4; + }; + + cidrv6 = mkOption { + type = types.nullOr types.net.cidrv6; + description = "The CIDRv6 of this network"; + default = null; + }; + + hosts = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (hostSubmod: { + options = { + id = mkOption { + type = types.int; + description = "The id of this host in the network"; + }; + + mac = mkOption { + type = types.nullOr types.net.mac; + description = "The MAC of the interface on this host that belongs to this network."; + default = null; + }; + + ipv4 = mkOption { + type = types.nullOr types.net.ipv4; + description = "The IPv4 of this host in this network"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4; + }; + + ipv6 = mkOption { + type = types.nullOr types.net.ipv6; + description = "The IPv6 of this host in this network"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6; + }; + + cidrv4 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv4 of this host in this network, including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4; + }; + + cidrv6 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv6 of this host in this network, including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6; + }; + }; + }) + ); + }; + }; in { options = { @@ -4398,12 +4497,44 @@ in ); }; + networks = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (netSubmod: { + options = networkOptions netSubmod // { + vlans = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (vlanNetSubmod: { + options = networkOptions vlanNetSubmod // { + id = mkOption { + type = types.ints.between 1 4094; + description = "The VLAN id"; + }; + + name = mkOption { + description = "The name of this VLAN"; + default = vlanNetSubmod.config._module.args.name; + type = types.str; + }; + }; + }) + ); + }; + }; + }) + ); + }; + hosts = mkOption { type = types.attrsOf ( types.submodule { options = { - ipv4 = mkOption { - type = types.str; + defaultGateway4 = mkOption { + type = types.nullOr types.net.ipv4; + }; + defaultGateway6 = mkOption { + type = types.nullOr types.net.ipv6; }; }; } @@ -6941,6 +7072,166 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t } #+end_src +**** Network settings + + +#+begin_src nix-ts :tangle modules/nixos/server/network.nix + { lib, config, ... }: + { + options.swarselmodules.server.network = lib.mkEnableOption "enable server network config"; + config = lib.mkIf config.swarselmodules.server.network { + + globals.networks.home.hosts.${config.node.name} = { + inherit (config.repo.secrets.local.networking.networks.home) id; + mac = config.repo.secrets.local.networking.networks.home.mac or null; + }; + + globals.hosts.${config.node.name} = { + inherit (config.repo.secrets.local.networking) defaultGateway4; + }; + + networking = { + inherit (config.repo.secrets.local.networking) hostId; + hostName = config.node.name; + nftables.enable = lib.mkDefault true; + enableIPv6 = lib.mkDefault true; + firewall = { + enable = lib.mkDefault true; + }; + }; + + }; + } +#+end_src + +**** Disk encryption + +The hostkey can be generated with =ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key=. +Use =lspci -v | grep -iA8 'network\|ethernet'= to supposedly find out which kernel module is needed for networking in initrd. However I prefer a different approach: + +Use =lspci -nn | grep -i network= to find out manufacturer info: + +#+begin_src shell :exports both +lspci -nn | grep -i 'network\|ethernet' +#+end_src + +#+RESULTS: +: 04:00.0 Network controller [0280]: MEDIATEK Corp. MT7922 802.11ax PCI Express Wireless Network Adapter [14c3:0616] + +From the last bracket, then take the first value to find out the correct kernel module: + +#+begin_src shell :exports both +lspci -k -d 14c3: +#+end_src + +#+RESULTS: +| 04:00.0 | Network | controller: | MEDIATEK | Corp. | MT7922 | 802.11ax | PCI | Express | Wireless | Network | Adapter | +| | Subsystem: | MEDIATEK | Corp. | Device | e616 | | | | | | | +| | Kernel | driver | in | use: | mt7921e | | | | | | | +| | Kernel | modules: | mt7921e | | | | | | | | | + +#+begin_src nix-ts :tangle modules/nixos/server/disk-encrypt.nix + { self, lib, config, globals, ... }: + let + localIp = globals.networks.home.hosts.${config.node.name}.ipv4; + subnetMask = globals.networks.home.subnetMask4; + gatewayIp = globals.hosts.${config.node.name}.defaultGateway4; + in + { + options.swarselmodules.server.diskEncryption = lib.mkEnableOption "enable disk encryption config"; + config = lib.mkIf (config.swarselmodules.server.diskEncryption && config.swarselsystems.isCrypted) { + + boot.kernelParams = lib.mkIf (!config.swarselsystems.isLaptop) [ "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none" ]; + boot.initrd = { + availableKernelModules = [ "r8169" ]; + network = { + enable = true; + udhcpc.enable = lib.mkIf config.swarselsystems.isLaptop true; + flushBeforeStage2 = true; + ssh = { + enable = true; + port = 22; + authorizedKeyFiles = [ + (self + /secrets/keys/ssh/yubikey.pub) + (self + /secrets/keys/ssh/magicant.pub) + ]; + hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; + }; + postCommands = '' + echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1' >> /root/.profile + ''; + }; + }; + + }; + } +#+end_src + +**** kavita +:PROPERTIES: +:CUSTOM_ID: h:d33f5982-dfe6-42d0-9cf2-2cd8c7b04295 +:END: + +#+begin_src nix-ts :tangle modules/nixos/server/router.nix + { self, lib, config, pkgs, globals, ... }: + let + serviceName = "router"; + serviceUser = "kavita"; + in + { + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + systemd.network = { + wait-online.anyInterface = true; + networks = { + "30-lan0" = { + matchConfig.Name = "lan0"; + linkConfig.RequiredForOnline = "enslaved"; + networkConfig = { + ConfigureWithoutCarrier = true; + }; + }; + "30-lan1" = { + matchConfig.Name = "lan1"; + linkConfig.RequiredForOnline = "enslaved"; + networkConfig = { + ConfigureWithoutCarrier = true; + }; + }; + "30-lan2" = { + matchConfig.Name = "lan2"; + linkConfig.RequiredForOnline = "enslaved"; + networkConfig = { + ConfigureWithoutCarrier = true; + }; + }; + "30-lan3" = { + matchConfig.Name = "lan3"; + linkConfig.RequiredForOnline = "enslaved"; + networkConfig = { + ConfigureWithoutCarrier = true; + }; + }; + "10-wan" = { + matchConfig.Name = "wan"; + networkConfig = { + # start a DHCP Client for IPv4 Addressing/Routing + DHCP = "ipv4"; + DNSOverTLS = true; + DNSSEC = true; + IPv6PrivacyExtensions = false; + IPForward = true; + }; + # make routing on this interface a dependency for network-online.target + linkConfig.RequiredForOnline = "routable"; + }; + }; + }; + }; + } +#+end_src + **** kavita :PROPERTIES: :CUSTOM_ID: h:d33f5982-dfe6-42d0-9cf2-2cd8c7b04295 @@ -6955,7 +7246,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceName = "kavita"; serviceUser = "kavita"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -7027,7 +7318,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceName = "jellyfin"; serviceUser = "jellyfin"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -7099,7 +7390,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceUser = "navidrome"; serviceGroup = serviceUser; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -7453,7 +7744,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceName = "matrix"; serviceDomain = config.repo.secrets.common.services.domains.matrix; serviceUser = "matrix-synapse"; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; federationPort = 8448; whatsappPort = 29318; @@ -7811,7 +8102,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceGroup = serviceUser; serviceName = "nextcloud"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -7891,7 +8182,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t serviceUser = "immich"; serviceName = "immich"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -7976,7 +8267,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= serviceGroup = serviceUser; serviceName = "paperless"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; tikaPort = 9998; gotenbergPort = 3002; @@ -8304,7 +8595,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= serviceUser = "syncthing"; serviceGroup = serviceUser; serviceName = "syncthing"; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; specificServiceName = "syncthing-${configName}"; cfg = config.services.${serviceName}; @@ -8530,7 +8821,7 @@ This section exposes several metrics that I use to check the health of my server serviceGroup = serviceUser; serviceName = "grafana"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; prometheusPort = 9090; prometheusUser = "prometheus"; @@ -8784,7 +9075,7 @@ This is a WIP Jenkins instance. It is used to automatically build a new system w servicePort = 8088; serviceName = "jenkins"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -8879,7 +9170,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with serviceUser = "freshrss"; serviceGroup = serviceName; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; inherit (config.swarselsystems) sopsFile; in @@ -8995,7 +9286,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with serviceGroup = serviceUser; serviceName = "forgejo"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; kanidmDomain = globals.services.kanidm.domain; in @@ -9159,7 +9450,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with servicePort = 27701; serviceName = "ankisync"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; ankiUser = globals.user.name; in @@ -9244,7 +9535,7 @@ To get other URLs (token, etc.), use https:///oauth2/openid//oauth2/openid//oauth2/openid//oauth2/openid//oauth2/openid/ -d ' on a machine with deployed secrets. Alternatively, run 'swarsel-install -n ' for a local install. For your convenience, an example call is in the bash history (press up on the keyboard to access). @@ -113,6 +106,7 @@ in wireless.enable = false; # dhcpcd.runHook = "${pkgs.utillinux}/bin/agetty --reload"; networkmanager.enable = true; + usePredictableInterfaceNames = false; }; services.getty.autologinUser = lib.mkForce "root"; @@ -139,6 +133,8 @@ in programs.bash.shellAliases = { "swarsel-install" = "nix run github:Swarsel/.dotfiles#swarsel-install --"; + "swarsel-net-manufacturer" = "lspci -nn | grep -i 'network\|ethernet'"; + "swarsel-kernel-module" = "lspci -k -d"; }; system.activationScripts.cache = { diff --git a/modules/nixos/common/globals.nix b/modules/nixos/common/globals.nix index c42e7ae..8d226d4 100644 --- a/modules/nixos/common/globals.nix +++ b/modules/nixos/common/globals.nix @@ -4,6 +4,91 @@ let mkOption types ; + + networkOptions = netSubmod: { + cidrv4 = mkOption { + type = types.nullOr types.net.cidrv4; + description = "The CIDRv4 of this network"; + default = null; + }; + + subnetMask4 = mkOption { + type = types.nullOr types.net.cidrv4; + description = "The dotted decimal form of the subnet mask of this network"; + readOnly = true; + default = lib.swarselsystems.cidrToSubnetMask netSubmod.cidrv4; + }; + + cidrv6 = mkOption { + type = types.nullOr types.net.cidrv6; + description = "The CIDRv6 of this network"; + default = null; + }; + + hosts = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (hostSubmod: { + options = { + id = mkOption { + type = types.int; + description = "The id of this host in the network"; + }; + + mac = mkOption { + type = types.nullOr types.net.mac; + description = "The MAC of the interface on this host that belongs to this network."; + default = null; + }; + + ipv4 = mkOption { + type = types.nullOr types.net.ipv4; + description = "The IPv4 of this host in this network"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv4; + }; + + ipv6 = mkOption { + type = types.nullOr types.net.ipv6; + description = "The IPv6 of this host in this network"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.host hostSubmod.config.id netSubmod.config.cidrv6; + }; + + cidrv4 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv4 of this host in this network, including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv4 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv4; + }; + + cidrv6 = mkOption { + type = types.nullOr types.str; # FIXME: this is not types.net.cidr because it would zero out the host part + description = "The IPv6 of this host in this network, including CIDR mask"; + readOnly = true; + default = + if netSubmod.config.cidrv6 == null then + null + else + lib.net.cidr.hostCidr hostSubmod.config.id netSubmod.config.cidrv6; + }; + }; + }) + ); + }; + }; in { options = { @@ -39,12 +124,44 @@ in ); }; + networks = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (netSubmod: { + options = networkOptions netSubmod // { + vlans = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (vlanNetSubmod: { + options = networkOptions vlanNetSubmod // { + id = mkOption { + type = types.ints.between 1 4094; + description = "The VLAN id"; + }; + + name = mkOption { + description = "The name of this VLAN"; + default = vlanNetSubmod.config._module.args.name; + type = types.str; + }; + }; + }) + ); + }; + }; + }) + ); + }; + hosts = mkOption { type = types.attrsOf ( types.submodule { options = { - ipv4 = mkOption { - type = types.str; + defaultGateway4 = mkOption { + type = types.nullOr types.net.ipv4; + }; + defaultGateway6 = mkOption { + type = types.nullOr types.net.ipv6; }; }; } diff --git a/modules/nixos/server/ankisync.nix b/modules/nixos/server/ankisync.nix index 0447dea..b845ad7 100644 --- a/modules/nixos/server/ankisync.nix +++ b/modules/nixos/server/ankisync.nix @@ -5,7 +5,7 @@ let servicePort = 27701; serviceName = "ankisync"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; ankiUser = globals.user.name; in diff --git a/modules/nixos/server/atuin.nix b/modules/nixos/server/atuin.nix index 38fe352..d355e6f 100644 --- a/modules/nixos/server/atuin.nix +++ b/modules/nixos/server/atuin.nix @@ -3,7 +3,7 @@ let servicePort = 8888; serviceName = "atuin"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/disk-encrypt.nix b/modules/nixos/server/disk-encrypt.nix new file mode 100644 index 0000000..dddc1a4 --- /dev/null +++ b/modules/nixos/server/disk-encrypt.nix @@ -0,0 +1,34 @@ +{ self, lib, config, globals, ... }: +let + localIp = globals.networks.home.hosts.${config.node.name}.ipv4; + subnetMask = globals.networks.home.subnetMask4; + gatewayIp = globals.hosts.${config.node.name}.defaultGateway4; +in +{ + options.swarselmodules.server.diskEncryption = lib.mkEnableOption "enable disk encryption config"; + config = lib.mkIf (config.swarselmodules.server.diskEncryption && config.swarselsystems.isCrypted) { + + boot.kernelParams = lib.mkIf (!config.swarselsystems.isLaptop) [ "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none" ]; + boot.initrd = { + availableKernelModules = [ "r8169" ]; + network = { + enable = true; + udhcpc.enable = lib.mkIf config.swarselsystems.isLaptop true; + flushBeforeStage2 = true; + ssh = { + enable = true; + port = 22; + authorizedKeyFiles = [ + (self + /secrets/keys/ssh/yubikey.pub) + (self + /secrets/keys/ssh/magicant.pub) + ]; + hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; + }; + postCommands = '' + echo 'cryptsetup-askpass || echo "Unlock was successful; exiting SSH session" && exit 1' >> /root/.profile + ''; + }; + }; + + }; +} diff --git a/modules/nixos/server/firefly-iii.nix b/modules/nixos/server/firefly-iii.nix index 37aa48a..c0acad1 100644 --- a/modules/nixos/server/firefly-iii.nix +++ b/modules/nixos/server/firefly-iii.nix @@ -5,7 +5,7 @@ let serviceGroup = serviceUser; serviceName = "firefly-iii"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; nginxGroup = "nginx"; diff --git a/modules/nixos/server/forgejo.nix b/modules/nixos/server/forgejo.nix index 886c6aa..a674078 100644 --- a/modules/nixos/server/forgejo.nix +++ b/modules/nixos/server/forgejo.nix @@ -7,7 +7,7 @@ let serviceGroup = serviceUser; serviceName = "forgejo"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; kanidmDomain = globals.services.kanidm.domain; in diff --git a/modules/nixos/server/freshrss.nix b/modules/nixos/server/freshrss.nix index 8e94add..0375e64 100644 --- a/modules/nixos/server/freshrss.nix +++ b/modules/nixos/server/freshrss.nix @@ -5,7 +5,7 @@ let serviceUser = "freshrss"; serviceGroup = serviceName; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; inherit (config.swarselsystems) sopsFile; in diff --git a/modules/nixos/server/garage.nix b/modules/nixos/server/garage.nix index 5ac3673..d537552 100644 --- a/modules/nixos/server/garage.nix +++ b/modules/nixos/server/garage.nix @@ -5,7 +5,7 @@ let serviceName = "garage"; servicePort = 3900; serviceDomain = config.repo.secrets.common.services.domains."${serviceName}-${configName}"; - serviceAddress = globals.hosts.${configName}.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; cfg = config.services.${serviceName}; metadata_dir = "/var/lib/garage/meta"; diff --git a/modules/nixos/server/homebox.nix b/modules/nixos/server/homebox.nix index 56adac9..c1b62ab 100644 --- a/modules/nixos/server/homebox.nix +++ b/modules/nixos/server/homebox.nix @@ -3,7 +3,7 @@ let servicePort = 7745; serviceName = "homebox"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/immich.nix b/modules/nixos/server/immich.nix index e3bc4a0..cefa330 100644 --- a/modules/nixos/server/immich.nix +++ b/modules/nixos/server/immich.nix @@ -4,7 +4,7 @@ let serviceUser = "immich"; serviceName = "immich"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/jellyfin.nix b/modules/nixos/server/jellyfin.nix index 420bbb6..552f8bf 100644 --- a/modules/nixos/server/jellyfin.nix +++ b/modules/nixos/server/jellyfin.nix @@ -4,7 +4,7 @@ let serviceName = "jellyfin"; serviceUser = "jellyfin"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/jenkins.nix b/modules/nixos/server/jenkins.nix index 91d94f0..808bcef 100644 --- a/modules/nixos/server/jenkins.nix +++ b/modules/nixos/server/jenkins.nix @@ -3,7 +3,7 @@ let servicePort = 8088; serviceName = "jenkins"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index 79b1983..e7ab275 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -8,7 +8,7 @@ let serviceGroup = serviceUser; serviceName = "kanidm"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; oauth2ProxyDomain = globals.services.oauth2Proxy.domain; immichDomain = globals.services.immich.domain; diff --git a/modules/nixos/server/kavita.nix b/modules/nixos/server/kavita.nix index c93be62..dfa915e 100644 --- a/modules/nixos/server/kavita.nix +++ b/modules/nixos/server/kavita.nix @@ -6,7 +6,7 @@ let serviceName = "kavita"; serviceUser = "kavita"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/koillection.nix b/modules/nixos/server/koillection.nix index 08da2d1..eb45709 100644 --- a/modules/nixos/server/koillection.nix +++ b/modules/nixos/server/koillection.nix @@ -6,7 +6,7 @@ let servicePort = 2282; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; serviceDir = "/Vault/data/koillection"; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; postgresUser = config.systemd.services.postgresql.serviceConfig.User; # postgres postgresPort = config.services.postgresql.settings.port; # 5432 diff --git a/modules/nixos/server/matrix.nix b/modules/nixos/server/matrix.nix index 24f4530..ba18600 100644 --- a/modules/nixos/server/matrix.nix +++ b/modules/nixos/server/matrix.nix @@ -6,7 +6,7 @@ let serviceName = "matrix"; serviceDomain = config.repo.secrets.common.services.domains.matrix; serviceUser = "matrix-synapse"; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; federationPort = 8448; whatsappPort = 29318; diff --git a/modules/nixos/server/monitoring.nix b/modules/nixos/server/monitoring.nix index 758e63d..d1ee714 100644 --- a/modules/nixos/server/monitoring.nix +++ b/modules/nixos/server/monitoring.nix @@ -5,7 +5,7 @@ let serviceGroup = serviceUser; serviceName = "grafana"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; prometheusPort = 9090; prometheusUser = "prometheus"; diff --git a/modules/nixos/server/navidrome.nix b/modules/nixos/server/navidrome.nix index 30cb8da..34b245a 100644 --- a/modules/nixos/server/navidrome.nix +++ b/modules/nixos/server/navidrome.nix @@ -5,7 +5,7 @@ let serviceUser = "navidrome"; serviceGroup = serviceUser; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/network.nix b/modules/nixos/server/network.nix new file mode 100644 index 0000000..90b8c0e --- /dev/null +++ b/modules/nixos/server/network.nix @@ -0,0 +1,26 @@ +{ lib, config, ... }: +{ + options.swarselmodules.server.network = lib.mkEnableOption "enable server network config"; + config = lib.mkIf config.swarselmodules.server.network { + + globals.networks.home.hosts.${config.node.name} = { + inherit (config.repo.secrets.local.networking.networks.home) id; + mac = config.repo.secrets.local.networking.networks.home.mac or null; + }; + + globals.hosts.${config.node.name} = { + inherit (config.repo.secrets.local.networking) defaultGateway4; + }; + + networking = { + inherit (config.repo.secrets.local.networking) hostId; + hostName = config.node.name; + nftables.enable = lib.mkDefault true; + enableIPv6 = lib.mkDefault true; + firewall = { + enable = lib.mkDefault true; + }; + }; + + }; +} diff --git a/modules/nixos/server/nextcloud.nix b/modules/nixos/server/nextcloud.nix index 36765d2..50e8b9f 100644 --- a/modules/nixos/server/nextcloud.nix +++ b/modules/nixos/server/nextcloud.nix @@ -8,7 +8,7 @@ let serviceGroup = serviceUser; serviceName = "nextcloud"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; diff --git a/modules/nixos/server/paperless.nix b/modules/nixos/server/paperless.nix index ca813b1..005bdab 100644 --- a/modules/nixos/server/paperless.nix +++ b/modules/nixos/server/paperless.nix @@ -7,7 +7,7 @@ let serviceGroup = serviceUser; serviceName = "paperless"; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; tikaPort = 9998; gotenbergPort = 3002; diff --git a/modules/nixos/server/radicale.nix b/modules/nixos/server/radicale.nix index 7ad9fe2..411a3e6 100644 --- a/modules/nixos/server/radicale.nix +++ b/modules/nixos/server/radicale.nix @@ -7,7 +7,7 @@ let serviceUser = "radicale"; serviceGroup = serviceUser; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; cfg = config.services.${serviceName}; in diff --git a/modules/nixos/server/snipe-it.nix b/modules/nixos/server/snipe-it.nix index b7a9edd..3ae183e 100644 --- a/modules/nixos/server/snipe-it.nix +++ b/modules/nixos/server/snipe-it.nix @@ -9,7 +9,7 @@ let serviceUser = "snipeit"; serviceGroup = serviceUser; serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; mysqlPort = 3306; in diff --git a/modules/nixos/server/syncthing.nix b/modules/nixos/server/syncthing.nix index 6d1ac78..6eb61c6 100644 --- a/modules/nixos/server/syncthing.nix +++ b/modules/nixos/server/syncthing.nix @@ -7,7 +7,7 @@ let serviceUser = "syncthing"; serviceGroup = serviceUser; serviceName = "syncthing"; - serviceAddress = globals.hosts.winters.ipv4; + serviceAddress = globals.networks.home.hosts.${config.node.name}.ipv4; specificServiceName = "syncthing-${configName}"; cfg = config.services.${serviceName}; diff --git a/nix/lib.nix b/nix/lib.nix index a7b6194..c41db61 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -6,6 +6,22 @@ let inherit (inputs.nixpkgs) lib; in rec { + cidrToSubnetMask = cidr: + let + prefixLength = lib.toInt (lib.last (lib.splitString "/" cidr)); + bits = lib.genList (i: if i < prefixLength then 1 else 0) 32; + octets = lib.genList + (i: + let + octetBits = lib.sublist (i * 8) 8 bits; + octetValue = lib.foldl (acc: bit: acc * 2 + bit) 0 octetBits; + in + octetValue + ) 4; + subnetMask = lib.concatStringsSep "." (map toString octets); + in + subnetMask; + mkIfElseList = p: yes: no: lib.mkMerge [ (lib.mkIf p yes) (lib.mkIf (!p) no) diff --git a/profiles/nixos/localserver/default.nix b/profiles/nixos/localserver/default.nix index c73b619..928e012 100644 --- a/profiles/nixos/localserver/default.nix +++ b/profiles/nixos/localserver/default.nix @@ -15,6 +15,8 @@ boot = lib.mkDefault true; server = { general = lib.mkDefault true; + network = lib.mkDefault true; + diskEncryption = lib.mkDefault true; packages = lib.mkDefault true; ssh = lib.mkDefault true; nginx = lib.mkDefault true; diff --git a/profiles/nixos/minimal/default.nix b/profiles/nixos/minimal/default.nix index a224336..c233faa 100644 --- a/profiles/nixos/minimal/default.nix +++ b/profiles/nixos/minimal/default.nix @@ -21,6 +21,7 @@ server = { ssh = lib.mkDefault true; + diskEncryption = lib.mkDefault true; }; }; diff --git a/secrets/repo/globals.nix.enc b/secrets/repo/globals.nix.enc index 64a35db..d6641b4 100644 --- a/secrets/repo/globals.nix.enc +++ b/secrets/repo/globals.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:8qexHpKJg6o1Fb9H50I3H25UOpNFs2sQl2hd3B2hdJRTjc96aVgTgI838Fnn7G6mFBpHqP0SFCU0/CP6SKqbhJ6SucrfpQN/RqZlSCxmuZi3sqv3voNd7/5JzY0D/5XUTfzHkeEA34HS0GcNLLY7m+QskfJdqGSMB5P++88xCNETqv+sRPVegm1ZGttj+tttesLkAcIU0556WiQhyIcpR4ZiO75NWRFerOmb4LxADR+bwBfesfGUfjflsqOSJll17N9SECSWE7o75Ojn+yde/EznK+zQlsCYvPp90d2xU6dpdRNtp9jrjvXvEVCmcwjIqIKXqurc2CU=,iv:xBYgbmjHwhbH+7WR5MLVysrChxr6rERo6WZuu07sUS0=,tag:vMoMu9mrrGRTA3oO2wsnWw==,type:str]", + "data": "ENC[AES256_GCM,data:1nK/JO8sa+N6EXpyIHBnRapOXYbtM38jnNCf/j0wIOG+0uJvQEFc1e9gIFvuvmPUpUjh6XMuEKNxvLTjFlaLiypOX3yJVTn2fiyOWSm244wcye0GRPe+RWIi+1kEPrFDBEG2JFB+9iGSx0Vf2NfBPgaVFnr4Z2TTGH/kvxiTV6KYucWQNHh+jvVKZ6vAsCP2pFWp2yhpov9l5Tj6MwyK7E46Gn7DmCAtlZcA64Nht+99Zrrfuq8byan6w8RMFR830GJvdMAAD/Vsz/6aGQfHhpJwl4L8/4WwvhQq/DuU1umI1Q7r7FosXbos6g8wTWuM3ccD7V//tFDeVkaMKJzkLkQt0JbyzansijadTYjo0I1w15iH2nySBSIrsOJauBcw3XaP6NfAC3fN1lh/fDaj5HWud5v2ginWRfJNYalfMvTkXm2E5m8SXjanGJL1bHBle4TwEDNPT8+LFIJm8gf57rQRcRlh,iv:W3xvnTblM4Aa0dzDKiWqHM6B5zmu5ddk3D4tYAVNBiY=,tag:KelbYP9xbTmDaWiPrkS+Mw==,type:str]", "sops": { "age": [ { @@ -27,8 +27,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBibGlMSU4vUEF5UlNVZzlr\nMTMyOFY2Zi8rZFdZT1JrelZEUUZkZHFvOFdzCjVPbVovaU9nZklJQWNZeDJZNm0r\nMXBIK2hsZEY0NElxTVVMWmN6WU1Ld28KLS0tIENaallkK05SMllia3prV25hZDR2\nZDBNU0dYYnJESG1JZGpvSGp1WW9UMVEKJgfdLp7BRXvyAekecNJiaBXmxSj1qNxx\nZeHceqEkfWV/PzX+RP4LHjXTQCLEOJijbKxDmxSsYq49hC9xjZASuw==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-07-22T17:19:04Z", - "mac": "ENC[AES256_GCM,data:r1h9ouXb8o8Vk3/l3SX6hxbPApMn4BcCIs52Jhv9s9RYURMGb9qqPipbX7yFIYDBMka2qJJ0BneJz2EI60nTxx+QqATImR2oot2U6iONrelgs+AL3We//xpHOVHSxQ9XMmeEOcVqXEU3u843jV1RElxarRCwB9yM6IWTPx2qNzA=,iv:bS571Ddgz6Fbhyxy2bL/087ZTD7egcvPoLXD9uF8aN0=,tag:HJBI6G6ivRHhJMXYrNhIKw==,type:str]", + "lastmodified": "2025-11-09T22:41:57Z", + "mac": "ENC[AES256_GCM,data:iHmgHvT3yn5ayimvO+miRA3dA/0o4juBvBzWIXwtZyt5gSI4oJizMbRaX5coVJgeDdPsYaiQFqSnEPrPmrMIR16jdmscQLvz7X1gtdanMP++5q13jWOkiUHPC2nZy47M+36bzC2P/BHqKE782ERTGnD70VZO4a1lOa7pB32NutY=,iv:oOn9x/xf5g82GXdZ9fDxgEiUScXXfzSdEZccqFQLF4w=,tag:iEhx2Hm0yP6G/1w6cIgHIg==,type:str]", "pgp": [ { "created_at": "2025-07-02T12:10:18Z", @@ -37,6 +37,6 @@ } ], "unencrypted_suffix": "_unencrypted", - "version": "3.10.2" + "version": "3.11.0" } }