Skip to content

Cgroup adopt#2

Open
reboss wants to merge 638 commits into
masterfrom
cgroup-adopt
Open

Cgroup adopt#2
reboss wants to merge 638 commits into
masterfrom
cgroup-adopt

Conversation

@reboss

@reboss reboss commented May 6, 2026

Copy link
Copy Markdown
Owner

- What I did

Added support for automatic cgroup adoption via a new daemon configuration option --adopt-user-cgroups. When enabled, containers automatically inherit their creator's cgroup parent instead of running under the default Docker cgroup. This enables better resource isolation and accounting in multi-user environments where users should not be able to escape their systemd resource constraints.

This feature is particularly useful in:

  • Multi-tenant environments where users have resource limits enforced via systemd
  • HPC environments where job schedulers track resource usage via cgroups
  • Systems where containers should respect the resource constraints of the user who created them

- How I did it

The implementation follows a clean architectural pattern:

  1. Peer credential extraction - Created middleware (daemon/server/middleware/peercred_linux.go) that extracts UID/GID/PID from Unix socket connections using the SO_PEERCRED syscall and stores them in the request context.

  2. Cgroup derivation - Added utility (pkg/cgroups/adoption_linux.go) that reads /proc/<pid>/cgroup and parses both cgroup v1 and v2 formats to derive the user's cgroup parent (extracts the deepest .slice component).

  3. Daemon configuration - Added AdoptUserCgroups bool field to daemon config with corresponding --adopt-user-cgroups CLI flag.

  4. Enforcement at daemon layer - Modified adaptContainerSettings() in daemon/daemon_unix.go to call applyCgroupAdoption() when the feature is enabled. The enforcement uses a strict model: if a user tries to specify a different cgroup parent than the adopted one, the request is rejected with an InvalidParameter error.

  5. Platform support - Linux-specific implementations with no-op stubs for other platforms.

All code follows test-driven development with comprehensive unit and integration tests.

- How to verify it

  1. Start dockerd with the feature enabled:

    sudo dockerd --adopt-user-cgroups
  2. Check your current cgroup:

    cat /proc/$$/cgroup
    # Example output (cgroup v2): 0::/user.slice/user-1000.slice/session-3.scope
  3. Create a container:

    docker run -d --name test nginx
  4. Verify the container inherited your cgroup parent:

    docker inspect test | jq '.[0].HostConfig.CgroupParent'
    # Should show: /user.slice/user-1000.slice (or similar)
  5. Verify enforcement - attempting to override should fail:

    docker run --cgroup-parent /custom/parent nginx
    # Should error: "cannot set cgroup parent when --adopt-user-cgroups is enabled"
  6. Run the test suite:

    # Unit tests
    make test-unit TESTDIRS='./pkg/cgroups ./daemon/server/middleware'
    
    # Integration tests
    make test-integration TESTFLAGS='-test.run TestCgroupAdoption'

- Human readable description for the release notes

+ Add `--adopt-user-cgroups` daemon flag to automatically set container cgroup parent based on the API client's cgroup (Linux only)

- A picture of a cute animal (not mandatory but encouraged)

Cute penguin

vvoland and others added 27 commits June 15, 2026 16:29
Use the Windows stop poll timeout when waiting for exit.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Use the Windows stop poll timeout when waiting for stopped containers.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Use the Windows stop poll timeout when waiting for ping to exit.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
…currency

libn/networkdb: fix waiting for many bulkSync ACKs
Dockerfile: update Docker CLI to v29.5.3
This is the sixth patch release of the 1.3.z series of runc. Among some
performance improvements and bugfixes, it includes a fix for a low-severity
vulnerability ([CVE-2026-41579]) and users are encouraged to update. As it was
a low-severity vulnerability and it was reported by multiple people, we decided
to release it publicly with NO EMBARGO.

Security

This release includes a fix for the following low-severity security issue:

- CVE-2026-41579 allowed a malicious image with a /dev symlink to have
  limited write access to the host filesystem in ways that our analysis
  indicates was too limited to be problematic in practice. This bug was very
  similar to those fixed in CVE-2025-31133, CVE-2025-52565, CVE-2025-31133
  and was simply missed at the time when we hardened the rootfs preparation
  code. We have conducted a deeper audit and not found any other problematic
  cases.

Fixed

- A regression in runc v1.3.0 which can result in a stuck runc exec or
  runc run when the container process runs for a short time.
- Various integration test improvements.

Changed

- When masking directories with maskPaths, runc will now re-use a single
  tmpfs instance (which is not writable) to reduce the number tmpfs
  superblocks that need to be reaped when containers die (in particular,
  Kubernetes applies masks to per-CPU sysfs directories which get expensive
  quickly).

[CVE-2026-41579]: GHSA-xjvp-4fhw-gc47

