GoogleCloudPlatform / GoogleCloudPlatform/cloud-sql-proxy
Alpine image: linux/arm64 variant ships an x86-64 userland because Dockerfile.alpine pins an amd64-specific base digest
- Dominant language
- Go
- Stars
- 1.4k
- Forks
- 352
- Avg merge
- 14h 54m
- Merged PRs (30d)
- 5
Description
### Bug Description
The linux/arm64 variant of the Alpine container image ships an x86-64 userland. The proxy binary itself is correctly cross-compiled for arm64, but busybox, apk, and everything else is x86-64. On arm64 nodes, `/bin/sh` and all other tools fail with `exec format error`, and the proxy's `--fuse` mode is broken because `fusermount` is an x86-64 binary.
Root cause: the final stage of `Dockerfile.alpine` pins `alpine:3` to an amd64-specific image manifest digest instead of the multi-arch index digest. Digest pinning bypasses buildx platform selection, so the `--platform linux/arm64` build silently uses the amd64 rootfs. The pin is unchanged at v2.24.1, so current releases are affected.
The removed bookworm variant (#2630) had the identical defect (see step 3 below), which suggests the digest-update automation systematically resolves platform manifest digests instead of index digests. Suggested fix: pin the index digest for `alpine:3` (the tag already publishes linux/arm64 in its manifest list), and fix the automation so the pin does not regress on the next update.
### Example code (or command)
```dockerfile
# Dockerfile.alpine (unchanged as of v2.24.1) — final stage pins an
# amd64-only manifest digest:
FROM alpine:3@sha256:79ff19e9084a00eece421b2523fb93e22d730e2c0e525905de047e848e56d95f
```
```sh
# Runtime symptom on an arm64 node:
$ kubectl exec -it -c cloud-sql-proxy -- /bin/sh
exec /bin/sh: exec format error
```
### Stacktrace
```bash
```
### Steps to reproduce?
No arm64 hardware is required; everything below inspects the published images with `crane`.
1. Compare the base rootfs layer of the amd64 and arm64 manifests. They are identical, which is impossible for a correct multi-arch build:
```sh
IMG=gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.23-alpine
crane manifest $IMG | jq -r '.manifests[] | select(.platform.os == "linux") | "\(.platform.architecture) \(.digest)"'
# then, for each digest:
crane manifest gcr.io/cloud-sql-connectors/cloud-sql-proxy@ | jq -r '.layers[0].digest'
# Observed:
# amd64 layers[0] = sha256:55afa1ecc21d2bb5e5045f32dafee56272ffd89860bac26f6c32123439af26a4
# arm64 layers[0] = sha256:55afa1ecc21d2bb5e5045f32dafee56272ffd89860bac26f6c32123439af26a4
# (the alpine 3.24.1 x86_64 minirootfs layer)
```
2. Confirm that busybox in the arm64 image is an x86-64 ELF:
```sh
$ crane export --platform linux/arm64 $IMG - | tar -xO bin/busybox | head -c 20 | xxd
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0300 3e00 ..>.
# e_machine = 0x3e (EM_X86_64); aarch64 would be 0xb7
```
3. (Optional) Confirm the same pattern in the removed bookworm variant:
```sh
# 2.23-bookworm shares its base layer across platforms:
# amd64 (sha256:06b15bd1b81bb27956d45da63d574010258ca14f605dbc79e446d6b742873461)
# layers[0] = sha256:20face757db20cd2eddf8149ec7d6b4f1630481ae00d25ad6396bcc5bef5b082
# arm64 (sha256:d80cd7c7689e6e56d76b8f4248513910cc09747eb66d8a91b3e5aef343bbe16a)
# layers[0] = sha256:20face757db20cd2eddf8149ec7d6b4f1630481ae00d25ad6396bcc5bef5b082
```
4. (Optional) On any arm64 host, run a shell in the image to observe
the runtime failure:
```sh
docker run --rm -it --entrypoint /bin/sh gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.23-alpine
# exec /bin/sh: exec format error
```
### Environment
- Image: `gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.23-alpine` (`linux/arm64` variant); `Dockerfile.alpine` carries the same pin at `v2.24.1`, so current releases are affected as well
- Host: any linux/arm64 node (e.g. GKE arm64 node pools); the defect is also verifiable host-independently with `crane` as above
- Tools used for verification: `crane`, `jq`, `xxd`
### Additional Details
_No response_
Contributor guide
Research direction
Start with Dockerfile.alpine and inspect the final alpine:3 digest, then use crane manifest to distinguish the platform manifest from the multi-arch index. Trace the digest-update automation mentioned in the issue and verify it preserves index digests. Done means the linux/arm64 image contains an arm64 Alpine rootfs and the pin does not regress on the next update.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, dockerfile
- Domain
- build-system, devops, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100