{ self , nixpkgs-unstable , nixpkgs , sops-nix , inputs , hosts , ... }: let pkgs = nixpkgs.legacyPackages."x86_64-linux"; in rec { nixosSystem = nixpkgs.lib.makeOverridable nixpkgs.lib.nixosSystem; nixosSystemUnstable = nixpkgs-unstable.lib.makeOverridable nixpkgs-unstable.lib.nixosSystem; baseModules = [ # make flake inputs accessiable in NixOS { _module.args.inputs = inputs; } { imports = [ ({ pkgs, ... }: { nix = { extraOptions = '' experimental-features = nix-command flakes ''; settings = { substituters = [ "https://cache.dynamicdiscord.de" "https://cache.nixos.org/" ]; trusted-public-keys = [ "cache.dynamicdiscord.de:DKueZicqi2NhJJXz9MYgUbiyobMs10fTyHCgAUibRP4=" ]; trusted-users = [ "root" "@wheel" ]; }; }; }) sops-nix.nixosModules.sops ]; } ]; defaultModules = baseModules; makeMicroVM = hostName: ipv4Addr: macAddr: modules: [ { microvm = { hypervisor = "cloud-hypervisor"; mem = 2560; shares = [ { source = "/nix/store"; mountPoint = "/nix/.ro-store"; tag = "store"; proto = "virtiofs"; socket = "store.socket"; } { source = "/var/lib/microvms/${hostName}/etc"; mountPoint = "/etc"; tag = "etc"; proto = "virtiofs"; socket = "etc.socket"; } { source = "/var/lib/microvms/${hostName}/var"; mountPoint = "/var"; tag = "var"; proto = "virtiofs"; socket = "var.socket"; } ]; interfaces = [ { type = "tap"; id = "vm-${hostName}"; mac = "${macAddr}"; } ]; }; systemd.network.enable = true; systemd.network.networks."20-lan" = { matchConfig.Type = "ether"; networkConfig = { Address = [ "${ipv4Addr}/24" ]; Gateway = "10.0.0.1"; DNS = ["1.1.1.1"]; DHCP = "no"; }; }; } ] ++ defaultModules ++ modules; inputsMod = inputs // { malobeo = self; }; vmMicroVMOverwrites = hostname: options: { microvm = rec { mem = pkgs.lib.mkForce 4096; hypervisor = pkgs.lib.mkForce "qemu"; socket = pkgs.lib.mkForce null; #needed for hosts that deploy imperative microvms (for example fanny) writableStoreOverlay = pkgs.lib.mkIf options.writableStore "/nix/.rw-store"; volumes = pkgs.lib.mkIf options.writableStore [ { image = "nix-store-overlay.img"; mountPoint = writableStoreOverlay; size = 2048; } ]; shares = pkgs.lib.mkForce (pkgs.lib.optionals (!options.writableStore) [ { tag = "ro-store"; source = "/nix/store"; mountPoint = "/nix/.ro-store"; } ] ++ pkgs.lib.optionals (options.varPath != "") [ { source = "${options.varPath}"; securityModel = "mapped"; mountPoint = "/var"; tag = "var"; } ]); interfaces = pkgs.lib.mkIf (!options.withNetworking) (pkgs.lib.mkForce [{ type = "user"; id = "eth0"; mac = "02:23:de:ad:be:ef"; }]); #if networking is disabled forward port 80 to still have access to webservices forwardPorts = pkgs.lib.mkIf (!options.withNetworking) (pkgs.lib.mkForce [ { from = "host"; host.port = 8080; guest.port = 80; } ]); }; fileSystems = { "/".fsType = pkgs.lib.mkForce "tmpfs"; # prometheus uses a memory mapped file which doesnt seem supported by 9p shares # therefore we mount a tmpfs inside the datadir "/var/lib/prometheus2/data" = pkgs.lib.mkIf (hostname == "overwatch" && options.varPath != "") (pkgs.lib.mkForce { fsType = pkgs.lib.mkForce "tmpfs"; }); }; boot.isContainer = pkgs.lib.mkForce false; services.timesyncd.enable = false; users.users.root.password = ""; services.getty.helpLine = '' Log in as "root" with an empty password. Use "reboot" to shut qemu down. ''; }; vmDiskoOverwrites = { boot.initrd = { secrets = pkgs.lib.mkForce {}; network.ssh.enable = pkgs.lib.mkForce false; }; malobeo.disks.enable = pkgs.lib.mkForce false; networking.hostId = "a3c3101f"; }; vmSopsOverwrites = host: { sops.defaultSopsFile = pkgs.lib.mkForce ../${host}/dummy.yaml; environment.etc = { devHostKey = { source = ../secrets/devkey_ed25519; mode = "0600"; }; }; services.openssh.hostKeys = [{ path = "/etc/devHostKey"; type = "ed25519"; }]; }; vmNestedMicroVMOverwrites = host: sopsDummy: { services.malobeo.microvm.deployHosts = pkgs.lib.mkForce []; microvm.vms = let # Map the values to each hostname to then generate an Attrset using listToAttrs mapperFunc = name: { inherit name; value = { specialArgs.inputs = inputsMod; specialArgs.self = self; config = { imports = (makeMicroVM "${name}" "${hosts.malobeo.hosts.${name}.network.address}" "${hosts.malobeo.hosts.${name}.network.mac}" [ ../${name}/configuration.nix (vmMicroVMOverwrites name { withNetworking = true; varPath = ""; writableStore = false; }) (if sopsDummy then (vmSopsOverwrites name) else {}) ]); }; }; }; in builtins.listToAttrs (map mapperFunc self.nixosConfigurations.${host}.config.services.malobeo.microvm.deployHosts); }; buildVM = host: networking: sopsDummy: disableDisko: varPath: writableStore: (self.nixosConfigurations.${host}.extendModules { modules = [ (vmMicroVMOverwrites host { withNetworking = networking; varPath = "${varPath}"; writableStore = writableStore; }) (if sopsDummy then (vmSopsOverwrites host) else {}) (if disableDisko then vmDiskoOverwrites else {}) ] ++ pkgs.lib.optionals (hosts.malobeo.hosts.${host}.type != "microvm") [ inputs.microvm.nixosModules.microvm ] ++ pkgs.lib.optionals (self.nixosConfigurations.${host}.config ? services.malobeo.microvm.deployHosts) [ (vmNestedMicroVMOverwrites host sopsDummy) ]; }); buildHost = hosts: (builtins.mapAttrs (host: settings: nixosSystem { system = if (settings.type == "rpi") then "aarch64-linux" else "x86_64-linux"; specialArgs.inputs = inputsMod; specialArgs.self = self; modules = (if (settings.type != "microvm") then defaultModules ++ [ ../${host}/configuration.nix ] else makeMicroVM "${host}" "${settings.network.address}" "${settings.network.mac}" [ inputs.microvm.nixosModules.microvm ../${host}/configuration.nix ]); }) hosts); }