full diff: opencontainers/runc@v1.3.5...v1.3.6

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Replace flaky legacy CLI restart-policy tests with container API
integration coverage.

The CLI tests poll inspect output after short-lived detached containers
exit, which can observe transient daemon monitor state while cleanup or
restart-policy handling is still settling.

On Windows this can race a manual restart against an `on-failure:3`
container that has not exhausted its automatic retries.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Try to deflake:

- TestStopContainerWithTimeoutCancel
- TestContainerRestartWithCancelledRequest
- TestWaitBlocked
- TestWaitRestartedContainer

Several container integration tests rely on shell TERM traps to produce
specific stop, restart, or wait behavior.
They issue stop or restart requests immediately after container
creation, so dockerd can signal the process before the shell installs
its trap.
When that happens, the process uses default signal behavior and tests
can miss the expected log line or observe the wrong exit status.

Emit a readiness log after installing each trap and wait for it before
issuing stop or restart. Move logsContains into a shared helper so the
readiness check can be reused by the affected tests.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
As libnftables uses `select(2)` on the netlink socket the process is
aborted if the socket's file descriptor is >= 1024. A dockerd process
could easily exceed 1024 open file descriptors at a time under normal
circumstances, so there is a risk of libnftables killing dockerd at a
random time through no fault of dockerd. Default to programming nftables
rulesets by exec'ing `nft -f` until libnftables is updated to be
compatible with processes that open a large number of file descriptors
by using `poll(2)` or `epoll(2)` instead of `select(2)`.

Signed-off-by: Cory Snider <csnider@mirantis.com>
internal/containerfs: Make cross platform
integration/container: Extend Windows container state waits
Move the useful Mounts API create coverage out of integration-cli and
into the container integration tests that own the behavior.

Add volume mount inspect assertions to TestContainerVolumeAnonymous for
destination normalization, read-only named volumes, explicit driver
configuration, and NoCopy mounts.

Add a focused bind mount inspect test for read-only bind mounts and
shared propagation.

Do not port the full integration-cli matrix. Existing integration tests
already cover anonymous volume creation and labeling, anonymous volume
removal with RemoveVolumes, and generic bind propagation handling.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
The minInt() helper function was introduced before Go had a built-in
min() function. Since the module requires Go 1.25 (and the built-in
min/max were introduced in Go 1.21), minInt() is now redundant.

Replace the one call site with the built-in min() and remove the
helper function.

Signed-off-by: Pham Duc Nam <nam8dmd@gmail.com>
- update the Dockerfile to switch to the cli-variant (as it doesn't
  require a docker daemon), and update to the latest v29 image
- update the script to not use the deprecated libnetwork repository

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
…ithTimeoutCancel

integration/container: Wait for trap setup before signaling
Dockerfile: update runc binary to v1.3.6
Signed-off-by: Rodrigo Campos <rodrigoca@microsoft.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
integration-cli/container: Rewrite on-failure restart tests
Migrate test api images search json content type from integration cli to integration tests

Signed-off-by: Abubacarr Ceesay <abubacarr671@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
integration-cli: Migrate TestContainersAPICreateMountsCreate
This test was already migrated as integration/container.TestStopContainerWithTimeout

Signed-off-by: carsontham <carsontham@outlook.com>
daemon/libnetwork: make libnftables opt-in only
…i-images

migrate TestAPIImagesSearchJSONContentType to integration
…it code

The Optional Features section called check_flags directly, so a missing
optional kernel flag (USER_NS, SECCOMP, the ext3/ext4 filesystem options,
etc.) sets EXITCODE=1 and the script exits non-zero, even when every
"Generally Necessary" flag is present. This breaks CI checks that rely
on the exit code to gate hosts where Docker is actually expected to run.

Mirror the pattern already used for Storage Drivers (L385-411): save the
running EXITCODE before the Optional Features section, run the optional
checks with a fresh EXITCODE, then restore it before the Network Drivers
section. A dedicated variable name (optional_features_exit) avoids a
collision with the CODE variable already used inside the kernel-<5.8
MEMCG_SWAP branch (L267-272).

Signed-off-by: Takumi Akasaka <takumiakasaka1231@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
…api-stop

integration-cli: migrated test TestContainerAPIStop
Also run `test-integration-flaky` for changed existing tests.

Keep the old added-test detection as-is, and add modified-test detection
on top of it to avoid regressing the existing behavior.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
reboss added 15 commits July 10, 2026 15:50
Tests verify:
- Context value storage and retrieval of peer credentials
- Middleware structure and handler wrapping
- Graceful handling of missing credentials

Signed-off-by: John Robbins <John.Robbins@amd.com>
Implements DeriveParentFromPid() to read /proc/<pid>/cgroup and extract
the appropriate cgroup parent slice for container placement.

