Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
155 lines (146 loc) · 7.8 KB
/
Copy pathDockerfile
File metadata and controls
155 lines (146 loc) · 7.8 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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# syntax=docker/dockerfile:1.28
# Unified multi-target Dockerfile for all pillar-csi components.
#
# Targets:
# controller — CSI controller (distroless, no runtime deps)
# agent — pillar-agent gRPC server (alpine + ZFS + LVM2 + NFS server)
# node — CSI node plugin (alpine + mount/NFS utils + e2fsprogs + xfsprogs)
#
# Usage:
# docker buildx bake # build all 3 images
# docker buildx bake controller # build controller only
# docker build --target=agent . # build agent only
#
# Security posture (all targets):
# - Non-root default user: UID/GID 65532 ("nonroot" convention)
# - Statically-linked Go binary (CGO_ENABLED=0), -trimpath
# - Versions pinned; update SHA digests when bumping
#
# See docker-bake.hcl for the build matrix and CI integration.
# ── Builder stage ─────────────────────────────────────────────────────────────
FROM --platform=$BUILDPLATFORM golang:1.27.2-alpine3.24 AS builder
WORKDIR /workspace
# Download dependencies first to maximise build-cache hits on source changes.
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ARG TARGETOS
ARG TARGETARCH
# TARGETVARIANT is set by buildx for sub-architecture levels (e.g. "v3" for
# linux/amd64/v3, "v7" for linux/arm/v7, "v8.2" for linux/arm64/v8.2). Empty
# for the architecture default (linux/amd64, linux/arm64).
ARG TARGETVARIANT
# Build all binaries under cmd/ in one invocation. The -o flag with a
# directory target writes each binary (named after its package directory)
# into that directory. This works with cross-compilation unlike go install
# which rejects GOBIN when GOOS/GOARCH differ from the host.
#
# Map the BuildKit TARGETVARIANT to the matching Go toolchain env var so each
# manifest in the multi-platform image carries a binary tuned for that
# instruction-set level:
# amd64/v1..v4 → GOAMD64 (Go 1.21+; default v1)
# arm/v6,v7 → GOARM (strip the leading 'v')
# arm64/v8.0..v9.5 → GOARM64 (Go 1.23+; default v8.0)
RUN --mount=type=cache,target=/root/.cache/go-build set -eu; \
GOAMD64=""; GOARM=""; GOARM64=""; \
case "$TARGETARCH" in \
amd64) GOAMD64="${TARGETVARIANT:-v1}" ;; \
arm) GOARM="${TARGETVARIANT#v}" ;; \
arm64) GOARM64="${TARGETVARIANT:-v8.0}" ;; \
esac; \
CGO_ENABLED=0 GOOS="${TARGETOS:-linux}" GOARCH="${TARGETARCH}" \
GOAMD64="$GOAMD64" GOARM="$GOARM" GOARM64="$GOARM64" \
go build \
-trimpath \
-pgo=auto \
-ldflags="-s -w" \
-o /workspace/bin/ \
./cmd/...
# ── Runtime: controller ───────────────────────────────────────────────────────
# Distroless — no shell, no package manager, minimal attack surface.
FROM gcr.io/distroless/static:nonroot AS controller
COPY --from=builder --link /workspace/bin/controller /usr/bin/manager
USER 65532:65532
ENTRYPOINT ["/usr/bin/manager"]
# ── Runtime: agent ────────────────────────────────────────────────────────────
# Alpine + ZFS + LVM2 + nfs-utils userspace tools. The agent invokes zfs(8),
# zpool(8), lvcreate(8), lvremove(8), rpc.mountd(8) and nfsdcld(8), plus
# exportfs(8) from its relocated /usr/libexec/pillar-csi path (see below).
# NVMe-oF uses configfs directly; NFS uses the host kernel nfsd and a supervised
# userspace NFSv4 client-recovery daemon with private persistent state.
#
# Runtime security (enforced in the DaemonSet manifest):
# --security-opt=no-new-privileges:true
# --read-only (combine with tmpfs mounts for /tmp, /run)
# --cap-drop ALL --cap-add SYS_ADMIN (ZFS + configfs need SYS_ADMIN)
FROM alpine:3.24.2 AS agent
RUN set -eux \
&& apk add --no-cache 'zfs~=2.4' lvm2 nfs-utils blkid \
# OpenZFS libshare runs the hard-coded `/usr/sbin/exportfs -ra` after every
# zfs create/mount/destroy whenever /usr/sbin/exportfs exists
# (lib/libshare/os/linux/nfs.c), rewriting /var/lib/nfs/etab from the empty
# /etc/exports and wiping pillar's export rows. Move exportfs out of that
# path so libzfs treats NFS sharing as unavailable (a no-op commit) while
# the agent keeps calling it via its absolute relocated path. The `test`
# is the build-time guard: this is the final agent layer, apk is removed
# below, and no later instruction reinstalls nfs-utils.
&& install -d -m 0755 /usr/libexec/pillar-csi \
&& mv /usr/sbin/exportfs /usr/libexec/pillar-csi/exportfs \
&& test ! -e /usr/sbin/exportfs \
# Configure LVM for container environments where udevd is not running.
&& sed -i 's/obtain_device_list_from_udev = 1/obtain_device_list_from_udev = 0/' /etc/lvm/lvm.conf \
&& sed -i 's/udev_sync = 1/udev_sync = 0/' /etc/lvm/lvm.conf \
&& sed -i 's/udev_rules = 1/udev_rules = 0/' /etc/lvm/lvm.conf \
&& addgroup -g 65532 nonroot \
&& adduser -u 65532 -G nonroot -s /sbin/nologin -D nonroot \
# Strip SUID/SGID bits from every file on the root filesystem.
&& (find / -xdev \( -perm -4000 -o -perm -2000 \) -exec chmod a-s {} + 2>/dev/null || true) \
# Remove package manager (prevents `apk add` at runtime).
&& rm -rf /sbin/apk /etc/apk /lib/apk /usr/share/apk /var/lib/apk \
# Remove shell (no interactive escape path).
&& rm -f /bin/sh /bin/bash /usr/bin/env
COPY --from=builder --link --chmod=0555 /workspace/bin/agent /usr/bin/pillar-agent
USER 65532:65532
EXPOSE 2049/tcp
EXPOSE 9500
ENTRYPOINT ["/usr/bin/pillar-agent"]
# ── Runtime: node ─────────────────────────────────────────────────────────────
# Alpine + mount utilities (util-linux) + ext4/XFS formatting and resize tools
# (e2fsprogs, xfsprogs). xfsprogs-extra carries xfs_growfs on Alpine, and the
# mkfs.xfs LTS profiles under /usr/share/xfsprogs/mkfs: the node plugin formats
# XFS with the Linux 5.15 one so every supported node kernel can mount the
# volume. hack/verify-mkfs-baseline.sh fails the build when the image's mkfs
# would create a filesystem Linux 5.15 cannot mount.
# device-mapper carries dmsetup, which holds the backend device of a local
# attach on the storage node through a linear target.
# nfs-utils supplies mount.nfs for NFSv4.2 stages without host package installs.
#
# Runtime security (enforced in the DaemonSet manifest):
# --security-opt=no-new-privileges:true
# --read-only (combine with tmpfs mounts for /tmp, /run)
# --cap-drop ALL --cap-add SYS_ADMIN (mount(8) and NVMe-oF need SYS_ADMIN)
FROM alpine:3.24.2 AS node
RUN --mount=type=bind,source=hack/verify-mkfs-baseline.sh,target=/tmp/verify-mkfs-baseline.sh \
set -eux \
&& apk add --no-cache \
'util-linux~=2.42' \
'e2fsprogs~=1.47' \
'e2fsprogs-extra~=1.47' \
'xfsprogs~=7.0' \
'xfsprogs-extra~=7.0' \
'device-mapper~=2.03' \
'quota-tools~=4.11' \
nfs-utils; \
# A separate command: the "|| true" below would mask a failure in the chain.
sh /tmp/verify-mkfs-baseline.sh; \
addgroup -g 65532 nonroot \
&& adduser -u 65532 -G nonroot -s /sbin/nologin -D nonroot \
# Strip SUID/SGID bits from every file on the root filesystem.
&& find / -xdev \( -perm -4000 -o -perm -2000 \) -exec chmod a-s {} + 2>/dev/null || true \
# Remove package manager (prevents `apk add` at runtime).
&& rm -rf /sbin/apk /etc/apk /lib/apk /usr/share/apk /var/lib/apk \
# Remove shell (no interactive escape path).
&& rm -f /bin/sh /bin/bash /usr/bin/env
COPY --from=builder --link --chmod=0555 /workspace/bin/node /usr/bin/pillar-node
USER 65532:65532
ENTRYPOINT ["/usr/bin/pillar-node"]