nixos/shadow: use file capabilities for newuidmap/newgidmap

Writing a multi-line /proc/<pid>/[ug]id_map only requires
CAP_SETUID/CAP_SETGID over the parent user namespace, not full root.
shadow's own --with-fcaps install mode (70971457b761) sets exactly
cap_setuid+ep / cap_setgid+ep, and Arch, Fedora and Debian have shipped
these binaries with file capabilities instead of setuid for years.

The setuid variant already drops to the same single capability before
the uid_map write (see lib/idmapping.c), so the privilege at the point
attacker-controlled data reaches the kernel is unchanged. The reduction
is in the startup window: with file capabilities the process never has
euid 0 and never holds the full capability set during NSS lookups,
/etc/subuid parsing and /proc/<pid> opening.

The only functional difference is that mapping host uid 0 into a child
namespace additionally needs CAP_SETFCAP, which the setuid path got
implicitly. NixOS never puts uid 0 into auto-allocated subuid ranges,
and granting it manually is a deliberate root-equivalent configuration;
the release notes document the override for that case.

nixosTests.{shadow,podman,docker-rootless} pass; the latter two
exercise newuidmap/newgidmap via rootless containers.

Supersedes #461172.

Co-authored-by: Rasheeq Azad <rasheeqhere@gmail.com>
This commit is contained in:
r-vdp
2026-04-17 10:57:19 +02:00
parent bbb5a121dc
commit 1ac3c5dc99
2 changed files with 12 additions and 3 deletions

View File

@@ -24,4 +24,4 @@
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- Create the first release note entry in this section!
- The `newuidmap` and `newgidmap` security wrappers are now installed with `cap_setuid`/`cap_setgid` file capabilities instead of the setuid-root bit, matching shadow's `--with-fcaps` install mode and other major distributions. Rootless containers (podman, docker-rootless, unprivileged user namespaces) are unaffected. The only behavioural change is that mapping host uid 0 via `/etc/subuid` (which NixOS never configures by default) additionally requires `cap_setfcap`; users who explicitly grant uid 0 in a subuid range can restore the previous behaviour with `security.wrappers.newuidmap.capabilities = lib.mkForce "cap_setuid,cap_setfcap+ep";`.

View File

@@ -267,13 +267,22 @@ in
group = "root";
inherit source;
};
mkCapRoot = capabilities: source: {
inherit capabilities source;
owner = "root";
group = "root";
};
in
{
su = mkSetuidRoot "${config.security.shadow.su.package}/bin/su";
sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
# File capabilities instead of setuid root, mirroring shadow's
# own --with-fcaps install mode and what Arch/Fedora/Debian ship.
# The kernel only requires CAP_SETUID/CAP_SETGID over the parent
# userns to write a multi-line /proc/<pid>/[ug]id_map.
newuidmap = mkCapRoot "cap_setuid+ep" "${cfg.package.out}/bin/newuidmap";
newgidmap = mkCapRoot "cap_setgid+ep" "${cfg.package.out}/bin/newgidmap";
}
// lib.optionalAttrs config.users.mutableUsers {
chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";