- Supports both cgroup v1 and v2 formats
- Prioritizes systemd slices over other controllers
- Extracts deepest .slice component (e.g., user-1000.slice)
- Falls back gracefully for non-systemd setups

Signed-off-by: John Robbins <John.Robbins@amd.com>
Tests verify:
- Cgroup v2 format parsing (unified hierarchy)
- Cgroup v1 format parsing (systemd and cpu controllers)
- Extraction of deepest .slice component
- Error handling for invalid/empty files
- Edge cases (root cgroup, no slice components)

Signed-off-by: John Robbins <John.Robbins@amd.com>
Adds new boolean config field to enable user cgroup adoption.
When enabled, containers will automatically inherit their creator's
cgroup parent based on the API client's process ID.

This is the configuration knob for the cgroup adoption enforcement
feature, allowing administrators to enable/disable it via daemon.json
or CLI flags.

Signed-off-by: John Robbins <John.Robbins@amd.com>
Adds CLI flag registration for the cgroup adoption feature.
Administrators can enable it via:
  dockerd --adopt-user-cgroups

Placed near other security and cgroup-related flags for logical grouping.

Signed-off-by: John Robbins <John.Robbins@amd.com>
Adds peer credential middleware to the middleware chain, positioned
after version middleware and before authorization middleware.

The middleware extracts peer credentials from Unix socket connections
and makes them available in request context for downstream handlers and
enforcement logic.

Signed-off-by: John Robbins <John.Robbins@amd.com>
Add placeholder tests for daemon-level cgroup adoption enforcement.
Tests are marked as Skip until the applyCgroupAdoption method is
implemented in the next commit.

Tests will verify:
- Adoption works when enabled with valid peer credentials
- Error when peer credentials missing
- Rejection of user-specified cgroup parent
- Acceptance of matching cgroup parent values

Signed-off-by: John Robbins <John.Robbins@amd.com>
Adds applyCgroupAdoption() method that:
- Extracts peer credentials from request context
- Derives cgroup parent from the client's PID
- Enforces that containers cannot override the cgroup parent
- Rejects requests with error if user tries to set different parent

Integrated into adaptContainerSettings() which is called during
container creation. Only enforces when AdoptUserCgroups config is enabled.

The enforcement is strict: containers MUST run under their creator's
cgroup when adoption is enabled, with no exceptions.

Signed-off-by: John Robbins <John.Robbins@amd.com>
- TestCgroupAdoptionEnabled: Verifies containers inherit creator's cgroup
- TestCgroupAdoptionDisabled: Verifies feature is off by default
- TestCgroupAdoptionUserOverrideRejected: Verifies enforcement (rejects non-matching parent)
- TestCgroupAdoptionMatchingParentAccepted: Allows matching parent override
- TestCgroupAdoptionNoPeerCredentials: Handles missing peer credentials gracefully

Tests require root and use daemon.New() test harness.

Signed-off-by: John Robbins <John.Robbins@amd.com>
Add ConnContext to http.Server to store the net.Conn in request context.
This is required for the peer credential middleware to access the
underlying Unix socket file descriptor via SO_PEERCRED syscall.

Without this, r.Context().Value(http.LocalAddrContextKey) returns nil
and peer credentials cannot be extracted.

Signed-off-by: John Robbins <John.Robbins@amd.com>
Change cgroup adoption to use the entire cgroup hierarchy path
instead of extracting only the deepest .slice component. This
ensures containers properly inherit resource limits from SLURM
jobs, systemd scopes, and other complex cgroup hierarchies.

For example, a SLURM job's cgroup:
  /system.slice/slurmstepd.scope/job_123/step_0/user/task_0
will now be adopted in full, rather than just "system.slice".
Add middleware to extract UID/GID/PID from Unix socket connections
using SO_PEERCRED. This allows API handlers to identify the client
process for security and resource management features.

The middleware stores credentials in the request context using
PeerCredKey, and uses a custom PeerConnKey to avoid conflicts with
http.LocalAddrContextKey which gets overwritten by the HTTP stack.
Register the peer credential middleware in the API server chain
and configure ConnContext to store connections in the request context.

Uses middleware.PeerConnKey to avoid conflicts with the standard
http.LocalAddrContextKey which the HTTP stack overwrites with the
local address value.
Update cgroup adoption tests to expect full path instead of
deepest .slice component. Add test for SLURM cgroup hierarchy.

Add tests for peer credential middleware to verify:
- Middleware handles missing connections gracefully
- PeerConnKey is distinct from http.LocalAddrContextKey
- Credentials are properly stored in context

f, err := os.Create(path)
// lgtm[go/path-injection] Ignore CodeQL "path depends on a user-provided value".
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.