diff --git a/flake.lock b/flake.lock index a1119d7..59aeb03 100644 --- a/flake.lock +++ b/flake.lock @@ -393,6 +393,27 @@ "url": "https://cdn.jsdelivr.net/npm/mermaid@11.12.3/dist/mermaid.min.js" } }, + "microvm": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "spectrum": "spectrum" + }, + "locked": { + "lastModified": 1782764610, + "narHash": "sha256-CzCF8RD9a9kwRrpeQ+z67sqqADUG0tf+4+YBSUG30y0=", + "owner": "microvm-nix", + "repo": "microvm.nix", + "rev": "3904edd282a7166cdad6c597964718e6d018baa6", + "type": "github" + }, + "original": { + "owner": "microvm-nix", + "repo": "microvm.nix", + "type": "github" + } + }, "niri": { "inputs": { "niri-stable": "niri-stable", @@ -728,6 +749,7 @@ "hjem": "hjem", "impermanence": "impermanence", "import-tree": "import-tree", + "microvm": "microvm", "niri": "niri", "nix-darwin": "nix-darwin", "nix-on-droid": "nix-on-droid", @@ -773,6 +795,22 @@ "type": "github" } }, + "spectrum": { + "flake": false, + "locked": { + "lastModified": 1782480951, + "narHash": "sha256-jrfyAgv5XfYDXJJ28OzANQOQIlsfNaI1kM9Mt13n+5k=", + "ref": "refs/heads/main", + "rev": "e4562d4c7646fea3cbb7306ff9b7ff41154c75c3", + "revCount": 1400, + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + }, + "original": { + "type": "git", + "url": "https://spectrum-os.org/git/spectrum" + } + }, "sqlite-lib-src": { "flake": false, "locked": { diff --git a/flake.nix b/flake.nix index 42c37bf..878c5b4 100644 --- a/flake.nix +++ b/flake.nix @@ -18,6 +18,8 @@ nix-on-droid.url = "github:nix-community/nix-on-droid"; nix-on-droid.inputs.nixpkgs.follows = "nixpkgs"; nix-on-droid.inputs.home-manager.follows = ""; + microvm.url = "github:microvm-nix/microvm.nix"; + microvm.inputs.nixpkgs.follows = "nixpkgs"; flake-parts.url = "github:hercules-ci/flake-parts"; import-tree.url = "github:vic/import-tree"; diff --git a/modules/nixos/microvm/guest.nix b/modules/nixos/microvm/guest.nix new file mode 100644 index 0000000..db43765 --- /dev/null +++ b/modules/nixos/microvm/guest.nix @@ -0,0 +1,49 @@ +{ + flake.modules.nixos.microvm-guest = + { pkgs, config, ... }: + let + inherit (builtins) + substring + hashString + concatStringsSep + genList + fromTOML + ; + generated = + let + minCid = 3; + hash = hashString "sha256" config.networking.fqdn; + getByte = i: substring (i * 2) 2 hash; + getMacOctet = i: if i == 0 then "${substring 0 1 hash}2" else getByte i; + in + { + macAddress = genList getMacOctet 6 |> concatStringsSep ":"; + cid = minCid + (fromTOML "rand = 0x${getByte 0}").rand; + }; + in + { + microvm = { + vsock.cid = generated.cid; + vsock.ssh.enable = true; + registerWithMachined = true; + interfaces = [ + { + type = "tap"; + id = substring 0 15 "vm-${config.networking.hostName}"; + mac = generated.macAddress; + } + ]; + shares = [ + { + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + tag = "ro-store"; + proto = "virtiofs"; + } + ]; + }; + + nix.optimise.automatic = false; + security.sudo.wheelNeedsPassword = false; + }; +} diff --git a/modules/nixos/microvm/host.nix b/modules/nixos/microvm/host.nix new file mode 100644 index 0000000..2de0116 --- /dev/null +++ b/modules/nixos/microvm/host.nix @@ -0,0 +1,35 @@ +{ inputs, lib, ... }: +{ + flake.modules.nixos.core = + { config, ... }: + let + inherit (config) vms; + enable = vms != { }; + in + { + imports = [ inputs.microvm.nixosModules.host ]; + + options.vms = lib.mkOption { + type = lib.types.attrsOf lib.types.deferredModule; + default = { }; + }; + + config.environment.persistence.nixos = lib.mkIf enable { directories = [ "/var/lib/microvms" ]; }; + + config.microvm = { + host.enable = lib.mkDefault enable; + host.useNotifySockets = true; + vms = builtins.mapAttrs (name: module: { + config.imports = [ + module + inputs.self.modules.nixos.base + inputs.self.modules.nixos.microvm-guest + { + networking.hostName = name; + networking.domain = config.networking.fqdn; + } + ]; + }) vms; + }; + }; +} diff --git a/modules/nixos/microvm/networking.nix b/modules/nixos/microvm/networking.nix new file mode 100644 index 0000000..ee66508 --- /dev/null +++ b/modules/nixos/microvm/networking.nix @@ -0,0 +1,42 @@ +{ + flake.modules.nixos.core = + { lib, config, ... }: + let + bridge = "microvm"; + in + { + config = lib.mkIf (config.microvm.host.enable) { + networking.bridges.${bridge}.interfaces = [ ]; + + systemd.network.networks."10-${bridge}" = { + matchConfig.Name = bridge; + dns = [ "${config.local.prefix}::1" ]; + domains = [ "~${config.networking.fqdn}" ]; + addresses = [ { Address = "${config.local.prefix}::1/64"; } ]; + ipv6Prefixes = [ { Prefix = "${config.local.prefix}::/64"; } ]; + }; + + systemd.network.networks."11-${bridge}" = { + matchConfig.Name = "vm-*"; + networkConfig.Bridge = bridge; + }; + + services.dnsmasq.enable = true; + services.dnsmasq.resolveLocalQueries = false; + services.dnsmasq.settings = { + bind-dynamic = true; + domain-needed = true; + no-resolv = true; + domain = config.networking.fqdn; + local = "/${config.networking.fqdn}/"; + enable-ra = true; + dhcp-range = "::2,::ff,constructor:${bridge}"; + }; + + networking.firewall.allowedUDPPorts = [ + 53 + 547 + ]; + }; + }; +} diff --git a/modules/nixos/yggdrasil/endpoints.nix b/modules/nixos/yggdrasil/endpoints.nix index c2d480a..feccc58 100644 --- a/modules/nixos/yggdrasil/endpoints.nix +++ b/modules/nixos/yggdrasil/endpoints.nix @@ -15,11 +15,12 @@ in { name, ... }: let # from https://wiki.nixos.org/wiki/Yggdrasil#Virtual-hosts - digest = builtins.hashString "sha256" name; - hextets = builtins.genList (i: builtins.substring (4 * i) 4 digest) 4; + hash = builtins.hashString "sha256" name; + hextets = builtins.genList (i: builtins.substring (i * 4) 4 hash) 4; + inherit (builtins.fromTOML "rand = 0x${builtins.substring 0 8 hash}") rand; minPort = 18000; maxPort = 18999; - inherit (builtins.fromTOML "rand = 0x${builtins.substring 0 8 digest}") rand; + randomPort = minPort + lib.mod rand (maxPort - minPort); in { options = { @@ -37,7 +38,7 @@ in }; port = lib.mkOption { type = lib.types.port; - default = minPort + lib.mod rand (maxPort - minPort); + default = randomPort; }; }; }