banger/docs/oci-import.md

5.4 KiB
Raw Permalink Blame History

OCI import (banger image pull)

banger image pull has two paths. The primary one — catalog bundle — is documented in docs/image-catalog.md. This doc covers the fallthrough: OCI-registry pull for arbitrary container images.

When to use it

Use the OCI path when you need a distro or image that isn't in the catalog. The catalog covers the common happy path (debian-bookworm); anything else (alpine, fedora, ubuntu, custom corporate images) goes through OCI pull.

banger image pull docker.io/library/alpine:3.20 --kernel-ref generic-6.12
banger image pull ghcr.io/myorg/devimg:v2        --kernel-ref generic-6.12

banger image pull dispatches based on the reference:

  • banger image pull debian-bookworm → catalog (fast path).
  • banger image pull docker.io/library/foo:bar → OCI (anything not in the catalog).

What works

  • Any public OCI image that exposes a linux/amd64 manifest.
  • Correct layer replay with whiteout semantics (.wh.* deletes, .wh..wh..opq opaque-dir markers).
  • Path-traversal, debugfs-hostile filename, and relative-symlink-escape protection.
  • Content-aware default sizing (content × 1.5, floor 1 GiB).
  • Layer caching on disk, keyed by blob sha256.
  • Ownership preservation — tar-header uid/gid/mode captured during flatten, applied to the ext4 via a debugfs pass, so setuid binaries (sudo, passwd) and root-owned config (/etc/shadow, /etc/sudoers) end up correctly owned.
  • Pre-injected banger agents — the pulled ext4 ships with banger-vsock-agent, banger-network.service, and the banger-first-boot unit already enabled.
  • First-boot sshd install — a one-shot systemd service installs openssh-server via the guest's package manager on first boot. Dispatches on /etc/os-releaseapt-get / apk / dnf / pacman / zypper. Subsequent boots skip the install.

What doesn't yet work

  • Private registries. Anonymous pulls only. Docker Hub, GHCR (public), quay.io (public) all work. Adding auth via authn.DefaultKeychain (from go-containerregistry) is a cheap follow-up when someone needs it.
  • Non-linux/amd64. The kernel catalog is x86_64-only, so pulled rootfses match. arm64 is additive in the schema.
  • Non-systemd rootfses. The injected units assume systemd as PID 1. Alpine ≥3.20 ships systemd; older alpine + void + busybox- init images won't honour the banger-* units.
  • First boot needs network access. The first-boot sshd install reaches out to the distro's package repo. VMs without NAT or without the bridge reaching the internet time out. The marker file stays in place so a later restart retries.

Architecture

Implementation details live in docs/oci-import-internals.md.

Guest-side boot sequence

On first boot of a pulled image:

  1. banger-network.service — brings the guest interface up with the IP assigned by banger's VM-create lifecycle.
  2. banger-first-boot.service (first boot only) — reads /etc/os-release, dispatches to the native package manager, installs openssh-server, enables ssh.service.
  3. banger-vsock-agent.service — the health-check daemon banger uses to confirm the VM is alive.

Subsequent boots skip step 2.

Adding distro support to first-boot

internal/imagepull/assets/first-boot.sh is the POSIX-sh dispatch. Add a new ID= branch and its install command, then rebuild banger (the asset is go:embed-ed).

Supported ID values today: debian, ubuntu, kali, raspbian, linuxmint, pop, alpine, fedora, rhel, centos, rocky, almalinux, arch, archlinux, manjaro, opensuse*, suse. Unknown distros fall back to ID_LIKE, then error cleanly.

Paths

Paths below assume the system install (banger system install). When running bangerd directly without the helper, the same files live under ~/.cache/banger/ and ~/.local/state/banger/ instead.

What Where
Layer blob cache /var/cache/banger/oci/blobs/sha256/<hex>
Staging dir /var/lib/banger/images/<id>.staging/
Extraction scratch $TMPDIR/banger-pull-<rand>/
Published image /var/lib/banger/images/<id>/rootfs.ext4

Cache lifecycle

OCI layer blobs accumulate as you pull images. Banger flattens every pull into a self-contained ext4, so the cache is purely a re-pull avoidance — losing it only costs network round-trips on the next pull of the same image. Reclaim disk with:

banger image cache prune --dry-run   # report size only
banger image cache prune             # remove every cached blob

Run with the daemon idle; an in-flight pull racing against prune may fail and need a retry.

Tech debt

  • Auth. When we add private-registry support, the natural path is authn.DefaultKeychain, which honours ~/.docker/config.json and the standard credential helpers.
  • Non-systemd rootfses. The guest agents assume systemd. Adding openrc / s6 / busybox-init variants means keeping parallel unit trees keyed on /etc/os-release.

Trust model

image pull (OCI path) delegates trust to the registry the user selected. go-containerregistry verifies layer digests against the manifest during download, so a tampered mirror can't ship modified layers without breaking the sha256 chain. Banger does not verify OCI image signatures (cosign/sigstore) — users who care should verify references out-of-band.