From 69619e0bcd198291d2e4f7a72ccd87d61a8d0bc9 Mon Sep 17 00:00:00 2001 From: Scott Worley Date: Mon, 25 Oct 2021 00:44:56 -0700 Subject: [PATCH 1/1] Use a squashfs Nix Store rather than a virtio host mount --- README | 4 +++ checks/mount-grep.nix | 17 ++++++++++++ flake.lock | 25 +++++++++++++++++ flake.nix | 19 +++++++++++++ modules/qemu-vm-isolation.nix | 52 +++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 README create mode 100644 checks/mount-grep.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 modules/qemu-vm-isolation.nix diff --git a/README b/README new file mode 100644 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 index 0000000..f8fed0e --- /dev/null +++ b/checks/mount-grep.nix @@ -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 index 0000000..01cb232 --- /dev/null +++ b/flake.lock @@ -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 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 index 0000000..7e0a37f --- /dev/null +++ b/modules/qemu-vm-isolation.nix @@ -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"; + }; + }]; + + }; +} -- 2.44.1