chore: migrate Docker base images from Alpine to Debian Trixie

Migrated all Docker stages from Alpine 3.23 to Debian Trixie (13) to
address critical CVE in Alpine's gosu package and improve security
update frequency.

Key changes:

Updated CADDY_IMAGE to debian:trixie-slim
Added gosu-builder stage to compile gosu 1.17 from source with Go 1.25.6
Migrated all builder stages to golang:1.25-trixie
Updated package manager from apk to apt-get
Updated user/group creation to use groupadd/useradd
Changed nologin path from /sbin/nologin to /usr/sbin/nologin
Security impact:

Resolved gosu Critical CVE (built from source eliminates vulnerable Go stdlib)
Reduced overall CVE count from 6 (bookworm) to 2 (trixie)
Remaining 2 CVEs are glibc-related with no upstream fix available
All Go binaries verified vulnerability-free by Trivy and govulncheck
Verification:

E2E tests: 243 passed (5 pre-existing failures unrelated to migration)
Backend coverage: 87.2%
Frontend coverage: 85.89%
Pre-commit hooks: 13/13 passed
TypeScript: 0 errors
Refs: CVE-2026-0861 (glibc, no upstream fix - accepted risk)
This commit is contained in:
GitHub Actions
2026-01-18 21:01:30 +00:00
parent c46c374261
commit e0a39518ba
10 changed files with 1325 additions and 85 deletions

View File

