From 1815f622248f4585dba8b563b6bb8ea696ebc58a Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 00:29:47 -0700 Subject: [PATCH 01/19] feat(docker): move to docker hardened image swaps the docker image to use [Docker Hardened Image](https://www.docker.com/products/hardened-images/) as the base, which has fewer CVEs and less attack surface --- docker/Dockerfile | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d869e63e0e..13d43a14d2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -65,20 +65,35 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /build/arcane \ ./cmd/main.go -# Stage 3: Production Image -FROM debian:trixie-slim AS runner +# Stage 3a: Runtime deps (apt-enabled) +FROM debian:trixie-slim AS runner-deps ARG TARGETARCH -ARG VERSION -ARG REVISION RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates curl tzdata tar gzip \ - vainfo clinfo libdrm2 libsystemd0 \ + && apt-get install -y --no-install-recommends \ + ca-certificates curl tzdata tar gzip \ + vainfo clinfo libdrm2 libsystemd0 \ && if [ "$TARGETARCH" = "amd64" ]; then \ apt-get install -y --no-install-recommends intel-gpu-tools; \ fi \ && apt-get clean && rm -rf /var/lib/apt/lists/* +# Stage 3b: Hardened Production Image (no apt) +ARG HARDENED_BASE_IMAGE=debian:trixie-slim +FROM ${HARDENED_BASE_IMAGE} AS runner-hardened + +ARG TARGETARCH +ARG VERSION +ARG REVISION + +# Copy runtime deps from runner-deps instead of apt-get +COPY --from=runner-deps /etc/ssl/certs/ /etc/ssl/certs/ +COPY --from=runner-deps /usr/share/zoneinfo/ /usr/share/zoneinfo/ +COPY --from=runner-deps /usr/bin/ /usr/bin/ +COPY --from=runner-deps /usr/lib/ /usr/lib/ +COPY --from=runner-deps /lib/ /lib/ +COPY --from=runner-deps /lib64/ /lib64/ +# keep the same env/settings as existing runner ENV GIN_MODE=release ENV PORT=3552 ENV ENVIRONMENT=production @@ -89,13 +104,10 @@ ENV NVIDIA_VISIBLE_DEVICES=all \ ONEAPI_DEVICE_SELECTOR=level_zero:*,opencl:* \ LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/opt/rocm/lib:/opt/intel/oneapi/lib:/opt/intel/oneapi/lib/intel64:/usr/lib/x86_64-linux-gnu:/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib:/lib64:/lib - WORKDIR /app RUN mkdir -p /app/data /builds COPY --from=backend-builder /build/arcane . EXPOSE 3552 VOLUME ["/app/data"] - LABEL com.getarcaneapp.arcane="true" - CMD ["./arcane"] From dc23bebd52a892e5d51c8b5bfb73d51fbc867c14 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 00:31:23 -0700 Subject: [PATCH 02/19] chore(docker): update whitespace undo unintentional blank line modifications --- docker/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 13d43a14d2..e3ae4e1211 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -104,10 +104,13 @@ ENV NVIDIA_VISIBLE_DEVICES=all \ ONEAPI_DEVICE_SELECTOR=level_zero:*,opencl:* \ LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/opt/rocm/lib:/opt/intel/oneapi/lib:/opt/intel/oneapi/lib/intel64:/usr/lib/x86_64-linux-gnu:/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib:/lib64:/lib + WORKDIR /app RUN mkdir -p /app/data /builds COPY --from=backend-builder /build/arcane . EXPOSE 3552 VOLUME ["/app/data"] + LABEL com.getarcaneapp.arcane="true" + CMD ["./arcane"] From 27a0ee927db511eda64ff0f232fc797e7891dc88 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 00:46:11 -0700 Subject: [PATCH 03/19] fix(DHI): add default argument earlier in file --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e3ae4e1211..befd12f88e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,6 @@ # syntax=docker/dockerfile:1 ARG BUILD_TAGS="" +ARG HARDENED_BASE_IMAGE=dhi.io/debian-base:trixie # Stage 1: Build Frontend FROM --platform=$BUILDPLATFORM node:25-trixie-slim AS frontend-builder @@ -78,7 +79,6 @@ RUN apt-get update \ fi \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Stage 3b: Hardened Production Image (no apt) -ARG HARDENED_BASE_IMAGE=debian:trixie-slim FROM ${HARDENED_BASE_IMAGE} AS runner-hardened ARG TARGETARCH From db50c79b393731e3414a3520629f0c14c37f4e56 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 00:55:20 -0700 Subject: [PATCH 04/19] fix(dhi): elevate to root for data elevates to root for data dir creation, then stops back to non-root user --- docker/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index befd12f88e..874bc3255d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -106,7 +106,11 @@ ENV NVIDIA_VISIBLE_DEVICES=all \ WORKDIR /app -RUN mkdir -p /app/data /builds + +USER root +RUN mkdir -p /app/data /builds \ + && chown -R 65532:65532 /app /builds +USER 65532:65532 COPY --from=backend-builder /build/arcane . EXPOSE 3552 VOLUME ["/app/data"] From 4c5d85a92432e4bfb3c3b326256deec9b3358c46 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 00:58:21 -0700 Subject: [PATCH 05/19] fix(DHI): update root user name usage use ID instead of name --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 874bc3255d..9b176929c3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -107,7 +107,8 @@ ENV NVIDIA_VISIBLE_DEVICES=all \ WORKDIR /app -USER root +# elevate to root to create data dir, then drop back to non-root +USER 0:0 RUN mkdir -p /app/data /builds \ && chown -R 65532:65532 /app /builds USER 65532:65532 From 91c969168037cfbbdd2267577389eb993d021f00 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 10:26:39 -0700 Subject: [PATCH 06/19] feat(DHI): add docker hardened image to dev image --- docker/Dockerfile.dev | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index fff2c3db8b..fb6c8ceee6 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,7 +1,10 @@ # syntax=docker/dockerfile:1 +# Allow swapping bbetween hardened and non-hardened images +ARG FRONTEND_BASE_IMAGE=dhi.io/node:25-trixie-slim +ARG BACKEND_BASE_IMAGE=dhi.io/golang:1.26-trixie # Stage 1: Frontend Development with optimized caching -FROM --platform=$BUILDPLATFORM node:25-trixie-slim AS frontend-dev +FROM --platform=$BUILDPLATFORM ${FRONTEND_BASE_IMAGE} AS frontend-dev RUN npm install -g --force corepack && corepack enable pnpm @@ -34,9 +37,11 @@ EXPOSE 3000 CMD ["pnpm", "-C", "frontend", "dev", "--host", "0.0.0.0"] # Stage 2: Backend Development with optimized caching -FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS backend-dev +FROM --platform=$BUILDPLATFORM ${BACKEND_BASE_IMAGE} AS backend-dev # Install development tools in a single layer +# Swap to root for hardened image +USER 0:0 RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ From 90cd9b7d918afbd716597e99e0e5282c1e0be91d Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 18:25:54 -0700 Subject: [PATCH 07/19] fix(dhi): install deps and then copy to dev ocntainer --- docker/Dockerfile.dev | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index fb6c8ceee6..7bccf6d9fd 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -39,9 +39,8 @@ CMD ["pnpm", "-C", "frontend", "dev", "--host", "0.0.0.0"] # Stage 2: Backend Development with optimized caching FROM --platform=$BUILDPLATFORM ${BACKEND_BASE_IMAGE} AS backend-dev -# Install development tools in a single layer -# Swap to root for hardened image -USER 0:0 +# Install development tools in a dedicated image +FROM debian:trixie-slim as dev-tools RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ @@ -50,6 +49,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ rm -rf /var/lib/apt/lists/* && \ go install github.com/air-verse/air@latest +FROM BACKEND_BASE_IMAGE as backend-dev +COPY --from=dev-tools /usr/bin/git /usr/bin/git +COPY --from=dev-tools /usr/bin/curl /usr/bin/curl +COPY --from=dev-tools /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=dev-tools /etc/ssl/certs /etc/ssl/certs + ENV GIN_MODE=debug \ PORT=3552 \ ENVIRONMENT=development \ From 5c83ff8ed77fdf84e19ac9e5c3b93e0213798e94 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 18:27:17 -0700 Subject: [PATCH 08/19] fix(dhi): oops, use actual image name --- docker/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 7bccf6d9fd..a3a513477f 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -49,7 +49,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ rm -rf /var/lib/apt/lists/* && \ go install github.com/air-verse/air@latest -FROM BACKEND_BASE_IMAGE as backend-dev +FROM golang:1.26-trixie as backend-dev COPY --from=dev-tools /usr/bin/git /usr/bin/git COPY --from=dev-tools /usr/bin/curl /usr/bin/curl COPY --from=dev-tools /usr/share/zoneinfo /usr/share/zoneinfo From bc80d18773a374702ecde2e90dcc87790f412123 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 21:46:14 -0700 Subject: [PATCH 09/19] fix(dhi): use unique names, derp --- docker/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index a3a513477f..040c80d526 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -49,7 +49,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ rm -rf /var/lib/apt/lists/* && \ go install github.com/air-verse/air@latest -FROM golang:1.26-trixie as backend-dev +FROM golang:1.26-trixie as backend-tools COPY --from=dev-tools /usr/bin/git /usr/bin/git COPY --from=dev-tools /usr/bin/curl /usr/bin/curl COPY --from=dev-tools /usr/share/zoneinfo /usr/share/zoneinfo From b6e16f45061e2ad03e6e2bdd44bbdd2e787776ce Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 21:57:11 -0700 Subject: [PATCH 10/19] fix(dhi): correct ordering --- docker/Dockerfile.dev | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 040c80d526..40d5a8aee2 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -37,24 +37,31 @@ EXPOSE 3000 CMD ["pnpm", "-C", "frontend", "dev", "--host", "0.0.0.0"] # Stage 2: Backend Development with optimized caching -FROM --platform=$BUILDPLATFORM ${BACKEND_BASE_IMAGE} AS backend-dev - -# Install development tools in a dedicated image -FROM debian:trixie-slim as dev-tools +# 2a - Debian stage for apt-get tools +FROM debian:trixie-slim AS dev-tools RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ git \ tzdata && \ - rm -rf /var/lib/apt/lists/* && \ - go install github.com/air-verse/air@latest + rm -rf /var/lib/apt/lists/* -FROM golang:1.26-trixie as backend-tools +# 2b - Go stage to install air +FROM golang:1.26-trixie AS backend-tools +RUN go install github.com/air-verse/air@latest COPY --from=dev-tools /usr/bin/git /usr/bin/git COPY --from=dev-tools /usr/bin/curl /usr/bin/curl COPY --from=dev-tools /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=dev-tools /etc/ssl/certs /etc/ssl/certs +# 2c - Actual dev runtime stage +FROM --platform=$BUILDPLATFORM ${BACKEND_BASE_IMAGE} AS backend-dev +COPY --from=backend-tools /go/bin/air /usr/local/bin/air +COPY --from=backend-tools /usr/bin/git /usr/bin/git +COPY --from=backend-tools /usr/bin/curl /usr/bin/curl +COPY --from=backend-tools /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=backend-tools /etc/ssl/certs /etc/ssl/certs + ENV GIN_MODE=debug \ PORT=3552 \ ENVIRONMENT=development \ From 10e5d182db69a513e225e3f33ab94bc5cbc962cb Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 22:00:24 -0700 Subject: [PATCH 11/19] fix(dhi): go dep caching --- docker/Dockerfile.dev | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 40d5a8aee2..1ce153302b 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -78,6 +78,13 @@ COPY cli/ ./cli/ # Copy backend Go module files for dependency caching COPY backend/go.mod backend/go.sum ./backend/ +# DHI golang image does not precreate the sumdb directory tree. +# Create it so go mod verify can write sumdb state. +USER 0:0 +RUN mkdir -p /go/pkg/sumdb/sum.golang.org /go/pkg/mod /root/.cache/go-build \ + && chown -R 65532:65532 /go/pkg /root/.cache || true +USER 65532:65532 + # Download dependencies with cache mounts RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ From a39624b2fe4e2f5093c4ca8e01dcac1ac889d8f7 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 22:02:01 -0700 Subject: [PATCH 12/19] fix(dhi): create more dirs --- docker/Dockerfile.dev | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 1ce153302b..4f98741150 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -78,11 +78,14 @@ COPY cli/ ./cli/ # Copy backend Go module files for dependency caching COPY backend/go.mod backend/go.sum ./backend/ -# DHI golang image does not precreate the sumdb directory tree. +# pre-create directories since DHI runs as non-root and cannot do it later # Create it so go mod verify can write sumdb state. USER 0:0 RUN mkdir -p /go/pkg/sumdb/sum.golang.org /go/pkg/mod /root/.cache/go-build \ && chown -R 65532:65532 /go/pkg /root/.cache || true +RUN mkdir -p /app/data /app/.bin /builds && \ + chown -R 65532:65532 /app /builds && \ + chmod 755 /app/data /app/.bin USER 65532:65532 # Download dependencies with cache mounts From c63e5fc0e55841b3e74d3a11b8b28485e4e9d2f8 Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 22:04:25 -0700 Subject: [PATCH 13/19] fix(dhi): remove bad default base image name --- docker/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 4f98741150..728486b22d 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Allow swapping bbetween hardened and non-hardened images ARG FRONTEND_BASE_IMAGE=dhi.io/node:25-trixie-slim -ARG BACKEND_BASE_IMAGE=dhi.io/golang:1.26-trixie +ARG BACKEND_BASE_IMAGE=dhi.io/golang:1.26 # Stage 1: Frontend Development with optimized caching FROM --platform=$BUILDPLATFORM ${FRONTEND_BASE_IMAGE} AS frontend-dev From 6b86299afab07bc23ad7057f2745cb0f4374252e Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sat, 2 May 2026 22:04:49 -0700 Subject: [PATCH 14/19] Update Dockerfile.dev --- docker/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 728486b22d..80eb873d2e 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # Allow swapping bbetween hardened and non-hardened images -ARG FRONTEND_BASE_IMAGE=dhi.io/node:25-trixie-slim +ARG FRONTEND_BASE_IMAGE=dhi.io/node:25 ARG BACKEND_BASE_IMAGE=dhi.io/golang:1.26 # Stage 1: Frontend Development with optimized caching From 9bfb47305b84e35613d9dd807f07d746a354d7da Mon Sep 17 00:00:00 2001 From: wrycu <4709746+wrycu@users.noreply.github.com> Date: Sun, 3 May 2026 11:53:31 -0700 Subject: [PATCH 15/19] Update Dockerfile.dev --- docker/Dockerfile.dev | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 80eb873d2e..fff2c3db8b 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,10 +1,7 @@ # syntax=docker/dockerfile:1 -# Allow swapping bbetween hardened and non-hardened images -ARG FRONTEND_BASE_IMAGE=dhi.io/node:25 -ARG BACKEND_BASE_IMAGE=dhi.io/golang:1.26 # Stage 1: Frontend Development with optimized caching -FROM --platform=$BUILDPLATFORM ${FRONTEND_BASE_IMAGE} AS frontend-dev +FROM --platform=$BUILDPLATFORM node:25-trixie-slim AS frontend-dev RUN npm install -g --force corepack && corepack enable pnpm @@ -37,30 +34,16 @@ EXPOSE 3000 CMD ["pnpm", "-C", "frontend", "dev", "--host", "0.0.0.0"] # Stage 2: Backend Development with optimized caching -# 2a - Debian stage for apt-get tools -FROM debian:trixie-slim AS dev-tools +FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS backend-dev + +# Install development tools in a single layer RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ git \ tzdata && \ - rm -rf /var/lib/apt/lists/* - -# 2b - Go stage to install air -FROM golang:1.26-trixie AS backend-tools -RUN go install github.com/air-verse/air@latest -COPY --from=dev-tools /usr/bin/git /usr/bin/git -COPY --from=dev-tools /usr/bin/curl /usr/bin/curl -COPY --from=dev-tools /usr/share/zoneinfo /usr/share/zoneinfo -COPY --from=dev-tools /etc/ssl/certs /etc/ssl/certs - -# 2c - Actual dev runtime stage -FROM --platform=$BUILDPLATFORM ${BACKEND_BASE_IMAGE} AS backend-dev -COPY --from=backend-tools /go/bin/air /usr/local/bin/air -COPY --from=backend-tools /usr/bin/git /usr/bin/git -COPY --from=backend-tools /usr/bin/curl /usr/bin/curl -COPY --from=backend-tools /usr/share/zoneinfo /usr/share/zoneinfo -COPY --from=backend-tools /etc/ssl/certs /etc/ssl/certs + rm -rf /var/lib/apt/lists/* && \ + go install github.com/air-verse/air@latest ENV GIN_MODE=debug \ PORT=3552 \ @@ -78,16 +61,6 @@ COPY cli/ ./cli/ # Copy backend Go module files for dependency caching COPY backend/go.mod backend/go.sum ./backend/ -# pre-create directories since DHI runs as non-root and cannot do it later -# Create it so go mod verify can write sumdb state. -USER 0:0 -RUN mkdir -p /go/pkg/sumdb/sum.golang.org /go/pkg/mod /root/.cache/go-build \ - && chown -R 65532:65532 /go/pkg /root/.cache || true -RUN mkdir -p /app/data /app/.bin /builds && \ - chown -R 65532:65532 /app /builds && \ - chmod 755 /app/data /app/.bin -USER 65532:65532 - # Download dependencies with cache mounts RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ From 5f7445f48ba3a8f945cd20862fee9ad63d4b1b8e Mon Sep 17 00:00:00 2001 From: Kyle Mendell Date: Tue, 5 May 2026 10:26:39 -0500 Subject: [PATCH 16/19] custom --- .github/workflows/hadolint.yml | 20 +++++- Justfile | 4 +- backend/go.mod | 1 - .../pkg/libarcane/startup/runtime_identity.go | 12 ++++ .../startup/runtime_identity_test.go | 16 +++++ docker/Dockerfile | 29 ++------ docker/Dockerfile-agent | 12 +--- docker/Dockerfile-dhi-base | 66 +++++++++++++++++++ docker/examples/compose.basic.yaml | 6 +- docker/examples/compose.proxy.yaml | 6 +- 10 files changed, 128 insertions(+), 44 deletions(-) create mode 100644 docker/Dockerfile-dhi-base diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index 58608f03bf..094ccefd69 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -28,6 +28,19 @@ jobs: contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/upload-sarif to upload SARIF results actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + strategy: + fail-fast: false + matrix: + include: + - name: manager + dockerfile: ./docker/Dockerfile + output: hadolint-results-manager.sarif + - name: agent + dockerfile: ./docker/Dockerfile-agent + output: hadolint-results-agent.sarif + - name: dhi-base + dockerfile: ./docker/Dockerfile-dhi-base + output: hadolint-results-dhi-base.sarif steps: - name: Checkout code uses: actions/checkout@v6 @@ -35,13 +48,14 @@ jobs: - name: Run hadolint uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 with: - dockerfile: ./docker/Dockerfile + dockerfile: ${{ matrix.dockerfile }} format: sarif - output-file: hadolint-results.sarif + output-file: ${{ matrix.output }} no-fail: true - name: Upload analysis results to GitHub uses: github/codeql-action/upload-sarif@v4 with: - sarif_file: hadolint-results.sarif + sarif_file: ${{ matrix.output }} + category: hadolint-${{ matrix.name }} wait-for-processing: true diff --git a/Justfile b/Justfile index 9ee69f0d84..6818bc1179 100644 --- a/Justfile +++ b/Justfile @@ -827,12 +827,12 @@ _build-backend: # Build manager container image [group('build')] _build-image-manager tag="ghcr.io/getarcaneapp/arcane:development" flag='': - docker buildx build {{ if flag == "--push" { "--push" } else { "" } }} --platform linux/arm64,linux/amd64,linux/arm/v7 -f 'docker/Dockerfile' --build-arg ENABLED_FEATURES="{{ env('ENABLED_FEATURES', env('BUILD_FEATURES', '')) }}" -t "{{ tag }}" . + docker buildx build {{ if flag == "--push" { "--push" } else { "" } }} --platform "{{ env('BUILD_PLATFORMS', 'linux/arm64,linux/amd64') }}" -f 'docker/Dockerfile' --build-arg ENABLED_FEATURES="{{ env('ENABLED_FEATURES', env('BUILD_FEATURES', '')) }}" -t "{{ tag }}" . # Build agent container image [group('build')] _build-image-agent tag="ghcr.io/getarcaneapp/arcane-headless:development" flag='': - docker buildx build {{ if flag == "--push" { "--push" } else { "" } }} --platform linux/arm64,linux/amd64,linux/arm/v7 -f 'docker/Dockerfile-agent' --build-arg ENABLED_FEATURES="{{ env('ENABLED_FEATURES', env('BUILD_FEATURES', '')) }}" -t "{{ tag }}" . + docker buildx build {{ if flag == "--push" { "--push" } else { "" } }} --platform "{{ env('BUILD_PLATFORMS', 'linux/arm64,linux/amd64') }}" -f 'docker/Dockerfile-agent' --build-arg ENABLED_FEATURES="{{ env('ENABLED_FEATURES', env('BUILD_FEATURES', '')) }}" -t "{{ tag }}" . # Build both frontend and backend [group('build')] diff --git a/backend/go.mod b/backend/go.mod index c0c13ee28a..5b983fea32 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,7 +3,6 @@ module github.com/getarcaneapp/arcane/backend go 1.26.2 replace github.com/getarcaneapp/arcane/cli => ../cli - replace github.com/getarcaneapp/arcane/types => ../types require ( diff --git a/backend/pkg/libarcane/startup/runtime_identity.go b/backend/pkg/libarcane/startup/runtime_identity.go index 54b3317c73..7edcab24a1 100644 --- a/backend/pkg/libarcane/startup/runtime_identity.go +++ b/backend/pkg/libarcane/startup/runtime_identity.go @@ -16,6 +16,8 @@ const ( defaultBuildsDirectory = "/builds" defaultDatabaseURL = "file:data/arcane.db?_pragma=journal_mode(WAL)&_pragma=busy_timeout(2500)&_txlock=immediate" defaultDockerSocketPath = "/var/run/docker.sock" + defaultRuntimeUID = 65532 + defaultRuntimeGID = 65532 mountInfoPath = "/proc/self/mountinfo" ) @@ -70,6 +72,16 @@ func loadRuntimeIdentityRequestInternal(getenv func(string) string) (runtimeIden pgid := strings.TrimSpace(getenv("PGID")) if puid == "" && pgid == "" { + if strings.EqualFold(strings.TrimSpace(getenv("ARCANE_DEFAULT_NONROOT")), "true") { + return runtimeIdentityRequest{ + Enabled: true, + UID: defaultRuntimeUID, + GID: defaultRuntimeGID, + CredentialUID: uint32(defaultRuntimeUID), + CredentialGID: uint32(defaultRuntimeGID), + }, "", nil + } + return runtimeIdentityRequest{}, "", nil } diff --git a/backend/pkg/libarcane/startup/runtime_identity_test.go b/backend/pkg/libarcane/startup/runtime_identity_test.go index 95f722e1e2..53c5c34d6e 100644 --- a/backend/pkg/libarcane/startup/runtime_identity_test.go +++ b/backend/pkg/libarcane/startup/runtime_identity_test.go @@ -17,6 +17,22 @@ func TestLoadRuntimeIdentityRequest(t *testing.T) { require.False(t, req.Enabled) }) + t.Run("enabled with default non-root image flag", func(t *testing.T) { + req, warning, err := loadRuntimeIdentityRequestInternal(func(key string) string { + if key == "ARCANE_DEFAULT_NONROOT" { + return "true" + } + return "" + }) + require.NoError(t, err) + require.Empty(t, warning) + require.True(t, req.Enabled) + require.Equal(t, defaultRuntimeUID, req.UID) + require.Equal(t, defaultRuntimeGID, req.GID) + require.Equal(t, uint32(defaultRuntimeUID), req.CredentialUID) + require.Equal(t, uint32(defaultRuntimeGID), req.CredentialGID) + }) + t.Run("warning when partial config", func(t *testing.T) { req, warning, err := loadRuntimeIdentityRequestInternal(func(key string) string { if key == "PUID" { diff --git a/docker/Dockerfile b/docker/Dockerfile index 9b176929c3..55181b17d1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 ARG BUILD_TAGS="" -ARG HARDENED_BASE_IMAGE=dhi.io/debian-base:trixie +ARG ARCANE_RUNTIME_BASE_IMAGE=ghcr.io/getarcaneapp/base:trixie # Stage 1: Build Frontend FROM --platform=$BUILDPLATFORM node:25-trixie-slim AS frontend-builder @@ -66,37 +66,18 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /build/arcane \ ./cmd/main.go -# Stage 3a: Runtime deps (apt-enabled) -FROM debian:trixie-slim AS runner-deps -ARG TARGETARCH - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - ca-certificates curl tzdata tar gzip \ - vainfo clinfo libdrm2 libsystemd0 \ - && if [ "$TARGETARCH" = "amd64" ]; then \ - apt-get install -y --no-install-recommends intel-gpu-tools; \ - fi \ - && apt-get clean && rm -rf /var/lib/apt/lists/* -# Stage 3b: Hardened Production Image (no apt) -FROM ${HARDENED_BASE_IMAGE} AS runner-hardened +# Stage 3: Hardened Production Image +FROM ${ARCANE_RUNTIME_BASE_IMAGE} AS runner-hardened ARG TARGETARCH ARG VERSION ARG REVISION -# Copy runtime deps from runner-deps instead of apt-get -COPY --from=runner-deps /etc/ssl/certs/ /etc/ssl/certs/ -COPY --from=runner-deps /usr/share/zoneinfo/ /usr/share/zoneinfo/ -COPY --from=runner-deps /usr/bin/ /usr/bin/ -COPY --from=runner-deps /usr/lib/ /usr/lib/ -COPY --from=runner-deps /lib/ /lib/ -COPY --from=runner-deps /lib64/ /lib64/ - # keep the same env/settings as existing runner ENV GIN_MODE=release ENV PORT=3552 ENV ENVIRONMENT=production +ENV ARCANE_DEFAULT_NONROOT=true ENV NVIDIA_VISIBLE_DEVICES=all \ NVIDIA_DRIVER_CAPABILITIES=compute,utility \ ROCR_VISIBLE_DEVICES=all \ @@ -107,11 +88,9 @@ ENV NVIDIA_VISIBLE_DEVICES=all \ WORKDIR /app -# elevate to root to create data dir, then drop back to non-root USER 0:0 RUN mkdir -p /app/data /builds \ && chown -R 65532:65532 /app /builds -USER 65532:65532 COPY --from=backend-builder /build/arcane . EXPOSE 3552 VOLUME ["/app/data"] diff --git a/docker/Dockerfile-agent b/docker/Dockerfile-agent index 73f0231b72..22c51232e0 100644 --- a/docker/Dockerfile-agent +++ b/docker/Dockerfile-agent @@ -1,6 +1,7 @@ # syntax=docker/dockerfile:1 ARG VERSION="dev" ARG REVISION="unknown" +ARG ARCANE_RUNTIME_BASE_IMAGE=ghcr.io/getarcaneapp/base:trixie FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS agent-builder ARG TARGETARCH @@ -42,17 +43,10 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /out/arcane-agent \ ./cmd/main.go -FROM debian:trixie-slim AS agent -ARG TARGETARCH -RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates curl tzdata tar gzip \ - vainfo clinfo libdrm2 libsystemd0 \ - && if [ "$TARGETARCH" = "amd64" ]; then \ - apt-get install -y --no-install-recommends intel-gpu-tools; \ - fi \ - && apt-get clean && rm -rf /var/lib/apt/lists/* +FROM ${ARCANE_RUNTIME_BASE_IMAGE} AS agent WORKDIR /app +USER 0:0 RUN mkdir -p /app/data COPY --from=agent-builder /out/arcane-agent ./arcane-agent diff --git a/docker/Dockerfile-dhi-base b/docker/Dockerfile-dhi-base new file mode 100644 index 0000000000..94897920a7 --- /dev/null +++ b/docker/Dockerfile-dhi-base @@ -0,0 +1,66 @@ +# syntax=docker/dockerfile:1 + +ARG DHI_BASE_IMAGE=dhi.io/debian-base:trixie + +# Runtime deps are assembled in an apt-enabled stage, then copied into the +# DHI base so downstream Arcane builds do not need dhi.io credentials. +FROM debian:trixie-slim AS runtime-deps +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + clinfo \ + curl \ + gzip \ + libdrm2 \ + libsystemd0 \ + tar \ + tzdata \ + vainfo \ + && if [ "${TARGETARCH}" = "amd64" ]; then \ + apt-get install -y --no-install-recommends intel-gpu-tools; \ + fi \ + && mkdir -p /runtime-root \ + && copy_path() { \ + path="$1"; \ + if [ -e "${path}" ]; then \ + mkdir -p "/runtime-root$(dirname "${path}")"; \ + cp -aL "${path}" "/runtime-root${path}"; \ + fi; \ + } \ + && copy_resolved_path() { \ + path="$1"; \ + if [ -e "${path}" ]; then \ + resolved_path="$(readlink -f "${path}")"; \ + mkdir -p "/runtime-root$(dirname "${resolved_path}")"; \ + cp -aL "${path}" "/runtime-root${resolved_path}"; \ + fi; \ + } \ + && copy_tree() { \ + path="$1"; \ + if [ -e "${path}" ]; then \ + mkdir -p "/runtime-root$(dirname "${path}")"; \ + cp -aL "${path}" "/runtime-root${path}"; \ + fi; \ + } \ + && runtime_bins="/usr/bin/curl /usr/bin/tar /usr/bin/gzip /usr/bin/vainfo /usr/bin/clinfo" \ + && if [ "${TARGETARCH}" = "amd64" ]; then \ + runtime_bins="${runtime_bins} $(find /usr/bin -maxdepth 1 -type f -name 'intel_*' | sort)"; \ + fi \ + && for bin in ${runtime_bins}; do \ + copy_path "${bin}"; \ + ldd "${bin}" \ + | awk '/=> \// { print $3 } /^\// { print $1 }' \ + | sort -u \ + | while read -r lib; do copy_resolved_path "${lib}"; done; \ + done \ + && copy_tree /etc/ssl/certs \ + && copy_tree /usr/share/ca-certificates \ + && copy_tree /usr/share/zoneinfo \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +FROM ${DHI_BASE_IMAGE} + +COPY --from=runtime-deps /runtime-root/ / diff --git a/docker/examples/compose.basic.yaml b/docker/examples/compose.basic.yaml index 0ec392ca87..bdc756c40f 100644 --- a/docker/examples/compose.basic.yaml +++ b/docker/examples/compose.basic.yaml @@ -25,8 +25,10 @@ services: healthcheck: test: [ - "CMD-SHELL", - "curl -fsS http://localhost:3552/api/health >/dev/null || exit 1", + "CMD", + "curl", + "-fsS", + "http://localhost:3552/api/health", ] interval: 10s timeout: 3s diff --git a/docker/examples/compose.proxy.yaml b/docker/examples/compose.proxy.yaml index 8c5793c11d..274798e51a 100644 --- a/docker/examples/compose.proxy.yaml +++ b/docker/examples/compose.proxy.yaml @@ -59,8 +59,10 @@ services: healthcheck: test: [ - "CMD-SHELL", - "curl -fsS http://localhost:3552/api/health >/dev/null || exit 1", + "CMD", + "curl", + "-fsS", + "http://localhost:3552/api/health", ] interval: 10s timeout: 3s From be1da7832f0ae266e303e6305a76f03b15c58ac7 Mon Sep 17 00:00:00 2001 From: Kyle Mendell Date: Tue, 5 May 2026 10:38:20 -0500 Subject: [PATCH 17/19] use 0:0 for user --- tests/setup/compose-postgres.yaml | 2 +- tests/setup/compose-proxy.yaml | 2 +- tests/setup/compose.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/setup/compose-postgres.yaml b/tests/setup/compose-postgres.yaml index a5c7503d10..ce57ab52f3 100644 --- a/tests/setup/compose-postgres.yaml +++ b/tests/setup/compose-postgres.yaml @@ -15,7 +15,7 @@ services: arcane: image: arcane:playwright-tests - user: root + user: "0:0" ports: - '3001:3552' environment: diff --git a/tests/setup/compose-proxy.yaml b/tests/setup/compose-proxy.yaml index dfdcfba1ee..67cc772ce3 100644 --- a/tests/setup/compose-proxy.yaml +++ b/tests/setup/compose-proxy.yaml @@ -32,7 +32,7 @@ services: arcane: image: arcane:playwright-tests - user: root + user: "0:0" ports: - '3002:3552' environment: diff --git a/tests/setup/compose.yaml b/tests/setup/compose.yaml index 53e193c6a2..35d9cc2275 100644 --- a/tests/setup/compose.yaml +++ b/tests/setup/compose.yaml @@ -1,7 +1,7 @@ services: arcane: image: arcane:playwright-tests - user: root + user: "0:0" ports: - '3000:3552' environment: From fe9bd6cb675fd4dc0d660cb46f6b11fc3cc1408f Mon Sep 17 00:00:00 2001 From: Kyle Mendell Date: Tue, 5 May 2026 11:09:05 -0500 Subject: [PATCH 18/19] use 65532 for user --- tests/setup/compose-postgres.yaml | 2 ++ tests/setup/compose-proxy.yaml | 2 ++ tests/setup/compose.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tests/setup/compose-postgres.yaml b/tests/setup/compose-postgres.yaml index ce57ab52f3..0fbf4d46b0 100644 --- a/tests/setup/compose-postgres.yaml +++ b/tests/setup/compose-postgres.yaml @@ -21,6 +21,8 @@ services: environment: - ENVIRONMENT=testing - APP_ENV=test + - PUID=65532 + - PGID=65532 - DATABASE_URL=postgres://arcane:arcane_test_password@postgres:5432/arcane_test?sslmode=disable - ENCRYPTION_KEY=3JDIgolks2tJ9ymm1AdqzlYMWu0DUWyt - JWT_SECRET=your-super-secret-jwt-key-change-this-in-production diff --git a/tests/setup/compose-proxy.yaml b/tests/setup/compose-proxy.yaml index 67cc772ce3..538bde32fe 100644 --- a/tests/setup/compose-proxy.yaml +++ b/tests/setup/compose-proxy.yaml @@ -38,6 +38,8 @@ services: environment: - ENVIRONMENT=testing - APP_ENV=test + - PUID=65532 + - PGID=65532 - ENCRYPTION_KEY=3JDIgolks2tJ9ymm1AdqzlYMWu0DUWyt - JWT_SECRET=your-super-secret-jwt-key-change-this-in-production - ADMIN_STATIC_API_KEY=${E2E_ADMIN_STATIC_API_KEY:-} diff --git a/tests/setup/compose.yaml b/tests/setup/compose.yaml index 35d9cc2275..03757ed57e 100644 --- a/tests/setup/compose.yaml +++ b/tests/setup/compose.yaml @@ -7,6 +7,8 @@ services: environment: - ENVIRONMENT=testing - APP_ENV=test + - PUID=65532 + - PGID=65532 - ENCRYPTION_KEY=3JDIgolks2tJ9ymm1AdqzlYMWu0DUWyt - JWT_SECRET=your-super-secret-jwt-key-change-this-in-production - ADMIN_STATIC_API_KEY=${E2E_ADMIN_STATIC_API_KEY:-} From 75655922dbd5c5d92f303cd2bcbce4a701d97e5c Mon Sep 17 00:00:00 2001 From: Kyle Mendell Date: Tue, 5 May 2026 11:36:42 -0500 Subject: [PATCH 19/19] wut --- tests/setup/compose-postgres.yaml | 1 - tests/setup/compose-proxy.yaml | 1 - tests/setup/compose.yaml | 1 - 3 files changed, 3 deletions(-) diff --git a/tests/setup/compose-postgres.yaml b/tests/setup/compose-postgres.yaml index 0fbf4d46b0..678a528724 100644 --- a/tests/setup/compose-postgres.yaml +++ b/tests/setup/compose-postgres.yaml @@ -15,7 +15,6 @@ services: arcane: image: arcane:playwright-tests - user: "0:0" ports: - '3001:3552' environment: diff --git a/tests/setup/compose-proxy.yaml b/tests/setup/compose-proxy.yaml index 538bde32fe..ed18bf0ec3 100644 --- a/tests/setup/compose-proxy.yaml +++ b/tests/setup/compose-proxy.yaml @@ -32,7 +32,6 @@ services: arcane: image: arcane:playwright-tests - user: "0:0" ports: - '3002:3552' environment: diff --git a/tests/setup/compose.yaml b/tests/setup/compose.yaml index 03757ed57e..88c94d3bf1 100644 --- a/tests/setup/compose.yaml +++ b/tests/setup/compose.yaml @@ -1,7 +1,6 @@ services: arcane: image: arcane:playwright-tests - user: "0:0" ports: - '3000:3552' environment: