]> git.scottworley.com Git - nixos-qemu-vm-isolation/commitdiff
Use a squashfs Nix Store rather than a virtio host mount
authorScott Worley <scottworley@scottworley.com>
Mon, 25 Oct 2021 07:44:56 +0000 (00:44 -0700)
committerScott Worley <scottworley@scottworley.com>
Mon, 25 Oct 2021 20:23:10 +0000 (13:23 -0700)
README [new file with mode: 0644]
checks/mount-grep.nix [new file with mode: 0644]
flake.lock [new file with mode: 0644]
flake.nix [new file with mode: 0644]
modules/qemu-vm-isolation.nix [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..f0be89d
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+Isolate NixOS QEMU VMs from each other and from the host by using a
+squashfs for the VM's /nix/store that contains only the VM's dependencies
+(like the installer has) rather than a virtio mount of the host's entire
+/nix/store.
diff --git a/checks/mount-grep.nix b/checks/mount-grep.nix
new file mode 100644 (file)
index 0000000..f8fed0e
--- /dev/null
@@ -0,0 +1,17 @@
+{ pkgs, ... }: {
+  name = "qemu-private-store-mount-grep";
+
+  nodes = {
+    shared = _: { };
+    private = _: { imports = [ ../modules/qemu-vm-isolation.nix ]; };
+  };
+
+  testScript = ''
+    start_all()
+    shared.wait_for_unit("multi-user.target")
+    private.wait_for_unit("multi-user.target")
+
+    shared.succeed("[[ $(mount | grep -c virt) -gt 0 ]]")
+    private.succeed("[[ $(mount | grep -c virt) -eq 0 ]]")
+  '';
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644 (file)
index 0000000..01cb232
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1635141467,
+        "narHash": "sha256-H+TVE6tBSm4nAepm7HRfW7AcrndI5e4+TJwCQo4/z+s=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "38d21595b8fb0a744aa31c5794013bf42cf98fa9",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644 (file)
index 0000000..355702e
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,19 @@
+{
+  outputs = { self, nixpkgs, }:
+    let
+      inherit (nixpkgs.lib) genAttrs;
+
+      systems = nixpkgs.lib.systems.supported.tier1;
+
+      forAllSystems = genAttrs systems;
+
+    in {
+      nixosModules = {
+        qemu-vm-isolation = import ./modules/qemu-vm-isolation.nix;
+      };
+      checks = forAllSystems (system: {
+        mount-grep = nixpkgs.legacyPackages."${system}".nixosTest
+          (import ./checks/mount-grep.nix);
+      });
+    };
+}
diff --git a/modules/qemu-vm-isolation.nix b/modules/qemu-vm-isolation.nix
new file mode 100644 (file)
index 0000000..7e0a37f
--- /dev/null
@@ -0,0 +1,52 @@
+{ config, lib, modulesPath, pkgs, ... }:
+let
+  inherit (lib) findSingle mkForce mkVMOverride;
+
+  lookupDriveDeviceName = driveName: driveList:
+    (findSingle (drive: drive.name == driveName)
+      (throw "Drive ${driveName} not found")
+      (throw "Multiple drives named ${driveName}") driveList).device;
+
+  storeMountPath = if config.virtualisation.writableStore then
+    "/nix/.ro-store"
+  else
+    "/nix/store";
+
+in {
+
+  boot.initrd.availableKernelModules = [ "squashfs" ];
+
+  fileSystems = mkVMOverride {
+    "${storeMountPath}" = {
+      device =
+        lookupDriveDeviceName "nixstore" config.virtualisation.qemu.drives;
+      fsType = "squashfs";
+      options = [ "ro" ];
+      neededForBoot = true;
+    };
+  };
+
+  system.build.squashfsStore =
+    pkgs.callPackage (modulesPath + "/../lib/make-squashfs.nix") {
+      storeContents = config.virtualisation.pathsInNixDB;
+    };
+
+  virtualisation = {
+
+    # This should be the default.
+    bootDevice = lookupDriveDeviceName "root" config.virtualisation.qemu.drives;
+
+    sharedDirectories = mkForce { };
+
+    qemu.drives = [{
+      name = "nixstore";
+      file = "${config.system.build.squashfsStore}";
+      driveExtraOpts = {
+        format = "raw";
+        read-only = "on";
+        werror = "report";
+      };
+    }];
+
+  };
+}