Drop privileges to www-data by default for serve and run#246
Merged
Conversation
Security: - serve/run now drop OS privileges to www-data by default when started as root and the user resolves — the no-flag path no longer keeps serving as root. Best-effort: skips silently when already non-root, warns and stays root when www-data is absent, and never aborts startup. The switch is gated on geteuid()==0 at both the default (apply_drop) and explicit (drop_to) paths, so user-switching only happens from root. - Explicit --user keeps fail-fast semantics (must start as root, resolve at parse time, drop or abort). --user=root is the opt-out to stay root. API: - Replace the drop field's Option<DropTarget> with a DropIntent enum (Explicit(DropTarget) | DefaultWwwData) on ServeOptions.drop_to and RunOptions.user; default is DefaultWwwData. resolve_user resolves a present --user eagerly and defers www-data resolution to drop time. - New privdrop::apply_drop(&DropIntent) replaces the direct drop_to calls in main; pure plan_default_drop(is_root, resolved) drives the decision and is unit-testable without a real setuid. Docs: - CHANGELOG: breaking-change entry — serve/run default to www-data; --user=root restores prior behavior, explicit --user overrides. - cli.md / docker.md / installation.md (en/ru/zh): document the default best-effort drop, the three outcomes, the --user=root opt-out, and that the image starts as root but serve/run self-drop before serving; bare php CLI invocations do not self-drop. Regenerate llms-full.txt. Tests: - Parser: default-to-www-data for serve/run/implicit, --user=root opt-out. - Planner: non-root skips, root-without-user stays root, root-with-user drops. 7 tests (7 unit).
0c89ba9 to
7f81780
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes
oxphp serveandoxphp rundrop OS privileges towww-databy default when no--useris given, so the official image no longer serves traffic as root out of the box. Explicit--userkeeps its fail-fast semantics;--user=rootis the opt-out to stay root.Behavior
The drop field changes from
Option<DropTarget>to aDropIntentenum (Explicit(DropTarget)|DefaultWwwData).--user)--user=<spec>www-data(or warn + stay root ifwww-datais absent)www-dataat drop time (not parse time), and skips when already non-root.--useris fail-fast: it must start as root and resolves the spec at parse time.geteuid()is checked inapply_dropfor the default and again indrop_tofor the explicit flag.serve, explicitrun, and implicit run. Timing (serve: after bind, before workers; run: before MINIT) is unchanged.Breaking change
The official image (which ships
www-data) now serves aswww-datarather than root. Pass--user=rootto restore the prior behavior, or--user=<name|uid[:gid]>to drop to a different account. Orchestrator-level drops (docker run --user, Composeuser:, KubernetessecurityContext) are unaffected and take precedence — a non-root start makes the self-drop a no-op. Documented as a breaking entry in the CHANGELOG.Tests
www-datafor serve/run/implicit,--user=rootopt-out, existing explicit cases.plan_default_drop: non-root skips, root-without-user stays root, root-with-user drops.886 tests pass on host (
cargo fmt/clippy --no-default-features/test --no-default-features).