···11-# Production Dockerfile with real PKCS#11 support
22-# Build the manager and agent binaries with CGO enabled
33-FROM golang:1.24-alpine AS builder
11+# Multi-stage distroless Dockerfile for maximum security with USB access
22+# Phase 2: Root + Distroless - compensates for root requirement with minimal attack surface
33+44+# Stage 1: Go builder (also serves as dependency source)
55+FROM golang:1.24-bookworm AS builder
46ARG TARGETOS
57ARG TARGETARCH
6877-# Install build dependencies for PKCS#11 and USB event monitoring
88-RUN apk add --no-cache \
99- gcc \
1010- g++ \
1111- eudev-dev \
1212- linux-headers
99+# Install both runtime and development packages in single stage
1010+RUN apt-get update && apt-get install -y \
1111+ opensc \
1212+ pcscd \
1313+ libpcsclite1 \
1414+ libusb-1.0-0 \
1515+ udev \
1616+ ca-certificates \
1717+ libudev-dev \
1818+ libpcsclite-dev \
1919+ libusb-1.0-0-dev \
2020+ && rm -rf /var/lib/apt/lists/*
2121+2222+# Create necessary runtime directories
2323+RUN mkdir -p /var/run/pcscd /var/lock/pcsc && \
2424+ chmod 755 /var/run/pcscd /var/lock/pcsc
13251414-# Return to workspace for Go builds
1526WORKDIR /workspace
1616-1727# Copy the Go Modules manifests
1828COPY go.mod go.mod
1929COPY go.sum go.sum
···2131# and so that source changes don't invalidate our downloaded layer
2232RUN go mod download
23333434+# Copy the go source
2435COPY cmd/ cmd/
2536COPY api/ api/
2637COPY internal/ internal/
2738COPY web/ web/
28392929-# Build unified binary with CGO enabled for PKCS#11 support (agent mode needs it)
4040+# Build with CGO enabled for PKCS#11 support
3041RUN CGO_ENABLED=1 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o hsm-operator cmd/hsm-operator/main.go
31423232-FROM alpine:3.22
3333-RUN apk add --no-cache opensc-dev ccid pcsc-lite openssl libtool libusb ca-certificates eudev
4343+# Stage 2: Base runtime stage with all files
4444+FROM gcr.io/distroless/cc-debian12:debug AS runtime
4545+4646+# Copy essential system files and create nonroot user
4747+COPY --from=builder /etc/passwd /etc/passwd
4848+COPY --from=builder /etc/group /etc/group
4949+5050+# Ensure nonroot user exists (distroless provides user 65532:65532)
5151+5252+# Copy PKCS#11 and USB libraries
5353+COPY --from=builder /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /usr/lib/pkcs11/opensc-pkcs11.so
5454+COPY --from=builder /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 /usr/lib/x86_64-linux-gnu/
5555+COPY --from=builder /usr/lib/x86_64-linux-gnu/libusb-1.0.so.0 /usr/lib/x86_64-linux-gnu/
5656+COPY --from=builder /usr/lib/x86_64-linux-gnu/libudev.so.1 /usr/lib/x86_64-linux-gnu/
5757+COPY --from=builder /usr/lib/x86_64-linux-gnu/libcap.so.2 /usr/lib/x86_64-linux-gnu/
5858+COPY --from=builder /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 /usr/lib/x86_64-linux-gnu/
34593535-WORKDIR /
3636-COPY --from=builder /workspace/hsm-operator .
3737-COPY --from=builder /workspace/web ./web/
3838-COPY entrypoint.sh /entrypoint.sh
3939-RUN chmod +x /entrypoint.sh
6060+# Copy essential binaries
6161+COPY --from=builder /usr/sbin/pcscd /usr/sbin/
6262+COPY --from=builder /usr/bin/pkcs11-tool /usr/bin/
40634141-# Create USB device access groups and add user to them
4242-RUN addgroup -g 20 dialout && \
4343- adduser 65532 dialout && \
4444- addgroup -g 85 usb 2>/dev/null || true && \
4545- adduser 65532 usb 2>/dev/null || true
6464+# Copy udev rules for HSM devices (CCID support)
6565+COPY --from=builder /lib/udev/rules.d/92-libccid.rules /lib/udev/rules.d/
46664747-RUN mkdir -p /var/run/pcscd /var/lock/pcsc && \
4848- chown -R 65532:65532 /var/run/pcscd /var/lock/pcsc && \
4949- chmod 755 /var/run/pcscd /var/lock/pcsc
6767+# Copy CA certificates
6868+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
6969+7070+# Copy runtime directories
7171+COPY --from=builder /var/run/pcscd /var/run/pcscd
7272+COPY --from=builder /var/lock/pcsc /var/lock/pcsc
50737474+# Copy application binary and entrypoint
7575+COPY --from=builder /workspace/hsm-operator /hsm-operator
7676+COPY --chmod=755 entrypoint.sh /entrypoint.sh
7777+7878+# Default to distroless nonroot user (can be overridden by deployment securityContext)
5179USER 65532:65532
52805353-ENTRYPOINT ["/entrypoint.sh"]
8181+# Stage 3: Debug variant with shell (DEFAULT for testing)
8282+FROM runtime
8383+8484+# Debug image includes busybox shell for troubleshooting
8585+# Access via: kubectl exec -it pod -- /busybox/sh
8686+ENTRYPOINT ["/busybox/sh", "/entrypoint.sh"]
+53
Dockerfile.alpine
···11+# Production Dockerfile with real PKCS#11 support
22+# Build the manager and agent binaries with CGO enabled
33+FROM golang:1.24-alpine AS builder
44+ARG TARGETOS
55+ARG TARGETARCH
66+77+# Install build dependencies for PKCS#11 and USB event monitoring
88+RUN apk add --no-cache \
99+ gcc \
1010+ g++ \
1111+ eudev-dev \
1212+ linux-headers
1313+1414+# Return to workspace for Go builds
1515+WORKDIR /workspace
1616+1717+# Copy the Go Modules manifests
1818+COPY go.mod go.mod
1919+COPY go.sum go.sum
2020+# cache deps before building and copying source so that we don't need to re-download as much
2121+# and so that source changes don't invalidate our downloaded layer
2222+RUN go mod download
2323+2424+COPY cmd/ cmd/
2525+COPY api/ api/
2626+COPY internal/ internal/
2727+COPY web/ web/
2828+2929+# Build unified binary with CGO enabled for PKCS#11 support (agent mode needs it)
3030+RUN CGO_ENABLED=1 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o hsm-operator cmd/hsm-operator/main.go
3131+3232+FROM alpine:3.22
3333+RUN apk add --no-cache opensc-dev ccid pcsc-lite openssl libtool libusb ca-certificates eudev
3434+3535+WORKDIR /
3636+COPY --from=builder /workspace/hsm-operator .
3737+COPY --from=builder /workspace/web ./web/
3838+COPY entrypoint.sh /entrypoint.sh
3939+RUN chmod +x /entrypoint.sh
4040+4141+# Create USB device access groups and add user to them
4242+RUN addgroup -g 20 dialout && \
4343+ adduser 65532 dialout && \
4444+ addgroup -g 85 usb 2>/dev/null || true && \
4545+ adduser 65532 usb 2>/dev/null || true
4646+4747+RUN mkdir -p /var/run/pcscd /var/lock/pcsc && \
4848+ chown -R 65532:65532 /var/run/pcscd /var/lock/pcsc && \
4949+ chmod 755 /var/run/pcscd /var/lock/pcsc
5050+5151+USER 65532:65532
5252+5353+ENTRYPOINT ["/entrypoint.sh"]
+10-2
Makefile
···33# To re-generate a bundle for another specific version without changing the standard setup, you can:
44# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
55# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
66-VERSION ?= 0.6.16
66+VERSION ?= 0.6.17
7788# CHANNELS define the bundle channels used in the bundle.
99# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
···237237# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
238238# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
239239.PHONY: docker-build
240240-docker-build: ## Build production docker image with PKCS#11 support.
240240+docker-build: ## Build debug distroless image (root + busybox shell) - default for testing.
241241 $(CONTAINER_TOOL) build -t ${IMG} .
242242+243243+.PHONY: docker-build-production
244244+docker-build-production: ## Build production distroless image (root + no shell).
245245+ $(CONTAINER_TOOL) build --target production -t ${IMG}-production .
246246+247247+.PHONY: docker-build-alpine
248248+docker-build-alpine: ## Build Alpine-based image (fallback option).
249249+ $(CONTAINER_TOOL) build -f Dockerfile.alpine -t ${IMG}-alpine .
242250243251.PHONY: docker-push
244252docker-push: ## Push docker image with the manager.
···11-#!/bin/sh
21set -e
3243# Debug: Show user and USB device permissions for agent mode only
···2322 fi
24232524 # Start pcscd with debug output
2525+ # Use /tmp for runtime files if root filesystem is readonly
2626 echo "Starting pcscd..."
2727 pcscd -f -d -a &
2828 PCSCD_PID=$!
+2-2
helm/hsm-secrets-operator/Chart.yaml
···22name: hsm-secrets-operator
33description: A Kubernetes operator that bridges Pico HSM binary data storage with Kubernetes Secrets
44type: application
55-version: 0.6.16
66-appVersion: v0.6.16
55+version: 0.6.17
66+appVersion: v0.6.17
77icon: https://raw.githubusercontent.com/cncf/artwork/master/projects/kubernetes/icon/color/kubernetes-icon-color.svg
88home: https://github.com/evanjarrett/hsm-secrets-operator
99sources: