stacklok / stacklok/go-microvm
Layered extraction fails when layer replaces symlink with real file
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 25
- Forks
- 5
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Layered OCI image extraction fails with refusing to write through symlink when a higher layer legitimately replaces a symlink (created by a lower layer) with a real file. Extraction falls back to flat mode, losing the layer caching benefit.
Reproduction
Build a layered Alpine image where the base layer has busybox (which creates /usr/bin/env as a symlink) and a subsequent layer installs coreutils (which replaces /usr/bin/env with a real binary):
# base image
FROM alpine:3.21
RUN apk add --no-cache coreutils findutils
When propolis extracts this image with layered extraction enabled, layer 1 (the apk add layer) tries to write the real coreutils /usr/bin/env over the busybox symlink from layer 0 and hits the symlink traversal guard:
level=WARN msg="layered extraction failed, falling back to flat extraction"
err="apply layer 1 (sha256:6a4114a...): copy file usr/bin/env: refusing to write through symlink: /path/to/cache/tmp-rootfs-.../usr/bin/env"
Expected behavior
Layered extraction should handle the case where a layer replaces an existing symlink with a real file. The symlink traversal protection should detect that the target path is the symlink itself (not traversing through one to reach a different location) and allow the replacement.
Context
This was discovered in waggle after upgrading to propolis v0.0.15 and introducing a shared base image with coreutils. The flat extraction fallback works correctly — VMs boot and run fine — but layer-level caching is not effective since every extraction falls back to flat mode.
Environment
- propolis v0.0.15
- Alpine 3.21 base images
coreutilspackage triggers the symlink replacement
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the layered OCI image extraction path and the symlink traversal guard that reports “refusing to write through symlink.” Reproduce the issue with the Alpine 3.21 image described here, then verify that replacing the existing /usr/bin/env symlink with a real file succeeds without falling back to flat extraction and preserves layer caching.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, go
- Domain
- infrastructure
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100