diff --git a/flake.lock b/flake.lock index 4c9d906..5b6d7de 100644 --- a/flake.lock +++ b/flake.lock @@ -198,6 +198,22 @@ "type": "github" } }, + "nixos-hardware": { + "locked": { + "lastModified": 1708594753, + "narHash": "sha256-c/gH7iXS/IYH9NrFOT+aJqTq+iEBkvAkpWuUHGU3+f0=", + "owner": "NixOS", + "repo": "nixos-hardware", + "rev": "3f7d0bca003eac1a1a7f4659bbab9c8f8c2a0958", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixos-hardware", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1702272962, @@ -253,6 +269,7 @@ "disko": "disko", "dns": "dns", "microvm": "microvm", + "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs_2", "nixpkgs-unstable": "nixpkgs-unstable" } diff --git a/flake.nix b/flake.nix index 635a074..1989ac2 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,7 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; deploy-rs.url = "github:serokell/deploy-rs"; + nixos-hardware.url = "github:NixOS/nixos-hardware/master"; disko = { url = "github:nix-community/disko"; @@ -28,12 +29,12 @@ }; outputs = - { self, nixpkgs, deploy-rs, disko, agenix, nixpkgs-unstable, dns, microvm, ... }: + { self, nixpkgs, deploy-rs, disko, agenix, nixpkgs-unstable, dns, microvm, nixos-hardware, ... }: let - system = "x86_64-linux"; - pkgs = nixpkgs.legacyPackages.${system}; + controllerArch = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${controllerArch}; lib = pkgs.lib; - pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; + pkgs-unstable = nixpkgs-unstable.legacyPackages.${controllerArch}; machines = import ./nixos/machines; physicalMachines = lib.filterAttrs (n: v: v.type == "physical") machines; mkNixosSystems = systemDef: @@ -48,7 +49,7 @@ physicalMachines; in { - devShells.${system}.default = pkgs.mkShell { + devShells.${controllerArch}.default = pkgs.mkShell { packages = with pkgs; [ libsecret # TODO: using nixos-anywhere from nixos-unstable produces buffer overflow. @@ -65,11 +66,12 @@ ]; }; - formatter.${system} = pkgs.nixfmt; + formatter.${controllerArch} = pkgs.nixfmt; nixosConfigurations = mkNixosSystems (name: machine: { - inherit system; - specialArgs = { inherit machines machine dns microvm disko agenix; }; + system = machine.arch; + + specialArgs = { inherit machines machine dns microvm disko agenix nixos-hardware; }; modules = [ ./nixos { networking.hostName = name; } @@ -83,14 +85,25 @@ nodes = mkDeployNodes (name: machine: { hostname = self.nixosConfigurations.${name}.config.networking.fqdn; profiles.system = { - path = deploy-rs.lib.${system}.activate.nixos + remoteBuild = machine.arch != controllerArch; + path = deploy-rs.lib."${machine.arch}".activate.nixos self.nixosConfigurations.${name}; }; }); }; + # Deploy-rs' flake checks seem broken for architectures different from the deployment machine. + # We skip these here. checks = builtins.mapAttrs - (system: deployLib: deployLib.deployChecks self.deploy) + (system: deployLib: + deployLib.deployChecks (self.deploy // { + nodes = (lib.attrsets.filterAttrs + (name: node: + machines.${name}.arch == controllerArch + ) + self.deploy.nodes); + }) + ) deploy-rs.lib; }; } diff --git a/nixos/machines/default.nix b/nixos/machines/default.nix index a520f90..0642755 100644 --- a/nixos/machines/default.nix +++ b/nixos/machines/default.nix @@ -1,6 +1,26 @@ +# TODO: Create a nixos module system for this. (mkMerge) +# That way, we don't have to specify isRaspberryPi on every machine... etc. { + warwick = { + type = "physical"; + arch = "aarch64-linux"; + isRaspberryPi = true; + isHypervisor = false; + isVirtualMachine = false; + + nixosModule.lab = { + storage = { + osDisk = "/dev/sda"; + }; + }; + }; + atlas = { type = "physical"; + arch = "x86_64-linux"; + isRaspberryPi = false; + isHypervisor = true; + isVirtualMachine = false; nixosModule.lab = { storage = { @@ -18,25 +38,31 @@ jefke = { type = "physical"; + arch = "x86_64-linux"; + isRaspberryPi = false; + isHypervisor = true; + isVirtualMachine = false; - nixosModule = { - lab = { - storage = { - osDisk = "/dev/sda"; - dataPartition = "/dev/nvme0n1p1"; - }; + nixosModule.lab = { + storage = { + osDisk = "/dev/sda"; + dataPartition = "/dev/nvme0n1p1"; + }; - ssh = { - useCertificates = true; - hostCert = builtins.readFile ./certificates/jefke/host_ed25519.crt; - userCert = builtins.readFile ./certificates/jefke/user_ed25519.crt; - }; + ssh = { + useCertificates = true; + hostCert = builtins.readFile ./certificates/jefke/host_ed25519.crt; + userCert = builtins.readFile ./certificates/jefke/user_ed25519.crt; }; }; }; lewis = { type = "physical"; + arch = "x86_64-linux"; + isRaspberryPi = false; + isHypervisor = true; + isVirtualMachine = false; nixosModule.lab = { backups.enable = true; @@ -59,18 +85,23 @@ hermes = { type = "virtual"; hypervisorName = "lewis"; + isRaspberryPi = false; + isVirtualMachine = true; + isHypervisor = false; nixosModule = { config, ... }: { lab = { - networking.dmz.services.enable = true; + networking = { + dmz.services.enable = true; + staticNetworking = true; + staticIPv4 = config.lab.networking.dmz.ipv4.services; + staticIPv6 = config.lab.networking.dmz.ipv6.services; + }; vm = { # TODO: would be cool to create a check that a mac address is only ever assigned to one VM. # TODO: idea: what if we generated these IDs by hashing the host name and reducing that to the amount of hosts possible? id = 7; - staticNetworking = true; - staticIPv4 = config.lab.networking.dmz.ipv4.services; - staticIPv6 = config.lab.networking.dmz.ipv6.services; shares = [{ name = "dnsmasq"; @@ -84,6 +115,9 @@ maestro = { type = "virtual"; hypervisorName = "atlas"; + isRaspberryPi = false; + isVirtualMachine = false; + isHypervisor = false; nixosModule = { config, ... }: { microvm.balloonMem = 7680; @@ -101,6 +135,9 @@ bancomart = { type = "virtual"; hypervisorName = "jefke"; + isRaspberryPi = false; + isVirtualMachine = false; + isHypervisor = false; nixosModule = { microvm.balloonMem = 7680; @@ -115,6 +152,9 @@ vpay = { type = "virtual"; hypervisorName = "lewis"; + isRaspberryPi = false; + isVirtualMachine = false; + isHypervisor = false; nixosModule = { microvm.balloonMem = 5120; diff --git a/nixos/modules/networking/default.nix b/nixos/modules/networking/default.nix index 16416f6..40f6404 100644 --- a/nixos/modules/networking/default.nix +++ b/nixos/modules/networking/default.nix @@ -22,11 +22,26 @@ in { }; }; - mainNicNamePattern = lib.mkOption { - default = "en*"; + staticNetworking = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + Whether this machine has static networking configuration applied. + Routing is prepopulated, but IP addresses have to be set. + ''; + }; + + staticIPv4 = lib.mkOption { type = lib.types.str; description = '' - Pattern to match the name of this machine's main NIC. + Static IPv4 address for the machine. + ''; + }; + + staticIPv6 = lib.mkOption { + type = lib.types.str; + description = '' + Static IPv6 address for the machine. ''; }; }; @@ -43,10 +58,10 @@ in { }; }; - systemd.network = lib.mkIf (machine.type == "physical") { + systemd.network = { enable = true; - netdevs = { + netdevs = lib.mkIf machine.isHypervisor { "20-vlandmz" = { vlanConfig.Id = 30; @@ -64,43 +79,89 @@ in { }; }; - networks = { - "30-main-nic" = { - matchConfig.Name = cfg.mainNicNamePattern; - vlan = [ "vlandmz" ]; + networks = lib.attrsets.mergeAttrsList [ + (lib.optionalAttrs machine.isHypervisor { + "30-main-nic" = { + matchConfig.Name = "en*"; + vlan = [ "vlandmz" ]; - networkConfig = { - DHCP = "yes"; + networkConfig = { + DHCP = "yes"; + }; }; - }; - "40-vlandmz" = { - matchConfig.Name = "vlandmz"; - linkConfig.RequiredForOnline = "enslaved"; + "40-vlandmz" = { + matchConfig.Name = "vlandmz"; + linkConfig.RequiredForOnline = "enslaved"; - networkConfig = { - IPv6AcceptRA = false; - LinkLocalAddressing = "no"; - Bridge = cfg.dmz.bridgeName; + networkConfig = { + IPv6AcceptRA = false; + LinkLocalAddressing = "no"; + Bridge = cfg.dmz.bridgeName; + }; }; - }; - "40-bridgedmz" = { - matchConfig.Name = cfg.dmz.bridgeName; - linkConfig.RequiredForOnline = "carrier"; + "40-bridgedmz" = { + matchConfig.Name = cfg.dmz.bridgeName; + linkConfig.RequiredForOnline = "carrier"; - networkConfig = { - IPv6AcceptRA = cfg.dmz.allowConnectivity; - LinkLocalAddressing = if cfg.dmz.allowConnectivity then "ipv6" else "no"; - DHCP = "yes"; + networkConfig = { + IPv6AcceptRA = cfg.dmz.allowConnectivity; + LinkLocalAddressing = if cfg.dmz.allowConnectivity then "ipv6" else "no"; + DHCP = "yes"; + }; }; - }; - "40-vms" = { - matchConfig.Name = "vm-*"; - networkConfig.Bridge = cfg.dmz.bridgeName; - }; - }; + "40-vms" = { + matchConfig.Name = "vm-*"; + networkConfig.Bridge = cfg.dmz.bridgeName; + }; + }) + (lib.optionalAttrs machine.isVirtualMachine { + "30-main-nic" = { + matchConfig.Name = "en*"; + + networkConfig = { + IPv6AcceptRA = ! cfg.staticNetworking; + DHCP = lib.mkIf (! cfg.staticNetworking) "yes"; + + Address = lib.mkIf cfg.staticNetworking [ + "${cfg.staticIPv4}/${cfg.dmz.ipv4.prefixLength}" + "${cfg.staticIPv6}/${cfg.dmz.ipv6.prefixLength}" + ]; + + DNS = lib.mkIf cfg.staticNetworking [ + cfg.dmz.ipv4.router + cfg.dmz.ipv6.router + ]; + }; + + routes = lib.mkIf cfg.staticNetworking [ + { + routeConfig = { + Gateway = cfg.dmz.ipv4.router; + Destination = "0.0.0.0/0"; + }; + } + { + routeConfig = { + Gateway = cfg.dmz.ipv6.router; + Destination = "::/0"; + }; + } + ]; + }; + }) + (lib.optionalAttrs machine.isRaspberryPi { + "30-main-nic" = { + matchConfig.Name = "end*"; + networkConfig = { + IPv6AcceptRA = true; + DHCP = "yes"; + }; + }; + }) + ]; }; }; } diff --git a/nixos/modules/storage.nix b/nixos/modules/storage.nix index 3ad1b78..94e3365 100644 --- a/nixos/modules/storage.nix +++ b/nixos/modules/storage.nix @@ -10,7 +10,8 @@ in { }; dataPartition = lib.mkOption { - type = lib.types.str; + default = null; + type = lib.types.nullOr lib.types.str; description = '' Partition to be used for data storage on this machine. ''; @@ -25,38 +26,51 @@ in { }; }; - config = lib.mkIf (machine.type == "physical") { - fileSystems.${cfg.dataMountPoint}.device = cfg.dataPartition; + config = { + fileSystems = lib.attrsets.mergeAttrsList [ + (lib.optionalAttrs machine.isHypervisor { + "${cfg.dataMountPoint}".device = cfg.dataPartition; + }) + (lib.optionalAttrs machine.isRaspberryPi { + "/" = { + device = "/dev/disk/by-label/NIXOS_SD"; + fsType = "ext4"; + options = [ "noatime" ]; + }; + }) + ]; # TODO: Rename this to 'osDisk'. Unfortunately, we would need to run nixos-anywhere again then. - disko.devices.disk.vdb = { - device = cfg.osDisk; - type = "disk"; + disko = lib.mkIf machine.isHypervisor { + devices.disk.vdb = { + device = cfg.osDisk; + type = "disk"; - content = { - type = "gpt"; + content = { + type = "gpt"; - partitions = { - swap.size = "100%"; + partitions = { + swap.size = "100%"; - ESP = { - type = "EF00"; - size = "500M"; + ESP = { + type = "EF00"; + size = "500M"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; }; - }; - root = { - end = "-4G"; + root = { + end = "-4G"; - content = { - type = "filesystem"; - format = "btrfs"; - mountpoint = "/"; + content = { + type = "filesystem"; + format = "btrfs"; + mountpoint = "/"; + }; }; }; }; diff --git a/nixos/physical.nix b/nixos/physical.nix index 1bf9b02..9b6e0af 100644 --- a/nixos/physical.nix +++ b/nixos/physical.nix @@ -1,11 +1,11 @@ -{ pkgs, config, lib, modulesPath, microvm, disko, agenix, machines, dns, ... }: { +{ pkgs, config, lib, microvm, disko, agenix, machine, machines, dns, nixos-hardware, ... }: { imports = [ - (modulesPath + "/installer/scan/not-detected.nix") microvm.nixosModules.host - ]; + ] + ++ lib.lists.optional (machine.isRaspberryPi) nixos-hardware.nixosModules.raspberry-pi-4; config = { - boot = { + boot = lib.mkIf (machine.isHypervisor) { kernelModules = [ "kvm-intel" ]; extraModulePackages = [ ]; @@ -30,10 +30,11 @@ nixpkgs = { config.allowUnfree = true; - hostPlatform = "x86_64-linux"; + # TODO: do we need this? + # hostPlatform = machine.arch; }; - hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware; + hardware.cpu.intel.updateMicrocode = lib.mkIf (machine.isHypervisor) config.hardware.enableRedistributableFirmware; age.identityPaths = [ "/etc/age_ed25519" ]; diff --git a/nixos/virtual/default.nix b/nixos/virtual/default.nix index f3bf85b..089fe41 100644 --- a/nixos/virtual/default.nix +++ b/nixos/virtual/default.nix @@ -2,6 +2,7 @@ imports = [ ./docker_swarm.nix ]; options.lab.vm = { + # TODO: make global. baseMACAddress = lib.mkOption { default = "BA:DB:EE:F0:00:00"; type = lib.types.str; @@ -17,29 +18,6 @@ ''; }; - staticNetworking = lib.mkOption { - default = false; - type = lib.types.bool; - description = '' - Whether this VM has static networking configuration applied. - Routing is prepopulated, but IP addresses have to be set. - ''; - }; - - staticIPv4 = lib.mkOption { - type = lib.types.str; - description = '' - Static IPv4 address for the VM. - ''; - }; - - staticIPv6 = lib.mkOption { - type = lib.types.str; - description = '' - Static IPv6 address for the VM. - ''; - }; - shares = lib.mkOption { default = [ ]; description = '' @@ -73,16 +51,20 @@ mountPoint = "/etc/ssh/host_keys"; }]; - services.openssh = { - hostKeys = [{ - path = "/etc/ssh/host_keys/ssh_host_ed25519_key"; - type = "ed25519"; - }]; + services.openssh = + let + hostKeyPath = "/etc/ssh/host_keys/ssh_host_ed25519_key"; + in + { + hostKeys = [{ + path = hostKeyPath; + type = "ed25519"; + }]; - extraConfig = '' - HostKey /etc/ssh/host_keys/ssh_host_ed25519_key - ''; - }; + extraConfig = '' + HostKey ${hostKeyPath} + ''; + }; microvm = { # TODO: make this dependent on the host CPU @@ -108,51 +90,5 @@ mac = pkgs.lib.net.mac.add config.lab.vm.id config.lab.vm.baseMACAddress; }]; }; - - networking.useDHCP = false; - - systemd.network = - let - cfg = config.lab.networking; - in - { - enable = true; - - networks = { - "30-main-nic" = { - matchConfig.Name = "en*"; - - networkConfig = { - IPv6AcceptRA = ! config.lab.vm.staticNetworking; - DHCP = lib.mkIf (! config.lab.vm.staticNetworking) "yes"; - - Address = lib.mkIf config.lab.vm.staticNetworking [ - "${ config.lab.vm.staticIPv4}/${cfg.dmz.ipv4.prefixLength}" - "${config.lab.vm.staticIPv6}/${cfg.dmz.ipv6.prefixLength}" - ]; - - DNS = lib.mkIf config.lab.vm.staticNetworking [ - cfg.dmz.ipv4.router - cfg.dmz.ipv6.router - ]; - }; - - routes = lib.mkIf config.lab.vm.staticNetworking [ - { - routeConfig = { - Gateway = cfg.dmz.ipv4.router; - Destination = "0.0.0.0/0"; - }; - } - { - routeConfig = { - Gateway = cfg.dmz.ipv6.router; - Destination = "::/0"; - }; - } - ]; - }; - }; - }; }; }