@@ -15,18 +15,52 @@ ARG VCS_REF
## If the requested tag isn't available, fall back to a known-good v2.11.0-beta.2 build.
ARG CADDY_VERSION=2.11.0-beta.2
## When an official caddy image tag isn't available on the host, use a
## plain Alpine base image and overwrite its caddy binary with our
## plain Debian slim base image and overwrite its caddy binary with our
## xcaddy-built binary in the later COPY step. This avoids relying on
## upstream caddy image tags while still shipping a pinned caddy binary.
# renovate: datasource=docker depName=alpine
ARG CADDY_IMAGE=alpine:3.23
## Using trixie (Debian 13 testing) for faster security updates - bookworm
## packages marked "wont-fix" are actively maintained in trixie.
# renovate: datasource=docker depName=debian
ARG CADDY_IMAGE=debian:trixie-slim
# ---- Cross-Compilation Helpers ----
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.9.0 AS xx
# ---- Gosu Builder ----
# Build gosu from source to avoid CVEs from Debian's pre-compiled version (Go 1.19.8)
# This fixes 22 HIGH/CRITICAL CVEs in stdlib embedded in Debian's gosu package
# CVEs fixed: CVE-2023-24531, CVE-2023-24540, CVE-2023-29402, CVE-2023-29404,
# CVE-2023-29405, CVE-2024-24790, CVE-2025-22871, and 15 more
FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS gosu-builder
COPY --from=xx / /
WORKDIR /tmp/gosu
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH
# renovate: datasource=github-releases depName=tianon/gosu
ARG GOSU_VERSION=1.17
RUN apt-get update && apt-get install -y --no-install-recommends \
git clang lld \
&& rm -rf /var/lib/apt/lists/*
# hadolint ignore=DL3059
RUN xx-apt install -y gcc libc6-dev
# Clone and build gosu from source with modern Go
RUN git clone --depth 1 --branch "${GOSU_VERSION}" https://github.com/tianon/gosu.git .
# Build gosu for target architecture with patched Go stdlib
# hadolint ignore=DL3059
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
CGO_ENABLED=0 xx-go build -v -ldflags '-s -w' -o /gosu-out/gosu . && \
xx-verify /gosu-out/gosu
# ---- Frontend Builder ----
# Build the frontend using the BUILDPLATFORM to avoid arm64 musl Rollup native issues
FROM --platform=$BUILDPLATFORM node:24.13.0-alpine AS frontend-builder
FROM --platform=$BUILDPLATFORM node:24.13.0-slim AS frontend-builder
WORKDIR /app/frontend
# Copy frontend package files
@@ -49,55 +83,21 @@ RUN --mount=type=cache,target=/app/frontend/node_modules/.cache \
npm run build
# ---- Backend Builder ----
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS backend-builder
FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS backend-builder
# Copy xx helpers for cross-compilation
COPY --from=xx / /
WORKDIR /app/backend
# Install build dependencies
# xx-apk installs packages for the TARGET architecture
# xx-apt installs packages for the TARGET architecture
ARG TARGETPLATFORM
ARG TARGETARCH
# hadolint ignore=DL3018
RUN apk add --no-cache clang lld
# hadolint ignore=DL3018,DL3059
RUN xx-apk add --no-cache gcc musl-dev sqlite-dev
# Create clang wrapper that intercepts -fuse-ld=gold and replaces with -fuse-ld=lld
# Go 1.25 hardcodes -fuse-ld=gold for ARM64 but Alpine's clang only has LLD
# Also, Go linker checks linker --version to verify "GNU gold" - we need to fake that too
# hadolint ignore=DL3059,SC2016
RUN if [ -f "/usr/bin/clang" ]; then \
mv /usr/bin/clang /usr/bin/clang.real && \
printf '#!/bin/sh\n\
# Wrapper to handle Go ARM64 gold linker requirement\n\
# Check if this is a version check with gold linker\n\
for arg in "$@"; do\n\
case "$arg" in\n\
-fuse-ld=gold)\n\
# Check if this is just a version check\n\
case "$*" in\n\
*--version*)\n\
# Fake gold version output for Go linker detection\n\
echo "GNU gold (fake for Go compatibility) 1.16"\n\
exit 0\n\
;;\n\
esac\n\
;;\n\
esac\n\
done\n\
# Transform arguments: replace -fuse-ld=gold with -fuse-ld=lld\n\
args=""\n\
for arg in "$@"; do\n\
case "$arg" in\n\
-fuse-ld=gold) args="$args -fuse-ld=lld" ;;\n\
*) args="$args $arg" ;;\n\
esac\n\
done\n\
exec /usr/bin/clang.real $args\n' > /usr/bin/clang && \
chmod +x /usr/bin/clang && \
echo "Created /usr/bin/clang wrapper with gold version spoofing"; \
fi
RUN apt-get update && apt-get install -y --no-install-recommends \
clang lld \
&& rm -rf /var/lib/apt/lists/*
# hadolint ignore=DL3059
RUN xx-apt install -y gcc libc6-dev libsqlite3-dev
# Install Delve (cross-compile for target)
# Note: xx-go install puts binaries in /go/bin/TARGETOS_TARGETARCH/dlv if cross-compiling.
@@ -137,13 +137,13 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
# ---- Caddy Builder ----
# Build Caddy from source to ensure we use the latest Go version and dependencies
# This fixes vulnerabilities found in the pre-built Caddy images (e.g. CVE-2025-59530, stdlib issues)
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS caddy-builder
FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS caddy-builder
ARG TARGETOS
ARG TARGETARCH
ARG CADDY_VERSION
# hadolint ignore=DL3018
RUN apk add --no-cache git
RUN apt-get update && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
# hadolint ignore=DL3062
RUN --mount=type=cache,target=/go/pkg/mod \
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
@@ -200,7 +200,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
# Build CrowdSec from source to ensure we use Go 1.25.5+ and avoid stdlib vulnerabilities
# (CVE-2025-58183, CVE-2025-58186, CVE-2025-58187, CVE-2025-61729)
# renovate: datasource=docker depName=golang versioning=docker
FROM --platform=$BUILDPLATFORM golang:1.25.6-alpine AS crowdsec-builder
FROM --platform=$BUILDPLATFORM golang:1.25.6-trixie AS crowdsec-builder
COPY --from=xx / /
WORKDIR /tmp/crowdsec
@@ -212,10 +212,11 @@ ARG TARGETARCH
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
ARG CROWDSEC_VERSION=1.7.4
# hadolint ignore=DL3018
RUN apk add --no-cache git clang lld
# hadolint ignore=DL3018,DL3059
RUN xx-apk add --no-cache gcc musl-dev
RUN apt-get update && apt-get install -y --no-install-recommends \
git clang lld \
&& rm -rf /var/lib/apt/lists/*
# hadolint ignore=DL3059
RUN xx-apt install -y gcc libc6-dev
# Clone CrowdSec source
RUN git clone --depth 1 --branch "v${CROWDSEC_VERSION}" https://github.com/crowdsecurity/crowdsec.git .
@@ -255,8 +256,8 @@ RUN mkdir -p /crowdsec-out/config && \
cp -r config/* /crowdsec-out/config/ || true
# ---- CrowdSec Fallback (for architectures where build fails) ----
# renovate: datasource=docker depName=alpine
FROM alpine:3.23 AS crowdsec-fallback
# renovate: datasource=docker depName=debian
FROM debian:trixie-slim AS crowdsec-fallback
WORKDIR /tmp/crowdsec
@@ -265,8 +266,10 @@ ARG TARGETARCH
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
ARG CROWDSEC_VERSION=1.7.4
# hadolint ignore=DL3018
RUN apk add --no-cache curl tar
# Note: Debian slim does NOT include tar by default - must be explicitly installed
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates tar \
&& rm -rf /var/lib/apt/lists/*
# Download static binaries as fallback (only available for amd64)
# For other architectures, create empty placeholder files so COPY doesn't fail
@@ -295,17 +298,21 @@ FROM ${CADDY_IMAGE}
WORKDIR /app
# Install runtime dependencies for Charon, including bash for maintenance scripts
# su-exec is used for dropping privileges after Docker socket group setup
# Explicitly upgrade c-ares to fix CVE-2025-62408
# hadolint ignore=DL3018
RUN apk --no-cache add bash ca-certificates sqlite-libs sqlite tzdata curl gettext su-exec libcap-utils \
&& apk --no-cache upgrade \
&& apk --no-cache upgrade c-ares
# Note: gosu is now built from source (see gosu-builder stage) to avoid CVEs from Debian's pre-compiled version
# Explicitly upgrade packages to fix security vulnerabilities
RUN apt-get update && apt-get install -y --no-install-recommends \
bash ca-certificates libsqlite3-0 sqlite3 tzdata curl gettext-base libcap2-bin libc-ares2 \
&& apt-get upgrade -y \
&& rm -rf /var/lib/apt/lists/*
# Copy gosu binary from gosu-builder (built with Go 1.25+ to avoid stdlib CVEs)
COPY --from=gosu-builder /gosu-out/gosu /usr/sbin/gosu
RUN chmod +x /usr/sbin/gosu
# Security: Create non-root user and group for running the application
# This follows the principle of least privilege (CIS Docker Benchmark 4.1)
RUN addgroup -g 1000 charon && \
adduser -D -u 1000 -G charon -h /app -s /sbin/nologin charon
RUN groupadd -g 1000 charon && \
useradd -u 1000 -g charon -d /app -s /usr/sbin/nologin -M charon
# Download MaxMind GeoLite2 Country database
# Note: In production, users should provide their own MaxMind license key
@@ -434,7 +441,7 @@ RUN ln -sf /app/data/crowdsec/config /etc/crowdsec
# 1. Maintains CIS Docker Benchmark compliance (non-root execution)
# 2. Enables Docker integration by dynamically adding charon to docker group
# 3. Ensures proper ownership of mounted volumes
# The entrypoint script uses su-exec to securely drop privileges after setup.
# The entrypoint script uses gosu to securely drop privileges after setup.
# Use custom entrypoint to start both Caddy and Charon
ENTRYPOINT ["/docker-entrypoint.sh"]