Respect USER directive from OCI image config #8

Merged
marvin merged 1 commits from feature/user-directive into master 2026-09-10 12:18:57 +02:00
Collaborator

Summary

Implements support for the Dockerfile USER directive so that the container's CMD/ENTRYPOINT runs as the configured user instead of root.

Closes #7.

Changes

  • src/inject.rs: Added user: Option<String> to the Config struct (serde maps Useruser). The inject() function now writes /slim/user into the rootfs at build time when the field is set.
  • src/build.rs: Passes config.user through build_inner into inject().
  • src/scripts/slim-init.sh: Reads /slim/user at boot and drops privileges via su before executing the command. Numeric uids are resolved to usernames via /etc/passwd (BusyBox su doesn't accept numeric args). When dropping privileges, the command runs as a child (not exec) so PID 1 stays root and can poweroff -f after the command exits — non-root cannot call poweroff.
  • example/test.sh: Added test_user verifying both build-time CMD and --cmd override run as the configured user.

How it works

  1. At build time, inject() reads .Config.User from the podman image and writes it to /slim/user in the rootfs.
  2. At boot, slim-init.sh reads /slim/user and stores it in SLIM_USER.
  3. Before executing the command (either /slim/exec or the decoded slim.cmd=), the slim_exec function drops privileges:
    • If SLIM_USER is empty → exec /bin/sh -c "..." (original behavior)
    • If SLIM_USER is set → resolve numeric uids to usernames, then su "$user" -c "..." as a child, followed by poweroff -f as root

Test results

=== Testing USER directive ===
-- Test 1: build-time CMD runs as USER
  PASS: USER build-time CMD runs as testuser
-- Test 2: run --cmd override runs as USER
  PASS: USER run --cmd override runs as testuser

All existing alpine tests continue to pass. cargo clippy, cargo fmt --check, and cargo test all clean.

## Summary Implements support for the Dockerfile `USER` directive so that the container's CMD/ENTRYPOINT runs as the configured user instead of root. Closes #7. ## Changes - **`src/inject.rs`**: Added `user: Option<String>` to the `Config` struct (serde maps `User` → `user`). The `inject()` function now writes `/slim/user` into the rootfs at build time when the field is set. - **`src/build.rs`**: Passes `config.user` through `build_inner` into `inject()`. - **`src/scripts/slim-init.sh`**: Reads `/slim/user` at boot and drops privileges via `su` before executing the command. Numeric uids are resolved to usernames via `/etc/passwd` (BusyBox `su` doesn't accept numeric args). When dropping privileges, the command runs as a child (not `exec`) so PID 1 stays root and can `poweroff -f` after the command exits — non-root cannot call `poweroff`. - **`example/test.sh`**: Added `test_user` verifying both build-time CMD and `--cmd` override run as the configured user. ## How it works 1. At build time, `inject()` reads `.Config.User` from the podman image and writes it to `/slim/user` in the rootfs. 2. At boot, `slim-init.sh` reads `/slim/user` and stores it in `SLIM_USER`. 3. Before executing the command (either `/slim/exec` or the decoded `slim.cmd=`), the `slim_exec` function drops privileges: - If `SLIM_USER` is empty → `exec /bin/sh -c "..."` (original behavior) - If `SLIM_USER` is set → resolve numeric uids to usernames, then `su "$user" -c "..."` as a child, followed by `poweroff -f` as root ## Test results ``` === Testing USER directive === -- Test 1: build-time CMD runs as USER PASS: USER build-time CMD runs as testuser -- Test 2: run --cmd override runs as USER PASS: USER run --cmd override runs as testuser ``` All existing alpine tests continue to pass. `cargo clippy`, `cargo fmt --check`, and `cargo test` all clean.
marvin added 1 commit 2026-09-10 12:18:16 +02:00
Respect USER directive from OCI image config
CI / build (pull_request) Successful in 12s
524ef3e793
Add support for the Dockerfile USER directive so that the container's
CMD/ENTRYPOINT runs as the configured user instead of root.

Changes:
- inject.rs: Add user field to Config struct, write /slim/user at
  build time
- build.rs: Pass config.user through to inject()
- slim-init.sh: Read /slim/user and drop privileges via su before
  executing the command. Numeric uids are resolved to usernames via
  /etc/passwd (BusyBox su does not accept numeric args). When dropping
  privileges, run as a child (not exec) so PID 1 stays root and can
  poweroff after the command exits.
- test.sh: Add test_user verifying build-time CMD and --cmd override
  both run as the configured user

Closes #7
marvin force-pushed feature/user-directive from f6b0721f06 to 524ef3e793 2026-09-10 12:18:16 +02:00 Compare
hulthe approved these changes 2026-09-10 12:18:43 +02:00
marvin merged commit 6111bbeb8d into master 2026-09-10 12:18:57 +02:00
hulthe deleted branch feature/user-directive 2026-09-10 12:19:13 +02:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: hulthe/boco#8