Conversation
0000f94 to
000034e
Compare
mkrootfs.sh has only ever extracted the bundled Alpine minirootfs tarball into a staging directory and fed that to mke2fs -d. This change adds a --image=docker://... mode so the same staging path can be populated from an arbitrary OCI image hosted on a v2 registry, then a --rewrite-uid mode that restores tar-header uid/gid/mode into the resulting ext4 inodes. The implementation has three pieces: scripts/oci-pull.py is a stdlib-only Python helper that resolves manifest lists by host arch, fetches layer blobs with bearer-token auth, applies them to a directory honoring whiteouts (.wh.NAME and .wh..wh..opq), hardlinks (archive-root or parent-relative ustar form), and OCI symlinks. Layer blobs are content-addressed and cached under $XDG_CACHE_HOME/kbox/oci-layers, with atomic-rename writes and verify-on-read so a corrupted cache self-heals. tools/oci-chown is an in-tree libext2fs helper that opens the ext4 image read-write, walks a NUL-separated manifest emitted by oci-pull.py (uid TAB gid TAB mode TAB path), and rewrites each inode's uid/gid hi+lo halves and i_mode permission bits via ext2fs_namei + ext2fs_read_inode/write_inode. The helper builds with its own Makefile (-lext2fs -lcom_err) and is built on demand by mkrootfs.sh; the kbox supervisor build is unchanged. scripts/mkrootfs.sh grows --image=URL, --size=MB, and --rewrite-uid flags. Backward compat for the positional SIZE_MB argument is preserved. When --rewrite-uid is set, oci-pull.py emits a manifest into a temp file, mke2fs runs as before, and oci-chown rewrites the inodes from the manifest before mkrootfs declares the image ready. The layer-apply path is the principal attack surface and is hardened against malicious images: - safe_join rejects '..' and absolute paths in tar member names. - Each member's parent directory is realpath-checked against the staging root before any write/unlink/chmod, so an in-staging symlink (e.g. layer 1 creates etc -> /etc, layer 2 writes etc/passwd) cannot redirect onto the host. - File writes use O_NOFOLLOW; pre-existing symlinks at the destination are unlinked first. - Hardlink targets are resolved through safe_join (and a parent- relative variant for ustar tarballs that also runs through safe_join after normalization), absolute linknames are rejected, the resolved source is realpath-confined, and os.link is called with follow_symlinks=False so a staging-resident symlink cannot redirect the link to a host file. - Bearer tokens are stripped on cross-host redirects compared by netloc, not just hostname, so a port-change redirect on the same host also drops the token. - DoS caps: MAX_MANIFEST_BYTES=4 MB, MAX_BLOB_BYTES=8 GB, MAX_TAR_MEMBERS=500_000. - Every blob is sha256-verified in flight; cache reads re-verify. oci-chown's manifest parser range-checks uid/gid (0..UINT32_MAX), rejects leading sign or whitespace, rejects mode bits outside 07777, and dies loudly on a manifest tail missing the trailing NUL. A new oci-image-import CI job pulls nginx:alpine end-to-end on every PR, asserts oci-chown reports a non-zero rewrite count, and verifies that /etc/nginx/nginx.conf ends up User=0/Group=0 and /usr/sbin/nginx mode is 0755. Verified on x86_64 (node1) and aarch64 (arm) with alpine:3.21 (185 inodes rewritten) and node:alpine (~2880 inodes; /home/node round- trips at User=1000/Group=1000). Integration suite shows parity with the baseline tarball rootfs. Close #17 Change-Id: Ic260ccce778a1de0875fc466cc8fde4c14e301a8
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.
mkrootfs.sh has only ever extracted the bundled Alpine minirootfs tarball into a staging directory and fed that to mke2fs -d. This change adds a --image=docker://... mode so the same staging path can be populated from an arbitrary OCI image hosted on a v2 registry, then a --rewrite-uid mode that restores tar-header uid/gid/mode into the resulting ext4 inodes.
The implementation has three pieces:
scripts/oci-pull.py is a stdlib-only Python helper that resolves manifest lists by host arch, fetches layer blobs with bearer-token auth, applies them to a directory honoring whiteouts (.wh.NAME and .wh..wh..opq), hardlinks (archive-root or parent-relative ustar form), and OCI symlinks. Layer blobs are content-addressed and cached under $XDG_CACHE_HOME/kbox/oci-layers, with atomic-rename writes and verify-on-read so a corrupted cache self-heals.
tools/oci-chown is an in-tree libext2fs helper that opens the ext4 image read-write, walks a NUL-separated manifest emitted by oci-pull.py (uid TAB gid TAB mode TAB path), and rewrites each inode's uid/gid hi+lo halves and i_mode permission bits via ext2fs_namei + ext2fs_read_inode/write_inode. The helper builds with its own Makefile (-lext2fs -lcom_err) and is built on demand by mkrootfs.sh; the kbox supervisor build is unchanged.
scripts/mkrootfs.sh grows --image=URL, --size=MB, and --rewrite-uid flags. Backward compat for the positional SIZE_MB argument is preserved. When --rewrite-uid is set, oci-pull.py emits a manifest into a temp file, mke2fs runs as before, and oci-chown rewrites the inodes from the manifest before mkrootfs declares the image ready.
The layer-apply path is the principal attack surface and is hardened against malicious images:
oci-chown's manifest parser range-checks uid/gid (0..UINT32_MAX), rejects leading sign or whitespace, rejects mode bits outside 07777, and dies loudly on a manifest tail missing the trailing NUL.
A new oci-image-import CI job pulls nginx:alpine end-to-end on every PR, asserts oci-chown reports a non-zero rewrite count, and verifies that /etc/nginx/nginx.conf ends up User=0/Group=0 and /usr/sbin/nginx mode is 0755.
Verified on x86_64 (node1) and aarch64 (arm) with alpine:3.21 (185 inodes rewritten) and node:alpine (~2880 inodes; /home/node round- trips at User=1000/Group=1000). Integration suite shows parity with the baseline tarball rootfs.
Close #17
Change-Id: Ic260ccce778a1de0875fc466cc8fde4c14e301a8
Summary by cubic
Adds rootless OCI image import to the rootfs build.
scripts/mkrootfs.shcan now pulldocker://...images and optionally restore tar-header ownership into ext4 inodes.New Features
scripts/mkrootfs.sh: adds--image=docker://...,--rewrite-uid, and--size=MB(positional size still supported); Alpine tarball remains default; buildstools/oci-chownon demand; validates size; requirespython3for--image.scripts/oci-pull.py: stdlib-only puller with bearer auth; resolves multi-arch; applies layers (whiteouts, hardlinks, symlinks); content-addressed cache under$XDG_CACHE_HOME/kbox/oci-layerswith sha256 verify andprune.tools/oci-chown:libext2fshelper that rewrites inode uid/gid/mode from a manifest; required for--root-idguests; supervisor build unchanged.O_NOFOLLOW, strict hardlink handling (follow_symlinks=False), token stripping on cross-host redirects, DoS caps, and digest verification.docs/oci-image-import.mdand README usage.CI
oci-image-importjob pullsnginx:alpine, asserts non-zero inode rewrites, and verifies/etc/nginx/nginx.confowner (0:0) and/usr/sbin/nginxmode (0755).Written for commit 00008c5. Summary will update on new commits. Review in cubic