Phase B-1: ownership fixup via debugfs pass
imagepull.Flatten now captures per-file uid/gid/mode/type from the tar headers as it walks layers, returning a Metadata map alongside the extracted tree. Whiteouts correctly drop the victim's metadata. The returned Metadata feeds the new imagepull.ApplyOwnership, which pipes a batched `set_inode_field` script to `debugfs -w -f -`. Why: mkfs.ext4 -d copies the runner's on-disk uids verbatim, so without this pass setuid binaries become setuid-nonroot and sshd refuses to start on the resulting image. With the pass, a pulled debian:bookworm has /usr/bin/sudo with uid=0 + setuid bit surviving intact. imagepull.BuildExt4 signature unchanged; ownership is applied as a separate step by the daemon orchestrator between BuildExt4 and StageBootArtifacts, keeping each helper focused. The seam (d.pullAndFlatten) now returns (Metadata, error) for test stubs to feed synthetic metadata. StdinRunner is a new duck-typed extension next to CommandRunner; the real system.Runner implements RunStdin, test mocks don't need to unless they exercise stdin. Prevents every existing mock from growing a new method. Tests: - TestFlattenCapturesHeaderMetadata: setuid bit + mode survive the tar-header walk - TestApplyOwnershipRewritesUidGidMode: real debugfs round-trip — create ext4 with runner's uid, apply synthetic metadata setting uid=0 + setuid mode, verify via `debugfs -R stat` that the inode now has uid=0 and mode 04755 - TestBuildOwnershipScriptDeterministic: sorted, well-formed sif script output Debugfs and mkfs.ext4 tests skip if the binaries aren't on PATH. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2e4d4b14da
commit
43982a4ae3
7 changed files with 334 additions and 32 deletions
|
|
@ -86,7 +86,8 @@ func (d *Daemon) PullImage(ctx context.Context, params api.ImagePullParams) (ima
|
|||
}
|
||||
defer os.RemoveAll(rootfsTree)
|
||||
|
||||
if err := d.runPullAndFlatten(ctx, ref, d.layout.OCICacheDir, rootfsTree); err != nil {
|
||||
meta, err := d.runPullAndFlatten(ctx, ref, d.layout.OCICacheDir, rootfsTree)
|
||||
if err != nil {
|
||||
return model.Image{}, fmt.Errorf("pull oci image: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +107,9 @@ func (d *Daemon) PullImage(ctx context.Context, params api.ImagePullParams) (ima
|
|||
if err := imagepull.BuildExt4(ctx, d.runner, rootfsTree, rootfsExt4, sizeBytes); err != nil {
|
||||
return model.Image{}, fmt.Errorf("build rootfs ext4: %w", err)
|
||||
}
|
||||
if err := imagepull.ApplyOwnership(ctx, d.runner, rootfsExt4, meta); err != nil {
|
||||
return model.Image{}, fmt.Errorf("apply ownership: %w", err)
|
||||
}
|
||||
|
||||
stagedKernel, stagedInitrd, stagedModules, err := imagemgr.StageBootArtifacts(ctx, d.runner, stagingDir, kernelPath, initrdPath, modulesDir)
|
||||
if err != nil {
|
||||
|
|
@ -138,13 +142,13 @@ func (d *Daemon) PullImage(ctx context.Context, params api.ImagePullParams) (ima
|
|||
}
|
||||
|
||||
// runPullAndFlatten is the seam tests substitute. nil → real implementation.
|
||||
func (d *Daemon) runPullAndFlatten(ctx context.Context, ref, cacheDir, destDir string) error {
|
||||
func (d *Daemon) runPullAndFlatten(ctx context.Context, ref, cacheDir, destDir string) (imagepull.Metadata, error) {
|
||||
if d.pullAndFlatten != nil {
|
||||
return d.pullAndFlatten(ctx, ref, cacheDir, destDir)
|
||||
}
|
||||
pulled, err := imagepull.Pull(ctx, ref, cacheDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return imagepull.Metadata{}, err
|
||||
}
|
||||
return imagepull.Flatten(ctx, pulled, destDir)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue