aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-05-17 13:39:56 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-05-17 13:39:56 +0200
commitde11e018dca18d11499debb8102ba6151cc21834 (patch)
tree22c06529d68394cd19ec7792f5e71c6f2a230b4a /modules
parentpkgs/default.nix: Migrate to the package arguments (diff)
downloadnixos-config-de11e018dca18d11499debb8102ba6151cc21834.zip
modules/nixos-shell: Init
A VM at your disposal. This is based on: https://github.com/Mic92/nixos-shell
Diffstat (limited to 'modules')
-rw-r--r--modules/by-name/ni/nixos-shell/module.nix128
-rwxr-xr-xmodules/by-name/ni/nixos-shell/nixos-shell.sh60
-rw-r--r--modules/by-name/ni/nixos-shell/shell_setup.nix191
-rw-r--r--modules/common/default.nix59
-rw-r--r--modules/common/nixos_shell_configuration.nix68
5 files changed, 502 insertions, 4 deletions
diff --git a/modules/by-name/ni/nixos-shell/module.nix b/modules/by-name/ni/nixos-shell/module.nix
new file mode 100644
index 00000000..219f080d
--- /dev/null
+++ b/modules/by-name/ni/nixos-shell/module.nix
@@ -0,0 +1,128 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Jörg Thalheim and contributors
+# SPDX-License-Identifier: MIT
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+ lib,
+ config,
+ pkgs,
+ self,
+ ...
+}: let
+ cfg = config.soispha.nixos-shell;
+in {
+ options.soispha.nixos-shell = {
+ enable = lib.mkEnableOption "nixos-shell";
+
+ user_name = lib.mkOption {
+ type = lib.types.str;
+ default = "soispha";
+ description = "The user to auto-login into the vm.";
+ };
+
+ configuration = {
+ specialArgs = lib.mkOption {
+ type = lib.types.attrsOf lib.types.anything;
+ default = {};
+ description = ''
+ The arguments to pass to the `specialArgs` attribute set.
+ '';
+ };
+ value = lib.mkOption {
+ type = lib.types.deferredModule;
+ default = {};
+ description = ''
+ Additional NixOS configuration to load into the VM's config.
+ '';
+ };
+ };
+
+ mounts = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
+ options = {
+ target = lib.mkOption {
+ type = lib.types.path;
+ description = "Target on the guest.";
+ };
+
+ cache_mode = lib.mkOption {
+ type = lib.types.enum ["none" "loose" "fscache" "mmap"];
+ default = "loose"; # bad idea? Well, at least it is fast!1!!
+ description = "9p caching policy";
+ };
+
+ readOnly =
+ (lib.mkEnableOption "mount this disk in read-only mode")
+ // {
+ default = true;
+ };
+
+ tag = lib.mkOption {
+ type = lib.types.str;
+ internal = true;
+ };
+ };
+
+ config.tag = lib.mkDefault (
+ builtins.substring 0 31 ( # tags must be shorter than 32 bytes
+ "a"
+ + # tags must not begin with a digit
+ builtins.hashString "md5" config._module.args.name
+ )
+ );
+ }));
+ default = {};
+ description = ''
+ Extra paths to make available in the vm.
+ These will be mounted ro to their `target.`
+ '';
+ };
+ };
+
+ config = let
+ vmSystem = self.inputs.nixpkgs.lib.nixosSystem {
+ inherit (cfg.configuration) specialArgs;
+
+ modules = [
+ {
+ # TODO(@bpeetz): This should be bumped each release. <2025-05-17>
+ system.stateVersion = "25.05";
+ }
+
+ cfg.configuration.value
+
+ (import ./shell_setup.nix {inherit cfg;})
+ ];
+ };
+
+ nixos-shell = pkgs.writeShellApplication {
+ name = "nixos-shell";
+ text = builtins.readFile ./nixos-shell.sh;
+
+ # We need to keep the PATH, as we otherwise can't pass it along.
+ inheritPath = true;
+
+ runtimeInputs = [
+ vmSystem.config.system.build.vm
+ pkgs.mktemp
+ pkgs.coreutils
+ pkgs.moreutils # for sponge
+ ];
+ runtimeEnv = {
+ HOST_NAME = vmSystem.config.system.name;
+ };
+ };
+ in
+ lib.mkIf cfg.enable {
+ environment.systemPackages = [
+ nixos-shell
+ ];
+
+ system.build.nixos-shell = vmSystem.config.system.build.vm;
+ };
+}
diff --git a/modules/by-name/ni/nixos-shell/nixos-shell.sh b/modules/by-name/ni/nixos-shell/nixos-shell.sh
new file mode 100755
index 00000000..390e60b1
--- /dev/null
+++ b/modules/by-name/ni/nixos-shell/nixos-shell.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env dash
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Jörg Thalheim and contributors
+# SPDX-License-Identifier: MIT
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+SHARED_DIR="$(mktemp --directory)"
+cleanup() {
+ rm --recursive "$SHARED_DIR"
+}
+trap cleanup EXIT
+export SHARED_DIR
+
+TMPDIR="$SHARED_DIR"
+export TMPDIR
+
+cat <<EOF >"$SHARED_DIR/.env_variables"
+{
+ "pwd": "$PWD",
+ "term": "$TERM",
+ "path": "$PATH"
+}
+EOF
+
+mk_tag() {
+ additional_path="$1"
+
+ # tags must be shorter than 32 bytes
+ # and must not begin with a digit.
+ {
+ printf "a"
+ echo "$additional_path" | sha256sum | head -c 30
+ }
+}
+
+for raw_additional_path in "$@"; do
+ additional_path="$(realpath "$raw_additional_path")"
+ tag="$(mk_tag "$additional_path")"
+
+ if [ "$(jq --arg mount "$tag" '.mount.[$mount]' "$SHARED_DIR/.env_variables")" != "null" ]; then
+ echo "Path '$additional_path' alread added."
+ shift 1
+ continue
+ fi
+
+ jq --arg mount "$tag" --arg target "$additional_path" \
+ '. + {mount: {$mount: $target}}' "$SHARED_DIR/.env_variables" | sponge "$SHARED_DIR/.env_variables"
+
+ set -- "$@" -virtfs "local,path=$additional_path,security_model=none,readonly=on,mount_tag=$tag"
+ shift 1
+done
+
+# Do not exec here, as we don't want to lose our cleanup hooks.
+"run-$HOST_NAME-vm" "$@"
diff --git a/modules/by-name/ni/nixos-shell/shell_setup.nix b/modules/by-name/ni/nixos-shell/shell_setup.nix
new file mode 100644
index 00000000..75af0678
--- /dev/null
+++ b/modules/by-name/ni/nixos-shell/shell_setup.nix
@@ -0,0 +1,191 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Jörg Thalheim and contributors
+# SPDX-License-Identifier: MIT
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+# This file contains the code that is used to setup the VM shell.
+{cfg}: {
+ lib,
+ config,
+ pkgs,
+ modulesPath,
+ ...
+}: let
+ mkVMDefault = lib.mkOverride 970;
+
+ env_file = "/tmp/shared/.env_variables";
+in {
+ imports = [
+ (modulesPath + "/profiles/qemu-guest.nix")
+ (modulesPath + "/virtualisation/qemu-vm.nix")
+ ];
+
+ config = {
+ assertions = [
+ {
+ assertion = builtins.elem cfg.user_name (lib.mapAttrsToList (name: value: name) config.users.users);
+ message = ''
+ Your user ${cfg.user_name} is not a recorded user in `config.users.users`.
+ Auto-login will not work.
+ '';
+ }
+ ];
+
+ # Lock root login.
+ users.users.root.hashedPassword = "";
+
+ # Remove unneeded clutter.
+ system.switch.enable = false;
+
+ virtualisation = {
+ # See https://wiki.qemu.org/Documentation/9psetup#Performance_Considerations.
+ # It is effectively a balance between ram and IO speed.
+ msize = let
+ kib = x: x * 1024;
+ in
+ mkVMDefault (kib 512);
+
+ graphics = mkVMDefault false;
+ memorySize = mkVMDefault 1700; # in MB
+ cores = mkVMDefault 16;
+
+ # Do not persist this VM.
+ diskImage = mkVMDefault null;
+
+ fileSystems =
+ lib.mapAttrs'
+ (_: mount: {
+ name = mount.target;
+
+ value = {
+ device = mount.tag;
+ fsType = "9p";
+ options =
+ [
+ "trans=virtio"
+ "version=9p2000.L"
+ "cache=${mount.cache_mode}"
+ "msize=${toString config.virtualisation.msize}"
+ ]
+ ++ lib.optionals mount.readOnly ["ro"];
+ };
+ })
+ cfg.mounts;
+
+ qemu = {
+ consoles = lib.mkIf (!config.virtualisation.graphics) ["tty0" "hvc0"];
+
+ options = let
+ mkMount = options: "-virtfs " + (builtins.concatStringsSep "," options);
+ in
+ lib.optionals (!config.virtualisation.graphics) [
+ "-serial null"
+ "-device virtio-serial"
+ "-chardev stdio,mux=on,id=char0,signal=off"
+ "-mon chardev=char0,mode=readline"
+ "-device virtconsole,chardev=char0,nr=0"
+ ]
+ ++ (lib.mapAttrsToList
+ (hostPath: mount:
+ mkMount [
+ "local"
+ "path=${builtins.toString hostPath}"
+ "security_model=none"
+ "readonly=on"
+ "mount_tag=${mount.tag}"
+ ])
+ cfg.mounts);
+ };
+ };
+
+ services = {
+ getty.helpLine = ''
+ If you are connect via serial console:
+ Type Ctrl-a c to switch to the qemu console
+ and `quit` to stop the VM.
+ '';
+
+ getty.autologinUser = cfg.user_name;
+ };
+
+ system.activationScripts = {
+ mountAdditionalPaths =
+ # bash
+ ''
+ PATH="${pkgs.jq}/bin:${pkgs.util-linux}/bin:$PATH"
+ export PATH
+
+ max_index="$(jq '.mount | keys | length' --raw-output ${env_file})"
+ index=0
+
+ mount --mkdir --type=tmpfs none "/.rw" --options=rw,relatime,mode=0755
+ while [ "$index" -lt "$max_index" ]; do
+ what="$(jq --argjson index "$index" '.mount | keys | map(.)[$index]' --raw-output ${env_file})"
+ where="$(jq --argjson index "$index" '.mount | map(.)[$index]' --raw-output ${env_file})"
+
+ mkdir "/.rw/$what"
+ mount --mkdir "$what" "/.ro/$what" \
+ --type=9p \
+ --options=ro,trans=virtio,version=9p2000.L,msize=${toString config.virtualisation.msize},x-systemd.requires=modprobe@9pnet_virtio.service
+
+ mkdir "/.rw/work-$what"
+ mount --mkdir --type=overlay overlay \
+ --options=rw,relatime,lowerdir="/.ro/$what",upperdir="/.rw/$what",workdir="/.rw/work-$what",uuid=on \
+ "/.ov/$what"
+
+ index="$((index + 1))"
+ done
+ '';
+ };
+
+ systemd.services.mountAdditionalPaths = {
+ after = ["local-fs.target"];
+ wantedBy = ["multi-user.target"];
+ path = [pkgs.jq];
+ script =
+ # bash
+ ''
+ max_index="$(jq '.mount | keys | length' --raw-output ${env_file})"
+ index=0
+
+ while [ "$index" -lt "$max_index" ]; do
+ what="$(jq --argjson index "$index" '.mount | keys | map(.)[$index]' --raw-output ${env_file})"
+ where="$(jq --argjson index "$index" '.mount | map(.)[$index]' --raw-output ${env_file})"
+
+ systemd-mount --type none "/.ov/$what" "$where" --options=bind
+
+ # HACK(@bpeetz): Nearly all of the paths are in $HOME anyways. So simply avoid
+ # the permission issue.
+ # Ideally, we would pass the original owners along with the mount. <2025-05-17>
+ chown --recursive soispha:users "/home/soispha"
+
+ index="$((index + 1))"
+ done
+ '';
+ };
+
+ environment = {
+ systemPackages = [
+ pkgs.jq
+ ];
+
+ sessionVariables = {
+ IN_NIXOS_SHELL = "true";
+ };
+
+ loginShellInit =
+ # bash
+ ''
+ cd "$(jq '.pwd' --raw-output ${env_file})"
+ export TERM="$(jq '.term' --raw-output ${env_file})"
+ export PATH="$(jq '.path' --raw-output ${env_file}):$PATH"
+ '';
+ };
+
+ networking.firewall.enable = mkVMDefault true;
+ };
+}
diff --git a/modules/common/default.nix b/modules/common/default.nix
index 690385b1..e4225e95 100644
--- a/modules/common/default.nix
+++ b/modules/common/default.nix
@@ -13,6 +13,15 @@
config,
pkgs,
lib,
+ # Needed for nixos-shell
+ libraries,
+ modules,
+ openPRsNixpkgs,
+ packageSets,
+ system,
+ self,
+ externalDependencies,
+ externalBinaries,
...
}: {
soispha = {
@@ -35,6 +44,48 @@
"/var/lib/systemd"
];
};
+
+ nixos-shell = {
+ enable = lib.mkDefault true;
+ configuration = {
+ specialArgs = {
+ inherit
+ libraries
+ modules
+ ;
+ };
+ value = lib.mkMerge [
+ {
+ _module.args = {
+ inherit
+ # extra package sources
+ openPRsNixpkgs
+ packageSets
+ # extra information
+ system
+ # nix registry
+ self
+ externalDependencies
+ # bins
+ # TODO: Integrate these into `pkgs/by-name` <2024-05-22>
+ externalBinaries
+ ;
+ };
+ }
+
+ {
+ require = [
+ ./nixos_shell_configuration.nix
+
+ ../../modules
+ ../../modules/common
+ ];
+ }
+ ];
+ };
+ mounts = {};
+ };
+
polkit.enable = true;
power.enable = true;
xdg.enable = true;
@@ -46,11 +97,11 @@
};
backup = {
storagebox = {
- enable = true;
+ enable = lib.mkDefault true;
user = "u459143-sub1";
};
local = {
- enable = true;
+ enable = lib.mkDefault true;
};
};
fwupd.enable = true;
@@ -78,7 +129,7 @@
water-reminder.enable = true;
systemDiff.enable = true;
unison = {
- enable = true;
+ enable = lib.mkDefault true;
foreign.userName = "soispha";
dataDir = "${config.home-manager.users.soispha.xdg.dataHome}/unison";
@@ -140,7 +191,7 @@
lf.enable = true;
gpg.enable = true;
river = {
- enable = true;
+ enable = lib.mkDefault true;
init = {
rules = [
{
diff --git a/modules/common/nixos_shell_configuration.nix b/modules/common/nixos_shell_configuration.nix
new file mode 100644
index 00000000..bbaa8477
--- /dev/null
+++ b/modules/common/nixos_shell_configuration.nix
@@ -0,0 +1,68 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{...}: {
+ soispha = {
+ networking = {
+ enable = true;
+ hostName = "lahmu";
+ mode = "systemd-networkd";
+ };
+
+ nixos-shell.enable = false;
+
+ nixpkgs = {
+ enable = true;
+ systemName = "x86_64-linux";
+ };
+
+ services = {
+ unison.enable = false;
+ backup = {
+ storagebox.enable = false;
+ local.enable = false;
+ };
+ };
+
+ programs = {
+ river.enable = false;
+
+ # We don't have access to the age secrets.
+ atuin.enableAge = false;
+ taskwarrior.enableAge = false;
+ };
+
+ locale = {
+ enable = true;
+ keyMap = "us";
+ };
+
+ users = {
+ enable = true;
+ enableDeprecatedPlugdev = true;
+
+ # Make logging in impossible.
+ # (This also removes root a obvious access-point from the virtual machine)
+ hashedPassword = "";
+ };
+ };
+
+ soispha = {
+ hardware = {
+ enable = false;
+ enableGraphics = false;
+ cpuType = "vm";
+ };
+
+ disks.enable = false;
+ };
+
+ # We run without state
+ # system.stateVersion = null;
+}