fix(sandbox): start OCI images with explicit UID or GID - #253
Conversation
|
🔍 OpenCodeReview found 2 issue(s) in this PR.
|
| let groups = if user.contains(':') { | ||
| self.read_account_file("/etc/group").await? | ||
| } else { | ||
| String::new() | ||
| }; |
There was a problem hiding this comment.
Only a named group requires /etc/group, but this downloads it for every USER containing :, including numeric forms such as 1000:1000. Such a valid numeric identity can now fail solely because an otherwise irrelevant group file is non-UTF-8 or exceeds the 1 MiB limit. Parse the group component first and read /etc/group only when that component is nonnumeric.
| if let Some(fields) = existing { | ||
| if parse_id(fields[3])? == gid { | ||
| return Ok(ResolvedUser { | ||
| name: fields[0].to_owned(), | ||
| passwd_entry: None, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
An explicit USER user:group must ignore the account's other supplementary group memberships, even when the requested GID equals its existing primary GID. Returning the original username here lets envd resolve that name normally and can retain supplementary groups, diverging from Docker semantics (and potentially granting extra access). For any explicit group, use/reuse the separate generated alias rather than returning the original account solely because its primary GID matches; reserve this early return for group.is_none().
| if let Some(fields) = names.get(name.as_str()) { | ||
| if fields[2].parse::<u32>() == Ok(uid) && fields[3].parse::<u32>() == Ok(gid) { | ||
| return Ok(ResolvedUser { | ||
| name, | ||
| passwd_entry: None, | ||
| }); | ||
| } | ||
| continue; | ||
| } | ||
| let passwd_entry = Some(format!("{name}:x:{uid}:{gid}::{home}:/bin/sh")); |
There was a problem hiding this comment.
A pre-existing image account named aenv-{uid}-{gid} is treated as one generated by this code based only on UID/GID. If that account has a different home (or other relevant passwd fields), envd will use those values, so relative filesystem operations no longer preserve the requested account's home as intended. Only reuse an entry if it matches the complete generated entry (at least UID, GID, and home); otherwise continue to a free suffix. Add a test for a same-UID/GID collision with a different home.
| ("nobody:0", "65534", "0"), | ||
| ("12345:nogroup", "12345", "65534"), |
There was a problem hiding this comment.
These expectations couple the identity test to distro-specific nobody/nogroup IDs, while the shared fixture uses a mutable image tag (ghcr.io/linuxserver/baseimage-ubuntu:noble). A valid account-database change could fail this test even though name resolution remains correct. Please either pin/use a fixture with guaranteed account mappings or determine the named account/group IDs from the guest before asserting them.
| if restored { | ||
| sandbox.pause().await?; | ||
| sandbox.resume().await?; | ||
| } |
There was a problem hiding this comment.
This only pauses and resumes the same running VM, so envd is not restarted and EnvdInstance::init does not run again. It therefore does not exercise the snapshot-restore path where numeric USER resolution must be repeated/idempotent. Keep the returned snapshot and start a new sandbox with resume_from_snapshot_config (or equivalent) to cover restoration.
0cf2f83 to
10ef972
Compare
10ef972 to
9078d89
Compare
| ); | ||
| bytes.extend_from_slice(&chunk); | ||
| } | ||
| String::from_utf8(bytes).with_context(|| format!("guest {path} is not UTF-8")) |
There was a problem hiding this comment.
This makes initialization fail for any numeric/UID:GID USER whenever either account file contains a non-UTF-8 byte, even if the requested UID/GID is represented by an otherwise valid ASCII record. Unix passwd/group files are byte-oriented and unrelated records can contain arbitrary bytes; numeric identity resolution only needs to parse the relevant colon-delimited fields. Please parse the account files as bytes (or use a carefully scoped lossy conversion) so unrelated invalid UTF-8 does not prevent a usable image from starting.
| .filter(|fields: &Vec<&str>| fields.len() == 7) | ||
| .collect(); |
There was a problem hiding this comment.
Malformed passwd records are silently dropped here. For a numeric UID this can make an existing account look absent and cause a second generated entry to be appended with a potentially different name/home, while for a named account it produces a misleading 'absent' error. Since this file is the source of identity resolution, please reject malformed records (with context) or parse them without silently discarding them.
What
Start and resume OCI images whose
Userconfig specifies a numeric UID or an explicit user/group pair. Resolve the identity to an envd account while preserving the requested UID/GID, image environment, and working directory.Why
OCI permits numeric identities without a passwd entry, but envd requires an account name. Passing
12345or12345:23456directly to envd can prevent image startup.Related issue
Extracted from #247 alongside #252 (volume deletion) and #254 (BuildKit).
Scope and non-goals
Guest identity resolution, privileged internal filesystem maintenance, tests, and runtime-configuration documentation. BuildKit integration and volume-deletion optimization are separate PRs.
Design and behavior changes
Compatibility and operations
Validation
make fmtmake clippymake test-unitCommands and results:
The VM test ran through the repository capability runner as a non-root user with isolated test state and dependency copies. Unit coverage includes existing and missing UIDs, explicit numeric and named groups, account-name collisions, invalid IDs, authenticated initialization, and image environment preservation. Go, code generation, and performance benchmarks do not apply.
Risks and reviewer notes
Review
src/sandbox/envd/user.rs. The fix adds passwd entries only when needed and leaves existing accounts intact. The new VM test checks six identities across cold startup and resume without depending on BuildKit. Guest root-selection helpers overlap identically with the volume fix and merge cleanly.Checklist