- tonistiigi/xx:1.9.0 → pinned with digest - golang:1.25-trixie → pinned with digest (gosu, backend, caddy builders) - golang:1.25.6-trixie → pinned with digest (crowdsec builder) - node:24.13.0-slim → pinned with digest (frontend builder) - debian:trixie-slim → pinned with digest (crowdsec fallback) All images now have renovate tracking comments for automatic security updates. This ensures reproducible builds and enables Renovate to notify on new digests.
472 lines
21 KiB
Docker
472 lines
21 KiB
Docker
# Multi-stage Dockerfile for Charon with integrated Caddy
|
|
# Single container deployment for simplified home user setup
|
|
|
|
# Build arguments for versioning
|
|
ARG VERSION=dev
|
|
ARG BUILD_DATE
|
|
ARG VCS_REF
|
|
# Set BUILD_DEBUG=1 to build with debug symbols (required for Delve debugging)
|
|
ARG BUILD_DEBUG=0
|
|
|
|
# Allow pinning Caddy version - Renovate will update this
|
|
# Build the most recent Caddy 2.x release (keeps major pinned under v3).
|
|
# Setting this to '2' tells xcaddy to resolve the latest v2.x tag so we
|
|
# avoid accidentally pulling a v3 major release. Renovate can still update
|
|
# this ARG to a specific v2.x tag when desired.
|
|
## Try to build the requested Caddy v2.x tag (Renovate can update this ARG).
|
|
## 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 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.
|
|
## Using trixie (Debian 13 testing) for faster security updates - bookworm
|
|
## packages marked "wont-fix" are actively maintained in trixie.
|
|
# renovate: datasource=docker depName=debian versioning=docker
|
|
ARG CADDY_IMAGE=debian:trixie-slim@sha256:77ba0164de17b88dd0bf6cdc8f65569e6e5fa6cd256562998b62553134a00ef0
|
|
|
|
# ---- Cross-Compilation Helpers ----
|
|
# renovate: datasource=docker depName=tonistiigi/xx
|
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.9.0@sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707 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
|
|
# renovate: datasource=docker depName=golang
|
|
FROM --platform=$BUILDPLATFORM golang:1.25-trixie@sha256:fb4b74a39c7318d53539ebda43ccd3ecba6e447a78591889c0efc0a7235ea8b3 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
|
|
# renovate: datasource=docker depName=node
|
|
FROM --platform=$BUILDPLATFORM node:24.13.0-slim@sha256:bf22df20270b654c4e9da59d8d4a3516cce6ba2852e159b27288d645b7a7eedc AS frontend-builder
|
|
WORKDIR /app/frontend
|
|
|
|
# Copy frontend package files
|
|
COPY frontend/package*.json ./
|
|
|
|
# Build-time project version (propagated from top-level build-arg)
|
|
ARG VERSION=dev
|
|
# Make version available to Vite as VITE_APP_VERSION during the frontend build
|
|
ENV VITE_APP_VERSION=${VERSION}
|
|
|
|
# Set environment to bypass native binary requirement for cross-arch builds
|
|
ENV npm_config_rollup_skip_nodejs_native=1 \
|
|
ROLLUP_SKIP_NODEJS_NATIVE=1
|
|
|
|
RUN npm ci
|
|
|
|
# Copy frontend source and build
|
|
COPY frontend/ ./
|
|
RUN --mount=type=cache,target=/app/frontend/node_modules/.cache \
|
|
npm run build
|
|
|
|
# ---- Backend Builder ----
|
|
# renovate: datasource=docker depName=golang
|
|
FROM --platform=$BUILDPLATFORM golang:1.25-trixie@sha256:fb4b74a39c7318d53539ebda43ccd3ecba6e447a78591889c0efc0a7235ea8b3 AS backend-builder
|
|
# Copy xx helpers for cross-compilation
|
|
COPY --from=xx / /
|
|
|
|
WORKDIR /app/backend
|
|
|
|
# Install build dependencies
|
|
# xx-apt installs packages for the TARGET architecture
|
|
ARG TARGETPLATFORM
|
|
ARG TARGETARCH
|
|
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.
|
|
# We find it and move it to /go/bin/dlv so it's in a consistent location for the next stage.
|
|
# hadolint ignore=DL3059,DL4006
|
|
RUN CGO_ENABLED=0 xx-go install github.com/go-delve/delve/cmd/dlv@latest && \
|
|
DLV_PATH=$(find /go/bin -name dlv -type f | head -n 1) && \
|
|
if [ -n "$DLV_PATH" ] && [ "$DLV_PATH" != "/go/bin/dlv" ]; then \
|
|
mv "$DLV_PATH" /go/bin/dlv; \
|
|
fi && \
|
|
xx-verify /go/bin/dlv
|
|
|
|
# Copy Go module files
|
|
COPY backend/go.mod backend/go.sum ./
|
|
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
|
|
|
# Copy backend source
|
|
COPY backend/ ./
|
|
|
|
# Build arguments passed from main build context
|
|
ARG VERSION=dev
|
|
ARG VCS_REF=unknown
|
|
ARG BUILD_DATE=unknown
|
|
ARG BUILD_DEBUG=0
|
|
|
|
# Build the Go binary with version information injected via ldflags
|
|
# xx-go handles CGO and cross-compilation flags automatically
|
|
# Note: Go 1.25 defaults to gold linker for ARM64, but clang doesn't support -fuse-ld=gold
|
|
# We override with -extldflags=-fuse-ld=bfd to use the BFD linker for cross-compilation
|
|
# When BUILD_DEBUG=1, we preserve debug symbols (no -s -w) and disable optimizations
|
|
# for Delve debugging. Otherwise, strip symbols for smaller production binaries.
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/go/pkg/mod \
|
|
if [ "$BUILD_DEBUG" = "1" ]; then \
|
|
echo "Building with debug symbols for Delve..."; \
|
|
CGO_ENABLED=1 xx-go build \
|
|
-gcflags="all=-N -l" \
|
|
-ldflags "-extldflags=-fuse-ld=bfd \
|
|
-X github.com/Wikid82/charon/backend/internal/version.Version=${VERSION} \
|
|
-X github.com/Wikid82/charon/backend/internal/version.GitCommit=${VCS_REF} \
|
|
-X github.com/Wikid82/charon/backend/internal/version.BuildTime=${BUILD_DATE}" \
|
|
-o charon ./cmd/api; \
|
|
else \
|
|
echo "Building optimized production binary..."; \
|
|
CGO_ENABLED=1 xx-go build \
|
|
-ldflags "-s -w -extldflags=-fuse-ld=bfd \
|
|
-X github.com/Wikid82/charon/backend/internal/version.Version=${VERSION} \
|
|
-X github.com/Wikid82/charon/backend/internal/version.GitCommit=${VCS_REF} \
|
|
-X github.com/Wikid82/charon/backend/internal/version.BuildTime=${BUILD_DATE}" \
|
|
-o charon ./cmd/api; \
|
|
fi
|
|
|
|
# ---- 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)
|
|
# renovate: datasource=docker depName=golang
|
|
FROM --platform=$BUILDPLATFORM golang:1.25-trixie@sha256:fb4b74a39c7318d53539ebda43ccd3ecba6e447a78591889c0efc0a7235ea8b3 AS caddy-builder
|
|
ARG TARGETOS
|
|
ARG TARGETARCH
|
|
ARG CADDY_VERSION
|
|
|
|
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
|
|
|
|
# Build Caddy for the target architecture with security plugins.
|
|
# Two-stage approach: xcaddy generates go.mod, we patch it, then build from scratch.
|
|
# This ensures the final binary is compiled with fully patched dependencies.
|
|
# hadolint ignore=SC2016
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/go/pkg/mod \
|
|
sh -c 'set -e; \
|
|
export XCADDY_SKIP_CLEANUP=1; \
|
|
echo "Stage 1: Generate go.mod with xcaddy..."; \
|
|
# Run xcaddy to generate the build directory and go.mod
|
|
GOOS=$TARGETOS GOARCH=$TARGETARCH xcaddy build v${CADDY_VERSION} \
|
|
--with github.com/greenpau/caddy-security \
|
|
--with github.com/corazawaf/coraza-caddy/v2 \
|
|
--with github.com/hslatman/caddy-crowdsec-bouncer \
|
|
--with github.com/zhangjiayin/caddy-geoip2 \
|
|
--with github.com/mholt/caddy-ratelimit \
|
|
--output /tmp/caddy-initial || true; \
|
|
# Find the build directory created by xcaddy
|
|
BUILDDIR=$(ls -td /tmp/buildenv_* 2>/dev/null | head -1); \
|
|
if [ ! -d "$BUILDDIR" ] || [ ! -f "$BUILDDIR/go.mod" ]; then \
|
|
echo "ERROR: Build directory not found or go.mod missing"; \
|
|
exit 1; \
|
|
fi; \
|
|
echo "Found build directory: $BUILDDIR"; \
|
|
cd "$BUILDDIR"; \
|
|
echo "Stage 2: Apply security patches to go.mod..."; \
|
|
# Patch ALL dependencies BEFORE building the final binary
|
|
# These patches fix CVEs in transitive dependencies
|
|
# Renovate tracks these via regex manager in renovate.json
|
|
# renovate: datasource=go depName=github.com/expr-lang/expr
|
|
go get github.com/expr-lang/expr@v1.17.7; \
|
|
# Clean up go.mod and ensure all dependencies are resolved
|
|
go mod tidy; \
|
|
echo "Dependencies patched successfully"; \
|
|
# Remove any temporary binaries from initial xcaddy run
|
|
rm -f /tmp/caddy-initial; \
|
|
echo "Stage 3: Build final Caddy binary with patched dependencies..."; \
|
|
# Build the final binary from scratch with the fully patched go.mod
|
|
# This ensures no vulnerable metadata is embedded
|
|
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /usr/bin/caddy \
|
|
-ldflags "-w -s" -trimpath -tags "nobadger,nomysql,nopgx" .; \
|
|
echo "Build successful with patched dependencies"; \
|
|
# Verify the binary exists and is executable (no execution to avoid hang)
|
|
test -x /usr/bin/caddy || exit 1; \
|
|
echo "Caddy binary verified"; \
|
|
# Clean up temporary build directories
|
|
rm -rf /tmp/buildenv_* /tmp/caddy-initial'
|
|
|
|
# ---- CrowdSec Builder ----
|
|
# 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-trixie@sha256:fb4b74a39c7318d53539ebda43ccd3ecba6e447a78591889c0efc0a7235ea8b3 AS crowdsec-builder
|
|
COPY --from=xx / /
|
|
|
|
WORKDIR /tmp/crowdsec
|
|
|
|
ARG TARGETPLATFORM
|
|
ARG TARGETOS
|
|
ARG TARGETARCH
|
|
# CrowdSec version - Renovate can update this
|
|
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
|
ARG CROWDSEC_VERSION=1.7.6
|
|
|
|
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 .
|
|
|
|
# Patch dependencies to fix CVEs in transitive dependencies
|
|
# This follows the same pattern as Caddy's dependency patches
|
|
# renovate: datasource=go depName=github.com/expr-lang/expr
|
|
# renovate: datasource=go depName=golang.org/x/crypto
|
|
RUN go get github.com/expr-lang/expr@v1.17.7 && \
|
|
go get golang.org/x/crypto@v0.46.0 && \
|
|
go mod tidy
|
|
|
|
# Fix compatibility issues with expr-lang v1.17.7
|
|
# In v1.17.7, program.Source() returns file.Source struct instead of string
|
|
# The upstream fix is in main branch but not yet released
|
|
RUN sed -i 's/string(program\.Source())/program.Source().String()/g' pkg/exprhelpers/debugger.go
|
|
|
|
# Build CrowdSec binaries for target architecture with patched dependencies
|
|
# hadolint ignore=DL3059
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/go/pkg/mod \
|
|
CGO_ENABLED=1 xx-go build -o /crowdsec-out/crowdsec \
|
|
-ldflags "-s -w -X github.com/crowdsecurity/crowdsec/pkg/cwversion.Version=v${CROWDSEC_VERSION}" \
|
|
./cmd/crowdsec && \
|
|
xx-verify /crowdsec-out/crowdsec
|
|
|
|
# hadolint ignore=DL3059
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/go/pkg/mod \
|
|
CGO_ENABLED=1 xx-go build -o /crowdsec-out/cscli \
|
|
-ldflags "-s -w -X github.com/crowdsecurity/crowdsec/pkg/cwversion.Version=v${CROWDSEC_VERSION}" \
|
|
./cmd/crowdsec-cli && \
|
|
xx-verify /crowdsec-out/cscli
|
|
|
|
# Copy config files
|
|
RUN mkdir -p /crowdsec-out/config && \
|
|
cp -r config/* /crowdsec-out/config/ || true
|
|
|
|
# ---- CrowdSec Fallback (for architectures where build fails) ----
|
|
# renovate: datasource=docker depName=debian
|
|
FROM debian:trixie-slim@sha256:77ba0164de17b88dd0bf6cdc8f65569e6e5fa6cd256562998b62553134a00ef0 AS crowdsec-fallback
|
|
|
|
WORKDIR /tmp/crowdsec
|
|
|
|
ARG TARGETARCH
|
|
# CrowdSec version - Renovate can update this
|
|
# renovate: datasource=github-releases depName=crowdsecurity/crowdsec
|
|
ARG CROWDSEC_VERSION=1.7.6
|
|
|
|
# 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
|
|
# hadolint ignore=DL3059,SC2015
|
|
RUN set -eux; \
|
|
mkdir -p /crowdsec-out/bin /crowdsec-out/config; \
|
|
if [ "$TARGETARCH" = "amd64" ]; then \
|
|
echo "Downloading CrowdSec binaries for amd64 (fallback)..."; \
|
|
curl -fSL "https://github.com/crowdsecurity/crowdsec/releases/download/v${CROWDSEC_VERSION}/crowdsec-release.tgz" \
|
|
-o /tmp/crowdsec.tar.gz && \
|
|
tar -xzf /tmp/crowdsec.tar.gz -C /tmp && \
|
|
cp "/tmp/crowdsec-v${CROWDSEC_VERSION}/cmd/crowdsec-cli/cscli" /crowdsec-out/bin/ && \
|
|
cp "/tmp/crowdsec-v${CROWDSEC_VERSION}/cmd/crowdsec/crowdsec" /crowdsec-out/bin/ && \
|
|
chmod +x /crowdsec-out/bin/* && \
|
|
if [ -d "/tmp/crowdsec-v${CROWDSEC_VERSION}/config" ]; then \
|
|
cp -r "/tmp/crowdsec-v${CROWDSEC_VERSION}/config/"* /crowdsec-out/config/; \
|
|
fi && \
|
|
echo "CrowdSec fallback binaries installed successfully"; \
|
|
else \
|
|
echo "CrowdSec binaries not available for $TARGETARCH - skipping"; \
|
|
touch /crowdsec-out/bin/.placeholder /crowdsec-out/config/.placeholder; \
|
|
fi
|
|
|
|
# ---- Final Runtime with Caddy ----
|
|
FROM ${CADDY_IMAGE}
|
|
WORKDIR /app
|
|
|
|
# Install runtime dependencies for Charon, including bash for maintenance scripts
|
|
# 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
|
|
# binutils provides objdump for debug symbol detection in docker-entrypoint.sh
|
|
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 binutils \
|
|
&& 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 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
|
|
# This uses the publicly available GeoLite2 database
|
|
RUN mkdir -p /app/data/geoip && \
|
|
curl -L "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb" \
|
|
-o /app/data/geoip/GeoLite2-Country.mmdb
|
|
|
|
# Copy Caddy binary from caddy-builder (overwriting the one from base image)
|
|
COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
|
|
|
|
# Allow non-root to bind privileged ports (80/443) securely
|
|
RUN setcap 'cap_net_bind_service=+ep' /usr/bin/caddy
|
|
|
|
# Copy CrowdSec binaries from the crowdsec-builder stage (built with Go 1.25.5+)
|
|
# This ensures we don't have stdlib vulnerabilities from older Go versions
|
|
COPY --from=crowdsec-builder /crowdsec-out/crowdsec /usr/local/bin/crowdsec
|
|
COPY --from=crowdsec-builder /crowdsec-out/cscli /usr/local/bin/cscli
|
|
COPY --from=crowdsec-builder /crowdsec-out/config /etc/crowdsec.dist
|
|
|
|
# Verify CrowdSec binaries
|
|
RUN chmod +x /usr/local/bin/crowdsec /usr/local/bin/cscli 2>/dev/null || true; \
|
|
if [ -x /usr/local/bin/cscli ]; then \
|
|
echo "CrowdSec installed (built from source with Go 1.25):"; \
|
|
cscli version || echo "CrowdSec version check failed"; \
|
|
else \
|
|
echo "CrowdSec not available for this architecture"; \
|
|
fi
|
|
|
|
# Create required CrowdSec directories in runtime image
|
|
# NOTE: Do NOT create /etc/crowdsec here - it must be a symlink created at runtime by non-root user
|
|
RUN mkdir -p /var/lib/crowdsec/data /var/log/crowdsec /var/log/caddy \
|
|
/app/data/crowdsec/config /app/data/crowdsec/data && \
|
|
chown -R charon:charon /var/lib/crowdsec /var/log/crowdsec \
|
|
/app/data/crowdsec
|
|
|
|
# Generate CrowdSec default configs to .dist directory
|
|
RUN if command -v cscli >/dev/null; then \
|
|
mkdir -p /etc/crowdsec.dist && \
|
|
cscli config restore /etc/crowdsec.dist/ || \
|
|
cp -r /etc/crowdsec/* /etc/crowdsec.dist/ 2>/dev/null || true; \
|
|
fi
|
|
|
|
# Copy CrowdSec configuration templates from source
|
|
COPY configs/crowdsec/acquis.yaml /etc/crowdsec.dist/acquis.yaml
|
|
COPY configs/crowdsec/install_hub_items.sh /usr/local/bin/install_hub_items.sh
|
|
COPY configs/crowdsec/register_bouncer.sh /usr/local/bin/register_bouncer.sh
|
|
|
|
# Make CrowdSec scripts executable
|
|
RUN chmod +x /usr/local/bin/install_hub_items.sh /usr/local/bin/register_bouncer.sh
|
|
|
|
# Copy Go binary from backend builder
|
|
COPY --from=backend-builder /app/backend/charon /app/charon
|
|
RUN ln -s /app/charon /app/cpmp || true
|
|
# Copy Delve debugger (xx-go install places it in /go/bin)
|
|
COPY --from=backend-builder /go/bin/dlv /usr/local/bin/dlv
|
|
|
|
# Copy frontend build from frontend builder
|
|
COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
|
|
|
|
# Copy startup script
|
|
COPY .docker/docker-entrypoint.sh /docker-entrypoint.sh
|
|
RUN chmod +x /docker-entrypoint.sh
|
|
|
|
# Copy utility scripts (used for DB recovery and maintenance)
|
|
COPY scripts/ /app/scripts/
|
|
RUN chmod +x /app/scripts/db-recovery.sh
|
|
|
|
# Set default environment variables
|
|
ENV CHARON_ENV=production \
|
|
CHARON_DB_PATH=/app/data/charon.db \
|
|
CHARON_FRONTEND_DIR=/app/frontend/dist \
|
|
CHARON_CADDY_ADMIN_API=http://localhost:2019 \
|
|
CHARON_CADDY_CONFIG_DIR=/app/data/caddy \
|
|
CHARON_GEOIP_DB_PATH=/app/data/geoip/GeoLite2-Country.mmdb \
|
|
CHARON_HTTP_PORT=8080 \
|
|
CHARON_CROWDSEC_CONFIG_DIR=/app/data/crowdsec \
|
|
CHARON_PLUGINS_DIR=/app/plugins
|
|
# Create necessary directories
|
|
RUN mkdir -p /app/data /app/data/caddy /config /app/data/crowdsec
|
|
|
|
# Security: Create plugins directory with secure permissions
|
|
# Mode 0755: owner rwx, group rx, other rx (NOT world-writable)
|
|
# This satisfies the PluginLoaderService security check (mode & 0002 == 0)
|
|
RUN mkdir -p /app/plugins && chmod 755 /app/plugins
|
|
|
|
# Security: Set ownership of all application directories to non-root charon user
|
|
# Note: /etc/crowdsec will be created as a symlink at runtime, not owned directly
|
|
# Note: /app/plugins has 755 permissions (NOT world-writable) for security
|
|
RUN chown -R charon:charon /app /config /var/log/crowdsec /var/log/caddy && \
|
|
chown -R charon:charon /etc/crowdsec.dist 2>/dev/null || true && \
|
|
chown -R charon:charon /var/lib/crowdsec 2>/dev/null || true
|
|
|
|
# Re-declare build args for LABEL usage
|
|
ARG VERSION=dev
|
|
ARG BUILD_DATE
|
|
ARG VCS_REF
|
|
|
|
# OCI image labels for version metadata
|
|
LABEL org.opencontainers.image.title="Charon (CPMP legacy)" \
|
|
org.opencontainers.image.description="Web UI for managing Caddy reverse proxy configurations" \
|
|
org.opencontainers.image.version="${VERSION}" \
|
|
org.opencontainers.image.created="${BUILD_DATE}" \
|
|
org.opencontainers.image.revision="${VCS_REF}" \
|
|
org.opencontainers.image.source="https://github.com/Wikid82/charon" \
|
|
org.opencontainers.image.url="https://github.com/Wikid82/charon" \
|
|
org.opencontainers.image.vendor="charon" \
|
|
org.opencontainers.image.licenses="MIT"
|
|
|
|
# Expose ports
|
|
EXPOSE 80 443 443/udp 2019 8080
|
|
|
|
# Security: Add healthcheck to monitor container health
|
|
# Verifies the Charon API is responding correctly
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
|
CMD curl -f http://localhost:8080/api/v1/health || exit 1
|
|
|
|
# Create CrowdSec symlink as root before switching to non-root user
|
|
# This symlink allows CrowdSec to use persistent storage at /app/data/crowdsec/config
|
|
# while maintaining the expected /etc/crowdsec path for compatibility
|
|
RUN ln -sf /app/data/crowdsec/config /etc/crowdsec
|
|
|
|
# Security: Container starts as root to handle Docker socket group permissions,
|
|
# then the entrypoint script drops privileges to the charon user before starting
|
|
# applications. This approach:
|
|
# 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 gosu to securely drop privileges after setup.
|
|
|
|
# Use custom entrypoint to start both Caddy and Charon
|
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|