Skip to content

internal/as_user: drop supplementary groups - #2301

Open
JasonColapietro wants to merge 3 commits into
coreos:mainfrom
JasonColapietro:as-user/drop-supplementary-groups
Open

JasonColapietro wants to merge 3 commits into
coreos:mainfrom
JasonColapietro:as-user/drop-supplementary-groups

Conversation

@JasonColapietro

@JasonColapietro JasonColapietro commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

set_eids() changes the effective gid and uid but leaves the caller's
supplementary groups in place. The privilege-dropped thread can therefore
access files through root's group memberships while acting as another user.

Clear the supplementary group list before relinquishing gid and uid, while
setgroups() is still permitted. Return an error if clearing the list fails.
The release note records this behavior change.

Regression coverage

Adds TestOpenFileDropsSupplementaryGroups, using the existing root-gated test
pattern suggested in review. It exercises the public OpenFile function:

  • a directory owned by root:4242, mode 0070, rejects creation as uid/gid
    65534 even though the caller belongs to supplementary group 4242;
  • creation in a directory owned by the target user still succeeds;
  • the caller keeps its supplementary groups after both operations.

A helper subprocess receives the group-4242 credential before it starts, so the
test never changes the test runner's credentials. The test needs Linux, cgo,
and root, and skips when run without root. Numeric test IDs avoid NSS lookups.

Validation

  • Linux amd64, Go 1.26.8: the new regression fails against the original C code
    because the group-only creation succeeds; the target-owned case still passes.
  • With the fix, root go test -count=1 -race -v ./internal/as_user passes.
  • go vet ./internal/as_user passes.
  • ./build butane && ./test passes as a non-root Linux user, including
    82 passing package groups and the Ignition/Butane documentation checks.
  • git diff --check and gofmt pass.

Scope

Ignition already runs as root; as_user is defense in depth rather than a
security boundary. Its caller writes SSH authorized keys during first boot.
The change clears supplementary groups rather than looking up and installing
the target user's group memberships.

Fixes #2242.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: coreos/ignition/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: e1104518-50a9-4eea-8889-a6071b4fe196

📥 Commits

Reviewing files that changed from the base of the PR and between 838740f and 8e404ef.

📒 Files selected for processing (1)
  • internal/as_user/as_user_test.go

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: tmt-tests
  • GitHub Check: Test ignition-validate (1.26.x, windows-latest)
  • GitHub Check: Test (1.26.x)
  • GitHub Check: Test (1.25.x)
🧰 Additional context used
📓 Path-based instructions (2)
**Naming**: PascalCase exported, camelCase unexported, snake_case filenames

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/as_user/as_user_test.go
**Formatting**: `gofmt` enforced (CI and `./test`) **License header**: Required on all `.go` files (Apache 2.0, 13-line header) **Imports**: stdlib, blank line, project packages, blank line, external deps

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/as_user/as_user_test.go
🔇 Additional comments (1)
internal/as_user/as_user_test.go (1)

1-109: LGTM!


📝 Walkthrough

Walkthrough

set_eids now removes supplementary groups before lowering effective gid and uid privileges. It returns failure if group removal fails. A root-only Linux test verifies access behavior and caller group preservation. Release notes record this fix and an unrelated relabel-path fix.

Changes

Privilege-drop hardening

Layer / File(s) Summary
Clear supplementary groups during credential changes
internal/as_user/as_user.c, docs/release-notes.md
set_eids calls setgroups(0, NULL) before changing credentials and returns -1 if the call fails. Its documentation and the release notes describe the behavior.
Verify supplementary-group handling
internal/as_user/as_user_test.go
A root-only subprocess test sets supplementary group 4242, checks failure and success cases for uid/gid 65534, and confirms that group 4242 remains in the caller after OpenFile returns.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: yasminvalim

🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning docs/release-notes.md adds a release-note entry for SELinux relabeling on OSTree platforms and references #2316. The entry is unrelated to issue #2242 and to supplementary-group removal. The diff sh… Remove the #2316 release-note entry from this pull request, or move it to the pull request that implements the SELinux relabeling change.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The change satisfies issue #2242. internal/as_user/as_user.c includes <grp.h> and calls setgroups(0, NULL) before setregid() and setreuid(). It returns -1 when group removal fails. `intern…
Commit Message Convention ✅ Passed Both non-merge commits in the review range conform. Their subjects are internal/as_user: drop supplementary groups and internal/as_user: test supplementary group drop; each uses a valid subsystem …
Title check ✅ Passed The title uses the required subsystem: lowercase description format, uses imperative mood, and accurately describes the main change to internal/as_user.
Description check ✅ Passed The description clearly explains the supplementary-group security fix, regression tests, validation, scope, and linked issue. It is directly related to the changeset.
Full details: Out of Scope Changes check

Explanation

docs/release-notes.md adds a release-note entry for SELinux relabeling on OSTree platforms and references #2316. The entry is unrelated to issue #2242 and to supplementary-group removal. The diff shows that this entry was introduced in this pull request.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@JasonColapietro
JasonColapietro marked this pull request as ready for review August 22, 2026 01:30
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

Binary size report (bin/amd64/ignition)

Size
Base (main) 33MiB
PR (#2301) 33MiB
Delta -4.6KiB (-0.01%)

@yasminvalim yasminvalim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, @JasonColapietro! Nice work!
I guess you just need to rebase and handle the conflicts, then we can run the workflow again :)

set_eids() switched the effective gid and uid to the target user but
never touched the supplementary group list, so the privilege-dropped
thread kept the caller's group memberships while acting as that user.
A directory reachable only through one of root's supplementary groups
stayed reachable for the duration of the switch.

Drop the list with setgroups(0, NULL) before relinquishing the gid and
uid, while the thread still holds the privilege required to make that
call, following the revocation order described in CERT POS36-C.

Fixes coreos#2242

Signed-off-by: Jason Colapietro <55137770+JasonColapietro@users.noreply.github.com>
@JasonColapietro
JasonColapietro force-pushed the as-user/drop-supplementary-groups branch from 27bbd35 to 838740f Compare September 15, 2026 16:51

@prestist prestist left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonColapietro Nice fix,lgtm.

One thought; we already have root-gated tests in internal/exec/util/user_group_lookup_test.go using the t.Skip("test requires root") pattern. Might be worth adding something similar here in internal/as_user/ to verify the groups actually get dropped. Like creating a dir that's only group-accessible (0070), then calling OpenFile as a user not in that group and checking it gets EACCES.

Not blocking, just wanted to know your thoughts?

JasonColapietro and others added 2 commits September 20, 2026 04:49
Exercise OpenFile with a group-only directory and a directory owned by
the target user. Require EACCES for access granted only by the caller's
supplementary group, while preserving access owned by the target user.

Set the caller's groups in a helper subprocess so the test runner's
credentials remain unchanged, and check that OpenFile leaves the helper's
own supplementary groups intact. Skip the test when not running as root.

Signed-off-by: Jason Colapietro <jasoncola1@gmail.com>
Describe the access and caller-group guarantees of the root-gated test.

Signed-off-by: Jason Colapietro <jasoncola1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hardening: Supplementary groups are never dropped in privilege-drop (as_user.c)

3 participants