From 7fe74bb8335f8fcca4f59585d43efc05812cdd53 Mon Sep 17 00:00:00 2001 From: nikstur Date: Sun, 12 Oct 2025 15:54:20 +0200 Subject: [PATCH 1/3] nixos/profiles/bashless: re-enable systemd-timesyncd systemd-timesyncd is now bashless and can thus re-enabled in the bashless profile. --- nixos/modules/profiles/bashless.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/nixos/modules/profiles/bashless.nix b/nixos/modules/profiles/bashless.nix index b6764a92f4f8..171a695809bf 100644 --- a/nixos/modules/profiles/bashless.nix +++ b/nixos/modules/profiles/bashless.nix @@ -36,8 +36,6 @@ systemd.shutdownRamfs.enable = lib.mkDefault false; # Relies on the gzip command which depends on bash services.logrotate.enable = lib.mkDefault false; - # Service relies on bash scripts - services.timesyncd.enable = lib.mkDefault false; # Check that the system does not contain a Nix store path that contains the # string "bash". From b9d0fd8be46d7a6ce24c13899ba07d5c3526f688 Mon Sep 17 00:00:00 2001 From: nikstur Date: Fri, 10 Oct 2025 23:18:06 +0200 Subject: [PATCH 2/3] nixos/systemd/shutdown: stricter sandboxing Copied the config from archlinux's mkinitcpio: https://github.com/archlinux/mkinitcpio/blob/318ae30f6a72dc5d256edbe23101fcffdba959ef/systemd/mkinitcpio-generate-shutdown-ramfs.service --- nixos/modules/system/boot/systemd/shutdown.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/nixos/modules/system/boot/systemd/shutdown.nix b/nixos/modules/system/boot/systemd/shutdown.nix index b42aaf5a0d32..ef2491a2d7ca 100644 --- a/nixos/modules/system/boot/systemd/shutdown.nix +++ b/nixos/modules/system/boot/systemd/shutdown.nix @@ -71,9 +71,21 @@ in serviceConfig = { Type = "oneshot"; + ExecStart = "${pkgs.makeInitrdNGTool}/bin/make-initrd-ng ${ramfsContents} /run/initramfs"; + + # Sandboxing ProtectSystem = "strict"; ReadWritePaths = "/run/initramfs"; - ExecStart = "${pkgs.makeInitrdNGTool}/bin/make-initrd-ng ${ramfsContents} /run/initramfs"; + ProtectHome = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + PrivateNetwork = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; }; }; }; From 653fd8c99590db1b83f96c52f70f31e5de239719 Mon Sep 17 00:00:00 2001 From: nikstur Date: Sat, 11 Oct 2025 00:53:17 +0200 Subject: [PATCH 3/3] nixos/systemd/shutdown: make shutdown ramfs bashless --- nixos/modules/profiles/bashless.nix | 2 -- .../modules/system/boot/systemd/shutdown.nix | 21 ++++++++++++++++++- nixos/tests/activation/bashless.nix | 8 +++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/nixos/modules/profiles/bashless.nix b/nixos/modules/profiles/bashless.nix index 171a695809bf..a658a5977709 100644 --- a/nixos/modules/profiles/bashless.nix +++ b/nixos/modules/profiles/bashless.nix @@ -32,8 +32,6 @@ boot.kexec.enable = lib.mkDefault false; # Relies on bash scripts powerManagement.enable = lib.mkDefault false; - # Has some bash inside - systemd.shutdownRamfs.enable = lib.mkDefault false; # Relies on the gzip command which depends on bash services.logrotate.enable = lib.mkDefault false; diff --git a/nixos/modules/system/boot/systemd/shutdown.nix b/nixos/modules/system/boot/systemd/shutdown.nix index ef2491a2d7ca..754e38b9714d 100644 --- a/nixos/modules/system/boot/systemd/shutdown.nix +++ b/nixos/modules/system/boot/systemd/shutdown.nix @@ -17,6 +17,7 @@ in enable = lib.mkEnableOption "pivoting back to an initramfs for shutdown" // { default = true; }; + contents = lib.mkOption { description = "Set of files that have to be linked into the shutdown ramfs"; example = lib.literalExpression '' @@ -34,6 +35,22 @@ in type = utils.systemdUtils.types.initrdStorePath; default = [ ]; }; + + shell.enable = lib.mkEnableOption "" // { + default = config.environment.shell.enable; + internal = true; + description = '' + Whether to enable a shell in the shutdown ramfs. + + In contrast to `environment.shell.enable`, this option actually + strictly disables all shells in the shutdown ramfs because they're not + copied into it anymore. Paths that use a shell (e.g. via the `script` + option), will break if this option is set. + + Only set this option if you're sure that you can recover from potential + issues. + ''; + }; }; config = lib.mkIf cfg.enable { @@ -43,9 +60,11 @@ in "/etc/os-release".source = config.environment.etc.os-release.source; }; systemd.shutdownRamfs.storePaths = [ - pkgs.runtimeShell "${pkgs.coreutils}/bin" ] + ++ lib.optionals cfg.shell.enable [ + pkgs.runtimeShell + ] ++ map (c: builtins.removeAttrs c [ "text" ]) (builtins.attrValues cfg.contents); systemd.mounts = [ diff --git a/nixos/tests/activation/bashless.nix b/nixos/tests/activation/bashless.nix index 06028a81db30..13a467293fc9 100644 --- a/nixos/tests/activation/bashless.nix +++ b/nixos/tests/activation/bashless.nix @@ -19,6 +19,7 @@ # work. environment.binsh = lib.mkForce null; boot.initrd.systemd.shell.enable = false; + systemd.shutdownRamfs.shell.enable = false; # This ensures that we only have the store paths of our closure in the # in the guest. This is necessary so we can grep in the store. @@ -31,6 +32,7 @@ environment.systemPackages = [ pkgs.coreutils pkgs.gnugrep + pkgs.findutils ]; # Unset the regex because the tests instrumentation needs bash. @@ -43,6 +45,12 @@ with subtest("/bin/sh doesn't exist"): machine.fail("stat /bin/sh") + with subtest("shutdown ramfs is bashless"): + machine.systemctl("start generate-shutdown-ramfs.service") + shutdown_ramfs_bash_paths = machine.succeed("find /run/initramfs -type d,f -name '*bash*'") + print(shutdown_ramfs_bash_paths) + t.assertNotIn("bash", shutdown_ramfs_bash_paths) + bash_store_paths = machine.succeed("ls /nix/store | grep bash || true") print(bash_store_paths) '';