Repository navigation
Expand file tree
/
Copy pathDockerfile.release
More file actions
99 lines (87 loc) · 5.42 KB
/
Copy pathDockerfile.release
File metadata and controls
99 lines (87 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# checkov:skip=CKV_DOCKER_4:Remote official artifacts use ADD --checksum for mandatory SHA256 verification.
# Release image assembled by GoReleaser (dockers_v2) from the prebuilt
# `portwing` binaries — one image per target platform from this single
# Dockerfile. (For a from-source build use the top-level Dockerfile instead.)
#
# Runtime packages come from Wolfi (Chainguard) on amd64/arm64 and from Alpine
# on armv7 — Wolfi has no armv7. buildx selects the per-arch base via the
# predefined TARGETARCH arg. Both arches use the same staging-rootfs → FROM
# scratch approach: packages install into /out, which the final scratch image
# copies in, so the apk *binary* never reaches the runtime (CVE-minimal, no
# package manager) while the apk database is retained for vuln scanners / SBOM.
#
# GoReleaser stages the per-platform binary under $TARGETPLATFORM/portwing in
# the build context (linux/amd64/portwing, linux/arm64/portwing,
# linux/arm/v7/portwing).
# Compose v5.5.1 with the containerd security patch, isolated from Portwing modules.
FROM --platform=$BUILDPLATFORM golang:1.27.1-alpine@sha256:cf6fca6641884b8433441b2b0652976f975e1d0fdd26d177eaaf8596087f3125 AS compose-builder
ADD --checksum=sha256:c72877db37172d8ee55f565e4fed20067af89015e986b190768fd4ee621025f2 https://github.com/docker/compose/archive/5f94fb0aa42a2cd1248c6e6c7fafb87546b9c8de.tar.gz /tmp/compose.tar.gz
WORKDIR /compose
RUN tar -xzf /tmp/compose.tar.gz --strip-components=1 \
&& go get github.com/containerd/containerd/v2@v2.3.6 \
&& go mod verify \
&& CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -trimpath -tags e2e \
-ldflags="-w -X github.com/docker/compose/v5/internal.Version=v5.5.1-portwing.2" \
-o /docker-compose ./cmd
# Wolfi rootfs — amd64 and arm64 share one recipe; buildx pulls the per-arch
# wolfi-base via --platform, so the arm64 build of this stage installs arm64
# packages even though the stage is named "-amd64".
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:65e1acb87a2bf356b92c5f70f3980f03b4bb51dfd483c834e01557525f15c1d9 AS rootfs-amd64
RUN apk add --no-cache --initdb --root /out \
--repository https://packages.wolfi.dev/os \
--keys-dir /etc/apk/keys \
ca-certificates-bundle busybox docker-cli docker-compose wget \
&& echo 'portwing:x:65532:65532:portwing:/home/portwing:/sbin/nologin' >>/out/etc/passwd \
&& echo 'portwing:x:65532:' >>/out/etc/group \
&& install -d -o 65532 -g 65532 /out/home/portwing /out/data/stacks \
&& rm -rf /out/var/cache/apk/*
FROM rootfs-amd64 AS rootfs-arm64
# Alpine rootfs — armv7 only (Wolfi has no armv7). The branch (vMAJOR.MINOR) is
# derived from the base image so the FROM tag is the single source of truth for
# the version: Dependabot bumps the tag and the repositories follow.
FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b AS rootfs-arm
RUN ALPINE_BRANCH="v$(cut -d. -f1,2 /etc/alpine-release)" \
&& apk add --no-cache --initdb --root /out \
--repository "https://dl-cdn.alpinelinux.org/alpine/${ALPINE_BRANCH}/main" \
--repository "https://dl-cdn.alpinelinux.org/alpine/${ALPINE_BRANCH}/community" \
--keys-dir /etc/apk/keys \
ca-certificates-bundle alpine-release \
&& mkdir -p /out/etc /out/usr/bin \
&& echo 'portwing:x:65532:65532:portwing:/home/portwing:/sbin/nologin' >>/out/etc/passwd \
&& echo 'portwing:x:65532:' >>/out/etc/group \
&& install -d -o 65532 -g 65532 /out/home/portwing /out/data/stacks \
&& rm -rf /out/var/cache/apk/*
# Static ARMv7 clients avoid Alpine's older bundled Go dependencies.
ADD --checksum=sha256:e5fc27c730a503192c2bcd5462f9316a0626c6b62d7b8cff9fe41116f740b0aa https://download.docker.com/linux/static/stable/armhf/docker-29.8.0.tgz /tmp/docker.tgz
COPY --from=compose-builder /docker-compose /out/usr/bin/docker-compose
COPY --from=compose-builder /compose/LICENSE /out/usr/share/licenses/docker-compose/LICENSE
RUN tar -xzf /tmp/docker.tgz -C /out/usr/bin --strip-components=1 docker/docker \
&& chmod 755 /out/usr/bin/docker /out/usr/bin/docker-compose \
&& mkdir -p /out/usr/libexec/docker/cli-plugins \
&& ln -s ../../../bin/docker-compose /out/usr/libexec/docker/cli-plugins/docker-compose \
&& rm /tmp/docker.tgz
# Select the rootfs by target arch. TARGETARCH is a buildx-predefined global arg
# (amd64 / arm64 / arm) usable directly in FROM without an explicit ARG.
FROM rootfs-${TARGETARCH} AS rootfs
# Final image: selected rootfs plus the prebuilt binary. Runs as the dedicated
# `portwing` user (UID 65532); reaching the host Docker socket requires adding
# the socket's group at deploy time via group_add / --group-add (see examples/
# and SECURITY.md).
FROM scratch
COPY --from=rootfs /out /
# TARGETPLATFORM is a global-scope predefined arg; redeclare it inside this
# stage so the COPY below can resolve the per-platform binary subdirectory.
ARG TARGETPLATFORM
COPY $TARGETPLATFORM/portwing /usr/bin/portwing
# DOCKER_CONFIG points at the /tmp tmpfs so `docker login` during compose
# deploys can write config.json under a read-only root filesystem.
ENV HOME=/home/portwing \
DOCKER_CONFIG=/tmp/.docker
USER 65532:65532
# /data/stacks is pre-created owned by 65532 in the rootfs stages, so volumes
# initialized from it are writable by the non-root user.
VOLUME /data/stacks
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD ["/usr/bin/portwing", "healthcheck"]
ENTRYPOINT ["/usr/bin/portwing"]