diff --git a/nixos/modules/system/boot/systemd/shutdown.nix b/nixos/modules/system/boot/systemd/shutdown.nix index 1e8b8c6f863c..242b47cc40b1 100644 --- a/nixos/modules/system/boot/systemd/shutdown.nix +++ b/nixos/modules/system/boot/systemd/shutdown.nix @@ -52,6 +52,7 @@ in what = "tmpfs"; where = "/run/initramfs"; type = "tmpfs"; + options = "mode=0700"; } ]; diff --git a/nixos/tests/systemd-shutdown.nix b/nixos/tests/systemd-shutdown.nix index 1f072086bdd1..fa0105cb90cf 100644 --- a/nixos/tests/systemd-shutdown.nix +++ b/nixos/tests/systemd-shutdown.nix @@ -17,12 +17,14 @@ import ./make-test-python.nix ( imports = [ ../modules/profiles/minimal.nix ]; systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/shutdown-message".source = pkgs.writeShellScript "shutdown-message" '' - echo "${msg}" + echo "${msg}" > /dev/kmsg ''; boot.initrd.systemd.enable = systemdStage1; }; testScript = '' + # Check that 'generate-shutdown-ramfs.service' is started + # automatically and that 'systemd-shutdown' runs our script. machine.wait_for_unit("multi-user.target") # .shutdown() would wait for the machine to power off machine.succeed("systemctl poweroff") @@ -31,6 +33,12 @@ import ./make-test-python.nix ( machine.wait_for_console_text("${msg}") # Don't try to sync filesystems machine.wait_for_shutdown() + + # In a separate boot, start 'generate-shutdown-ramfs.service' + # manually in order to check the permissions on '/run/initramfs'. + machine.systemctl("start generate-shutdown-ramfs.service") + stat = machine.succeed("stat --printf=%a:%u:%g /run/initramfs") + assert stat == "700:0:0", f"Improper permissions on /run/initramfs: {stat}" ''; } ) diff --git a/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock b/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock index ce5f5ef00971..67f72276575e 100644 --- a/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock +++ b/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "eyre" @@ -35,6 +35,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + [[package]] name = "log" version = "0.4.21" @@ -47,6 +53,7 @@ version = "0.1.0" dependencies = [ "eyre", "goblin", + "libc", "serde", "serde_json", ] diff --git a/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml b/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml index 69081b94d892..bdcdffd80195 100644 --- a/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml +++ b/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml @@ -9,5 +9,6 @@ edition = "2018" [dependencies] eyre = "0.6.8" goblin = "0.5.0" +libc = "0.2.171" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/pkgs/build-support/kernel/make-initrd-ng/src/main.rs b/pkgs/build-support/kernel/make-initrd-ng/src/main.rs index 934c2faebed8..64c114f2d9fa 100644 --- a/pkgs/build-support/kernel/make-initrd-ng/src/main.rs +++ b/pkgs/build-support/kernel/make-initrd-ng/src/main.rs @@ -5,9 +5,12 @@ use std::fs; use std::hash::Hash; use std::iter::FromIterator; use std::os::unix; +use std::os::unix::fs::PermissionsExt; use std::path::{Component, Path, PathBuf}; use std::process::Command; +use libc::umask; + use eyre::Context; use goblin::{elf::Elf, Object}; use serde::Deserialize; @@ -191,9 +194,9 @@ fn copy_file< let mut permissions = fs::metadata(&target) .wrap_err_with(|| format!("failed to get metadata for {:?}", target))? .permissions(); - permissions.set_readonly(false); - fs::set_permissions(&target, permissions) - .wrap_err_with(|| format!("failed to set readonly flag to false for {:?}", target))?; + permissions.set_mode(permissions.mode() | 0o200); + fs::set_permissions(&target, permissions.clone()) + .wrap_err_with(|| format!("failed to set read-write permissions for {:?}", target))?; // Strip further than normal if let Ok(strip) = env::var("STRIP") { @@ -207,6 +210,11 @@ fn copy_file< println!("{:?} was not successfully stripped.", OsStr::new(&target)); } } + + // Remove writable permissions + permissions.set_mode(permissions.mode() ^ 0o222); + fs::set_permissions(&target, permissions) + .wrap_err_with(|| format!("failed to remove writable permissions for {:?}", target))?; }; Ok(()) @@ -335,6 +343,9 @@ fn main() -> eyre::Result<()> { let output = &args[2]; let out_path = Path::new(output); + // The files we create should not be writable. + unsafe { umask(0o022) }; + let mut queue = NonRepeatingQueue::::new(); for sp in input {