Home / Alt manpages / proc_pid_setgroups(5)

  • proc_pid_setgroups(5)
  • File format
  • linux

Control setgroups Safely While Creating a User Namespace

Get the write order wrong with /proc/pid/setgroups and a user namespace setup fails with a confusing permission error. This guide covers when to read or write the file, and how to verify the result before writing a group ID map. Allow about 15 minutes for a read-only check, longer if you are building a namespace setup around it.

The key result is simple: allow and deny describe whether processes in the target user namespace may use setgroups(2), and writing deny is a one-way security decision for that namespace.

1. Check the local kernel and file

The installed proc_pid_setgroups(5) page comes from Linux man-pages 6.7, package version 6.7-2. It documents the file as available since Linux 3.19. This machine is running Linux 6.8.0-139-generic. The file is part of the user namespace interface, so its useful meaning depends on the namespace containing the process you inspect.

Start with your own process. This does not change state and normally needs no elevated privileges:

$ printf 'pid=%s\n' "$$"
pid=24831
$ cat /proc/$$/setgroups
allow

The PID is different on every shell. The initial user namespace normally reports allow. If the file is missing, stop and check you are on Linux and that the process is visible through the /proc mount you are using. Do not create a replacement file: this is a kernel interface, not ordinary configuration stored on disk.

2. Understand which process the path names

In /proc/pid/setgroups, pid is a process ID, not a user namespace ID. The value describes the user namespace that contains that process. Reading /proc/$$/setgroups checks your shell's namespace; reading a child PID checks the child's namespace, subject to the normal permissions and visibility rules of /proc.

Compare the setting with the UID and GID maps without modifying either:

$ pid=$$
$ printf 'setgroups: '
$ cat "/proc/$pid/setgroups"
setgroups: allow
$ printf 'uid map:\n'
$ cat "/proc/$pid/uid_map"
         0          0 4294967295
$ printf 'gid map:\n'
$ cat "/proc/$pid/gid_map"
         0          0 4294967295

The three-column identity maps above are the dummy maps exposed for the initial user namespace. A newly created user namespace starts without a usable UID or GID mapping, so do not treat this output as proof that a newly created namespace is ready for group changes.

3. Know when setgroups is actually permitted

The setting is only one part of the decision. If the target namespace's /proc/pid/gid_map has not been written, setgroups(2) is not permitted regardless of whether setgroups says allow. Once a valid GID map exists, allow permits the relevant processes to use the system call, subject to their capabilities and the other kernel rules.

This is a common diagnostic trap: seeing allow does not mean a process can immediately change supplementary groups. Check both files together:

$ pid=$$
$ cat "/proc/$pid/setgroups"
allow
$ cat "/proc/$pid/gid_map"
         0          0 4294967295

For a process in a new namespace, an empty or unwritten group map means group ID operations are still blocked. The exact map content is deployment-specific; never copy a map from an unrelated process just because its format looks right.

4. Decide whether the namespace must retain setgroups

Write deny only when the namespace must not change supplementary groups. The kernel allows a privileged process with CAP_SYS_ADMIN in the relevant namespace to write allow or deny before the namespace's GID map is written. In common unprivileged namespace setup, denying first is the step that permits a restricted single-line GID mapping without needing CAP_SETGID in the parent user namespace.

Many namespace helpers make this choice for you: on this machine, util-linux 2.41.3 documents that unshare --map-root-user, --map-current-user and --map-group imply --setgroups=deny. Read the helper's manual before combining options; do not add a second mechanism just because the resulting file is hard to explain.

Security boundary

Writing deny permanently disables setgroups(2) in that user namespace, and the kernel propagates the restriction to child user namespaces. Do not use it in a namespace that must later support group changes, and do not test it against a long-running service without a rollback plan. There is no undo: writing allow after the restriction has taken effect fails.

5. Apply deny before the GID map, when required

The order matters. For a new user namespace, the intended sequence is: create the namespace, write deny to its setgroups file, then write a valid GID map. The process doing the write needs the permissions the user namespace rules require. A typical privileged setup uses an explicit target PID and checks every write:

# Replace TARGET_PID with the child in the new user namespace.
# This is a security-sensitive, one-way change.
$ sudo sh -c 'printf deny > /proc/TARGET_PID/setgroups'
$ sudo cat /proc/TARGET_PID/setgroups
deny
$ sudo cat /proc/TARGET_PID/gid_map
# Continue with the mapping tool only after checking the intended map.

Do not paste the command with the literal placeholder. Resolve the child PID first, confirm it belongs to the namespace you intend to configure, and record the current setting. If the target already has gid_map written, writing deny is too late and fails with EPERM.

For an unprivileged workflow, use the namespace tool's documented mapping options rather than hand-writing a privileged shell command. Inspect the command before running it:

$ unshare --help | grep -E -- '--map-root-user|--setgroups'
$ unshare --map-root-user sh -c 'cat /proc/self/setgroups; cat /proc/self/uid_map; cat /proc/self/gid_map'
deny
         0       1000          1
         0       1000          1

This creates a short-lived namespace that exits when the shell exits. The UID, GID and exact result depend on the invoking account and host policy. If unprivileged user namespaces are disabled, the command can fail before the file is even created; that is a host policy result, not a reason to edit /proc manually.

Diagnose failures without guessing

  • Wrong order. A failed write commonly means the operation happened out of order, the target PID is wrong, the writer lacks the required capability, or the namespace policy forbids the transition.
  • Expected after deny. Trying allow after a deny decision fails and stays failing; it is not repaired by repeating the write, restarting the shell, or trying a different spelling.
  • gid_map already set. The ordering rule also blocks changing the setting to deny once gid_map is written.
$ printf allow | sudo tee /proc/TARGET_PID/setgroups
tee: /proc/TARGET_PID/setgroups: Operation not permitted
$ sudo cat /proc/TARGET_PID/setgroups
deny

Check the target's namespace identity, map contents and process lifetime before changing the setup. Keep the original namespace helper command and mapping configuration under review: a namespace can disappear when its last process exits, but that does not make an already-running service's security decision reversible. For a service, stop it through its normal supervisor, restore the previous namespace configuration, and start it only after verifying the replacement in a maintenance window.

Done means

  • Identified the namespace. You read the target process's setgroups value and identified its user namespace.
  • Checked both files. You checked gid_map separately and know that allow alone does not enable group changes.
  • Chose deliberately. You chose deny only when permanent setgroups disablement was intended.
  • Ordered the writes. You applied the decision before writing gid_map, when the namespace setup required it.
  • Verified and kept a fallback. You verified the final value and retained a recovery plan for any service or sandbox that depends on the namespace.