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:
139
Dockerfile
139
Dockerfile
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user