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:
795
docs/plans/debian_migration_spec.md
Normal file
795
docs/plans/debian_migration_spec.md
Normal file
@@ -0,0 +1,795 @@
|
||||
# Alpine to Debian Slim Migration Specification
|
||||
|
||||
> **Version**: 1.0.0
|
||||
> **Created**: 2026-01-18
|
||||
> **Status**: PLANNING
|
||||
> **Author**: Planning Agent
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
### Security Rationale
|
||||
|
||||
The user has identified a critical CVE in Alpine Linux that necessitates migrating the Docker base images from Alpine to Debian slim. This migration addresses:
|
||||
|
||||
1. **Critical CVE Mitigation**: Immediate resolution of the identified Alpine Linux vulnerability
|
||||
2. **Broader Security Posture**: Debian's larger security team and faster CVE response times
|
||||
3. **glibc vs musl Compatibility**: Eliminates potential musl libc edge cases that can cause subtle bugs in Go binaries with CGO
|
||||
4. **Long-term Maintainability**: Debian slim provides a battle-tested, stable base with predictable security update cycles
|
||||
|
||||
### Key Benefits of Debian Slim
|
||||
|
||||
| Aspect | Alpine | Debian Slim | Advantage |
|
||||
|--------|--------|-------------|-----------|
|
||||
| Security Updates | Community-driven | Dedicated security team (Debian Security Team) | Faster CVE patches |
|
||||
| C Library | musl libc | glibc | Better compatibility with CGO |
|
||||
| Package Availability | ~10k packages | ~60k packages | More comprehensive |
|
||||
| DNS Resolution | musl DNS bugs known | glibc mature DNS | More reliable |
|
||||
| Image Size | ~5MB base | ~25MB base | Alpine smaller, but acceptable trade-off |
|
||||
|
||||
---
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Dockerfile Structure Overview
|
||||
|
||||
The current `Dockerfile` is a multi-stage build with the following Alpine-based stages:
|
||||
|
||||
#### Builder Stages (Alpine-based)
|
||||
|
||||
| Stage | Base Image | Purpose |
|
||||
|-------|------------|---------|
|
||||
| `xx` | `tonistiigi/xx:1.9.0` | Cross-compilation helpers (unchanged) |
|
||||
| `frontend-builder` | `node:24.13.0-alpine` | Build React frontend |
|
||||
| `backend-builder` | `golang:1.25-alpine` | Build Go backend with CGO |
|
||||
| `caddy-builder` | `golang:1.25-alpine` | Build Caddy with plugins |
|
||||
| `crowdsec-builder` | `golang:1.25.6-alpine` | Build CrowdSec from source |
|
||||
| `crowdsec-fallback` | `alpine:3.23` | Fallback binary download |
|
||||
|
||||
#### Runtime Stage (Alpine-based)
|
||||
|
||||
| Stage | Base Image | Purpose |
|
||||
|-------|------------|---------|
|
||||
| Final runtime | `alpine:3.23` (via `CADDY_IMAGE` ARG) | Production runtime |
|
||||
|
||||
### Alpine Packages Currently Installed
|
||||
|
||||
#### Builder Stage Packages (apk)
|
||||
|
||||
```dockerfile
|
||||
# backend-builder
|
||||
apk add --no-cache clang lld
|
||||
xx-apk add --no-cache gcc musl-dev sqlite-dev
|
||||
|
||||
# caddy-builder
|
||||
apk add --no-cache git
|
||||
|
||||
# crowdsec-builder
|
||||
apk add --no-cache git clang lld
|
||||
xx-apk add --no-cache gcc musl-dev
|
||||
|
||||
# crowdsec-fallback
|
||||
apk add --no-cache curl tar
|
||||
```
|
||||
|
||||
#### Runtime Stage Packages (apk)
|
||||
|
||||
```dockerfile
|
||||
# Final runtime image
|
||||
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
|
||||
```
|
||||
|
||||
### Alpine-Specific Commands in Dockerfile
|
||||
|
||||
1. **User/Group Creation**:
|
||||
```dockerfile
|
||||
RUN addgroup -g 1000 charon && \
|
||||
adduser -D -u 1000 -G charon -h /app -s /sbin/nologin charon
|
||||
```
|
||||
|
||||
2. **Package Management**:
|
||||
- `apk add --no-cache`
|
||||
- `apk --no-cache upgrade`
|
||||
- `xx-apk add --no-cache` (cross-compilation)
|
||||
|
||||
3. **Privilege Dropping**:
|
||||
- Uses `su-exec` (Alpine-specific lightweight sudo replacement)
|
||||
|
||||
### Alpine-Specific Commands in docker-entrypoint.sh
|
||||
|
||||
1. **User Group Management**:
|
||||
```bash
|
||||
addgroup -g "$DOCKER_SOCK_GID" docker 2>/dev/null || true
|
||||
addgroup charon docker 2>/dev/null || true
|
||||
addgroup charon "$GROUP_NAME" 2>/dev/null || true
|
||||
```
|
||||
|
||||
2. **File Statistics**:
|
||||
```bash
|
||||
stat -c '%a' "$PLUGINS_DIR" # Alpine stat syntax
|
||||
stat -c '%g' /var/run/docker.sock
|
||||
```
|
||||
|
||||
### CI/CD Workflow References
|
||||
|
||||
Files referencing Alpine that need updates:
|
||||
|
||||
| File | Line | Reference |
|
||||
|------|------|-----------|
|
||||
| `.github/workflows/docker-build.yml` | 103-104 | `caddy:2-alpine` image pull |
|
||||
| `.github/workflows/security-weekly-rebuild.yml` | 53-54 | `caddy:2-alpine` image pull |
|
||||
| `.github/workflows/security-weekly-rebuild.yml` | 127 | `apk info` command for package check |
|
||||
|
||||
---
|
||||
|
||||
## Target State: Debian Slim Configuration
|
||||
|
||||
### Recommended Base Images
|
||||
|
||||
| Current Alpine Image | Debian Slim Replacement | Notes |
|
||||
|---------------------|-------------------------|-------|
|
||||
| `node:24.13.0-alpine` | `node:24.13.0-slim` | Node.js official slim variant |
|
||||
| `golang:1.25-alpine` | `golang:1.25-bookworm` | Go official Debian variant |
|
||||
| `golang:1.25.6-alpine` | `golang:1.25.6-bookworm` | CrowdSec builder |
|
||||
| `alpine:3.23` | `debian:bookworm-slim` | Runtime image |
|
||||
| `caddy:2-alpine` | Build Caddy ourselves | Already building from source |
|
||||
|
||||
### Package Mapping: Alpine → Debian
|
||||
|
||||
| Alpine Package | Debian Equivalent | Notes |
|
||||
|----------------|-------------------|-------|
|
||||
| `bash` | `bash` | Same |
|
||||
| `ca-certificates` | `ca-certificates` | Same |
|
||||
| `sqlite-libs` | `libsqlite3-0` | Runtime library |
|
||||
| `sqlite` | `sqlite3` | CLI tool |
|
||||
| `sqlite-dev` | `libsqlite3-dev` | Build dependency |
|
||||
| `tzdata` | `tzdata` | Same |
|
||||
| `curl` | `curl` | Same |
|
||||
| `gettext` | `gettext-base` | Smaller variant with envsubst |
|
||||
| `su-exec` | `gosu` | Debian equivalent |
|
||||
| `libcap-utils` | `libcap2-bin` | Contains setcap |
|
||||
| `clang` | `clang` | Same |
|
||||
| `lld` | `lld` | Same |
|
||||
| `gcc` | `gcc` | Same (may need build-essential) |
|
||||
| `musl-dev` | `libc6-dev` | glibc development files |
|
||||
| `git` | `git` | Same |
|
||||
| `tar` | `tar` | Usually pre-installed |
|
||||
| `c-ares` | `libc-ares2` | Async DNS library |
|
||||
|
||||
### User/Group Creation Syntax Changes
|
||||
|
||||
| Operation | Alpine | Debian |
|
||||
|-----------|--------|--------|
|
||||
| Create group | `addgroup -g 1000 charon` | `groupadd -g 1000 charon` |
|
||||
| Create user | `adduser -D -u 1000 -G charon -h /app -s /sbin/nologin charon` | `useradd -u 1000 -g charon -d /app -s /usr/sbin/nologin -M charon` |
|
||||
| Add to group | `addgroup charon docker` | `usermod -aG docker charon` |
|
||||
|
||||
> **Note**: Debian uses `/usr/sbin/nologin` instead of Alpine's `/sbin/nologin`
|
||||
|
||||
### Entrypoint Script Changes
|
||||
|
||||
The `docker-entrypoint.sh` requires these changes:
|
||||
|
||||
1. **Replace `addgroup`/`adduser` with `groupadd`/`useradd`**
|
||||
2. **Replace `su-exec` with `gosu`**
|
||||
3. **Update stat command syntax** (BSD vs GNU - Debian uses GNU which is same)
|
||||
|
||||
---
|
||||
|
||||
## Detailed Migration Steps
|
||||
|
||||
### Phase 1: Builder Stage Migrations
|
||||
|
||||
#### Step 1.1: Frontend Builder
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 27-47
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
FROM --platform=$BUILDPLATFORM node:24.13.0-alpine AS frontend-builder
|
||||
|
||||
# AFTER (Debian slim)
|
||||
FROM --platform=$BUILDPLATFORM node:24.13.0-slim AS frontend-builder
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
- No package installation changes needed (npm handles dependencies)
|
||||
- Environment variables remain the same
|
||||
|
||||
#### Step 1.2: Backend Builder
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 49-143
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS backend-builder
|
||||
# ...
|
||||
RUN apk add --no-cache clang lld
|
||||
RUN xx-apk add --no-cache gcc musl-dev sqlite-dev
|
||||
|
||||
# AFTER (Debian)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS backend-builder
|
||||
# ...
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
clang lld \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
**Critical Change - CGO with glibc**:
|
||||
- Remove the clang wrapper workaround for ARM64 gold linker (lines 67-91)
|
||||
- glibc environments handle this natively
|
||||
- Change `xx-apk` to cross-compilation apt packages or use `TARGETPLATFORM` specific installs
|
||||
|
||||
**xx-go Cross Compilation Notes**:
|
||||
- The `xx` helper supports Debian-based images
|
||||
- Replace `xx-apk` with appropriate Debian cross-compilation setup
|
||||
|
||||
#### Step 1.3: Caddy Builder
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 145-202
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS caddy-builder
|
||||
# ...
|
||||
RUN apk add --no-cache git
|
||||
|
||||
# AFTER (Debian)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS caddy-builder
|
||||
# ...
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### Step 1.4: CrowdSec Builder
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 204-256
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.6-alpine AS crowdsec-builder
|
||||
# ...
|
||||
RUN apk add --no-cache git clang lld
|
||||
RUN xx-apk add --no-cache gcc musl-dev
|
||||
|
||||
# AFTER (Debian)
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.6-bookworm AS crowdsec-builder
|
||||
# ...
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git clang lld gcc libc6-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### Step 1.5: CrowdSec Fallback
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 258-293
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
FROM alpine:3.23 AS crowdsec-fallback
|
||||
# ...
|
||||
RUN apk add --no-cache curl tar
|
||||
|
||||
# AFTER (Debian)
|
||||
FROM debian:bookworm-slim AS crowdsec-fallback
|
||||
# ...
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl ca-certificates tar \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
> **⚠️ IMPORTANT**: Debian slim does NOT include `tar` by default. It must be explicitly installed for CrowdSec binary extraction.
|
||||
|
||||
### Phase 2: Runtime Stage Migration
|
||||
|
||||
#### Step 2.1: Base Image Change
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 23, 295-303
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
ARG CADDY_IMAGE=alpine:3.23
|
||||
# ...
|
||||
FROM ${CADDY_IMAGE}
|
||||
# ...
|
||||
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
|
||||
|
||||
# AFTER (Debian)
|
||||
ARG CADDY_IMAGE=debian:bookworm-slim
|
||||
# ...
|
||||
FROM ${CADDY_IMAGE}
|
||||
# ...
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bash ca-certificates libsqlite3-0 sqlite3 tzdata curl gettext-base gosu libcap2-bin libc-ares2 \
|
||||
&& apt-get upgrade -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### Step 2.2: User Creation
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Lines**: 307-308
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine)
|
||||
RUN addgroup -g 1000 charon && \
|
||||
adduser -D -u 1000 -G charon -h /app -s /sbin/nologin charon
|
||||
|
||||
# AFTER (Debian)
|
||||
RUN groupadd -g 1000 charon && \
|
||||
useradd -u 1000 -g charon -d /app -s /usr/sbin/nologin -M charon
|
||||
```
|
||||
|
||||
> **⚠️ PATH CHANGE**: Debian uses `/usr/sbin/nologin` instead of Alpine's `/sbin/nologin`.
|
||||
|
||||
#### Step 2.3: setcap Command
|
||||
|
||||
**File**: `Dockerfile`
|
||||
**Line**: 318
|
||||
|
||||
```dockerfile
|
||||
# BEFORE (Alpine - same)
|
||||
RUN setcap 'cap_net_bind_service=+ep' /usr/bin/caddy
|
||||
|
||||
# AFTER (Debian - same, but requires libcap2-bin)
|
||||
RUN setcap 'cap_net_bind_service=+ep' /usr/bin/caddy
|
||||
```
|
||||
|
||||
### Phase 3: Entrypoint Script Migration
|
||||
|
||||
**File**: `.docker/docker-entrypoint.sh`
|
||||
|
||||
> **⚠️ CRITICAL**: Debian slim does NOT include `wget`. The entrypoint uses wget for the Caddy readiness check. All `wget` calls must be replaced with `curl` equivalents.
|
||||
|
||||
#### Step 3.0: Replace wget with curl for Caddy Readiness Check
|
||||
|
||||
```bash
|
||||
# BEFORE (Alpine - uses wget)
|
||||
wget -q --spider http://localhost:2019/config/ || exit 1
|
||||
|
||||
# AFTER (Debian - uses curl)
|
||||
curl -sf http://localhost:2019/config/ > /dev/null || exit 1
|
||||
```
|
||||
|
||||
#### Step 3.1: Replace su-exec with gosu
|
||||
|
||||
```bash
|
||||
# BEFORE (Alpine)
|
||||
run_as_charon() {
|
||||
if is_root; then
|
||||
su-exec charon "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# AFTER (Debian)
|
||||
run_as_charon() {
|
||||
if is_root; then
|
||||
gosu charon "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 3.2: Replace addgroup/adduser with groupadd/usermod
|
||||
|
||||
```bash
|
||||
# BEFORE (Alpine)
|
||||
addgroup -g "$DOCKER_SOCK_GID" docker 2>/dev/null || true
|
||||
addgroup charon docker 2>/dev/null || true
|
||||
|
||||
# AFTER (Debian)
|
||||
groupadd -g "$DOCKER_SOCK_GID" docker 2>/dev/null || true
|
||||
usermod -aG docker charon 2>/dev/null || true
|
||||
```
|
||||
|
||||
```bash
|
||||
# BEFORE (Alpine)
|
||||
addgroup charon "$GROUP_NAME" 2>/dev/null || true
|
||||
|
||||
# AFTER (Debian)
|
||||
usermod -aG "$GROUP_NAME" charon 2>/dev/null || true
|
||||
```
|
||||
|
||||
#### Step 3.3: stat Command (No Change Required)
|
||||
|
||||
Both Alpine and Debian use GNU coreutils `stat`, so the syntax remains:
|
||||
```bash
|
||||
stat -c '%a' "$PLUGINS_DIR"
|
||||
stat -c '%g' /var/run/docker.sock
|
||||
```
|
||||
|
||||
### Phase 4: CI/CD Workflow Updates
|
||||
|
||||
#### Step 4.1: docker-build.yml
|
||||
|
||||
**File**: `.github/workflows/docker-build.yml`
|
||||
**Lines**: 103-104
|
||||
|
||||
```yaml
|
||||
# BEFORE
|
||||
run: |
|
||||
docker pull caddy:2-alpine
|
||||
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' caddy:2-alpine)
|
||||
|
||||
# AFTER
|
||||
# Remove this step entirely - we build Caddy from source
|
||||
# Or update to pull debian:bookworm-slim for digest verification
|
||||
run: |
|
||||
docker pull debian:bookworm-slim
|
||||
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' debian:bookworm-slim)
|
||||
```
|
||||
|
||||
#### Step 4.2: security-weekly-rebuild.yml
|
||||
|
||||
**File**: `.github/workflows/security-weekly-rebuild.yml`
|
||||
|
||||
**Lines 53-54** (Caddy digest):
|
||||
```yaml
|
||||
# Remove or update similar to docker-build.yml
|
||||
```
|
||||
|
||||
**Lines 127-133** (Package version check):
|
||||
```yaml
|
||||
# BEFORE
|
||||
- name: Check Alpine package versions
|
||||
run: |
|
||||
echo "Checking key security packages:" >> $GITHUB_STEP_SUMMARY
|
||||
docker run --rm --entrypoint "" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} \
|
||||
sh -c "apk update >/dev/null 2>&1 && apk info c-ares curl libcurl openssl" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# AFTER
|
||||
- name: Check Debian package versions
|
||||
run: |
|
||||
echo "Checking key security packages:" >> $GITHUB_STEP_SUMMARY
|
||||
docker run --rm --entrypoint "" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} \
|
||||
sh -c "dpkg -l | grep -E 'libc-ares|curl|libcurl|openssl|libssl'" >> $GITHUB_STEP_SUMMARY
|
||||
```
|
||||
|
||||
### Phase 5: Cross-Compilation Considerations
|
||||
|
||||
#### xx Helper Compatibility
|
||||
|
||||
The `tonistiigi/xx` project supports both Alpine and Debian. Key changes:
|
||||
|
||||
1. **Remove xx-apk usage**: Replace with native apt-get for the target architecture
|
||||
2. **CGO Cross-Compilation**: Debian has better cross-compilation toolchain support
|
||||
3. **Remove Gold Linker Workaround**: The clang wrapper hack (lines 67-91) for Go 1.25 ARM64 can be removed
|
||||
|
||||
```dockerfile
|
||||
# Debian cross-compilation setup (replaces xx-apk)
|
||||
ARG TARGETARCH
|
||||
RUN dpkg --add-architecture ${TARGETARCH} && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gcc-$(dpkg-architecture -A ${TARGETARCH} -qDEB_TARGET_GNU_TYPE) \
|
||||
libc6-dev:${TARGETARCH} \
|
||||
libsqlite3-dev:${TARGETARCH} \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
**Alternative**: Continue using `xx` helper which has Debian support:
|
||||
```dockerfile
|
||||
COPY --from=xx / /
|
||||
RUN xx-apt install -y libc6-dev libsqlite3-dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Phase 1: Local Build Verification
|
||||
|
||||
1. **Build All Architectures**:
|
||||
```bash
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t charon:debian-test .
|
||||
```
|
||||
|
||||
2. **Verify Binary Execution**:
|
||||
```bash
|
||||
docker run --rm charon:debian-test /app/charon --version
|
||||
docker run --rm charon:debian-test caddy version
|
||||
docker run --rm charon:debian-test cscli version
|
||||
```
|
||||
|
||||
3. **Verify Package Installation**:
|
||||
```bash
|
||||
docker run --rm --entrypoint bash charon:debian-test -c "which gosu setcap curl sqlite3"
|
||||
```
|
||||
|
||||
### Phase 2: Functional Testing
|
||||
|
||||
1. **Run E2E Playwright Tests**:
|
||||
```bash
|
||||
npx playwright test --project=chromium
|
||||
```
|
||||
|
||||
2. **Run Backend Unit Tests**:
|
||||
```bash
|
||||
make test-backend
|
||||
```
|
||||
|
||||
3. **Run Docker Compose Stack**:
|
||||
```bash
|
||||
docker compose -f .docker/compose/docker-compose.yml up -d
|
||||
# Verify all services start correctly
|
||||
curl http://localhost:8080/api/v1/health
|
||||
```
|
||||
|
||||
### Phase 3: Security Verification
|
||||
|
||||
1. **Trivy Vulnerability Scan**:
|
||||
```bash
|
||||
trivy image charon:debian-test --severity CRITICAL,HIGH
|
||||
```
|
||||
|
||||
2. **Verify No Alpine CVE Present**:
|
||||
```bash
|
||||
trivy image charon:debian-test | grep -i alpine
|
||||
# Should return nothing
|
||||
```
|
||||
|
||||
3. **Verify User Permissions**:
|
||||
```bash
|
||||
docker run --rm charon:debian-test id
|
||||
# Should show: uid=1000(charon) gid=1000(charon)
|
||||
```
|
||||
|
||||
### Phase 4: Performance Validation
|
||||
|
||||
1. **Compare Image Sizes**:
|
||||
```bash
|
||||
docker images | grep charon
|
||||
# Alpine: ~150MB, Debian: ~200MB (acceptable)
|
||||
```
|
||||
|
||||
2. **Startup Time Comparison**:
|
||||
```bash
|
||||
time docker run --rm charon:debian-test /app/charon --version
|
||||
```
|
||||
|
||||
3. **Memory Usage Comparison**:
|
||||
```bash
|
||||
docker stats --no-stream charon-container
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
### Immediate Rollback
|
||||
|
||||
1. **Revert Dockerfile Changes**:
|
||||
```bash
|
||||
git checkout main -- Dockerfile
|
||||
git checkout main -- .docker/docker-entrypoint.sh
|
||||
```
|
||||
|
||||
2. **Rebuild with Alpine**:
|
||||
```bash
|
||||
docker buildx build --no-cache -t charon:alpine-rollback .
|
||||
```
|
||||
|
||||
### Staged Rollback
|
||||
|
||||
If issues are discovered post-deployment:
|
||||
|
||||
1. **Tag Current (Debian) Image**:
|
||||
```bash
|
||||
docker tag ghcr.io/wikid82/charon:latest ghcr.io/wikid82/charon:debian-v1
|
||||
```
|
||||
|
||||
2. **Push Previous Alpine Image**:
|
||||
```bash
|
||||
docker tag ghcr.io/wikid82/charon:v{previous} ghcr.io/wikid82/charon:latest
|
||||
docker push ghcr.io/wikid82/charon:latest
|
||||
```
|
||||
|
||||
3. **Document Rollback**:
|
||||
- Create GitHub issue documenting the reason
|
||||
- Update CHANGELOG.md with rollback notice
|
||||
|
||||
### Rollback Criteria
|
||||
|
||||
Trigger rollback if any of these occur:
|
||||
- [ ] Critical security vulnerability in Debian base
|
||||
- [ ] Application crashes on startup
|
||||
- [ ] E2E tests fail > 10%
|
||||
- [ ] Memory usage increases > 50%
|
||||
- [ ] Build times increase > 3x
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Security Features to Maintain
|
||||
|
||||
| Feature | Alpine Implementation | Debian Implementation | Status |
|
||||
|---------|----------------------|----------------------|--------|
|
||||
| Non-root user | `adduser -D` | `useradd -M` | ✅ Maintain |
|
||||
| Privilege dropping | `su-exec` | `gosu` | ✅ Maintain |
|
||||
| Capability binding | `setcap` via `libcap-utils` | `setcap` via `libcap2-bin` | ✅ Maintain |
|
||||
| Read-only filesystem | N/A | N/A | N/A |
|
||||
| Minimal packages | `--no-cache` | `--no-install-recommends` | ✅ Maintain |
|
||||
| Security upgrades | `apk upgrade` | `apt-get upgrade` | ✅ Maintain |
|
||||
| HEALTHCHECK | Present | Present | ✅ Maintain |
|
||||
|
||||
### Security Enhancements with Debian
|
||||
|
||||
1. **glibc Security**: Better Address Space Layout Randomization (ASLR)
|
||||
2. **Faster CVE Patches**: Debian Security Team is larger and faster
|
||||
3. **No musl Edge Cases**: Eliminates subtle bugs in Go binaries with CGO
|
||||
4. **SELinux Compatibility**: Debian has better SELinux support if needed
|
||||
|
||||
### Security Scanning Updates
|
||||
|
||||
Update Trivy configuration to scan for Debian-specific vulnerabilities:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/security-weekly-rebuild.yml
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@...
|
||||
with:
|
||||
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Pre-Migration
|
||||
|
||||
- [ ] Create feature branch: `feature/debian-migration`
|
||||
- [ ] Document current Alpine image hashes for comparison
|
||||
- [ ] Run full E2E test suite as baseline
|
||||
- [ ] Create backup of working Dockerfile
|
||||
- [ ] **Backup current production image for rollback**:
|
||||
```bash
|
||||
docker tag ghcr.io/wikid82/charon:latest ghcr.io/wikid82/charon:pre-debian-migration
|
||||
docker push ghcr.io/wikid82/charon:pre-debian-migration
|
||||
```
|
||||
|
||||
### Dockerfile Changes
|
||||
|
||||
- [ ] Update `frontend-builder` stage to Debian slim
|
||||
- [ ] Update `backend-builder` stage to Debian bookworm
|
||||
- [ ] Update `caddy-builder` stage to Debian bookworm
|
||||
- [ ] Update `crowdsec-builder` stage to Debian bookworm
|
||||
- [ ] Update `crowdsec-fallback` stage to Debian slim
|
||||
- [ ] Update final runtime stage to Debian slim
|
||||
- [ ] Update `CADDY_IMAGE` ARG default
|
||||
- [ ] Replace all `apk` commands with `apt-get`
|
||||
- [ ] Update user/group creation commands
|
||||
- [ ] Replace `su-exec` with `gosu`
|
||||
- [ ] Remove ARM64 clang wrapper workaround
|
||||
- [ ] Update cross-compilation setup for xx helper
|
||||
|
||||
### Entrypoint Changes
|
||||
|
||||
- [ ] Replace `su-exec` with `gosu`
|
||||
- [ ] Replace `addgroup` with `groupadd`
|
||||
- [ ] Replace `adduser` with `usermod -aG`
|
||||
|
||||
### CI/CD Changes
|
||||
|
||||
- [ ] Update `docker-build.yml` Caddy digest step
|
||||
- [ ] Update `security-weekly-rebuild.yml` package check
|
||||
- [ ] Update any other workflows referencing Alpine
|
||||
- [ ] **Update Renovate configuration** (`renovate.json`) to track Debian base image updates (see Appendix B)
|
||||
|
||||
### Testing
|
||||
|
||||
- [ ] Build multi-architecture image (amd64, arm64)
|
||||
- [ ] Run all E2E Playwright tests
|
||||
- [ ] Run all backend unit tests
|
||||
- [ ] Run Trivy vulnerability scan
|
||||
- [ ] Verify non-root user execution
|
||||
- [ ] Verify CrowdSec initialization
|
||||
- [ ] Verify Caddy startup
|
||||
- [ ] **gosu functionality test**: Verify privilege dropping works correctly
|
||||
```bash
|
||||
docker run --rm charon:debian-test gosu charon id
|
||||
# Expected: uid=1000(charon) gid=1000(charon) groups=1000(charon)
|
||||
docker run --rm charon:debian-test gosu charon whoami
|
||||
# Expected: charon
|
||||
```
|
||||
- [ ] **Docker socket integration test**: Verify socket group mapping works
|
||||
```bash
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock charon:debian-test \
|
||||
bash -c "stat -c '%g' /var/run/docker.sock && groups charon"
|
||||
# Verify charon user is added to the docker socket's group
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] Update README.md if any user-facing changes
|
||||
- [ ] Update CHANGELOG.md with migration details
|
||||
- [ ] Update `docs/DOCKER.md` with Debian-specific instructions
|
||||
- [ ] Update `docs/features.md` to reflect base image change
|
||||
- [ ] Archive this plan to `docs/implementation/`
|
||||
|
||||
### Post-Migration
|
||||
|
||||
- [ ] Monitor production for 48 hours
|
||||
- [ ] Verify no regression in vulnerability reports
|
||||
- [ ] Close related security issues/CVEs
|
||||
- [ ] Remove any Alpine-specific workarounds from codebase
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Complete Debian Package List
|
||||
|
||||
```dockerfile
|
||||
# Runtime packages
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gettext-base \
|
||||
gosu \
|
||||
libc-ares2 \
|
||||
libcap2-bin \
|
||||
libsqlite3-0 \
|
||||
sqlite3 \
|
||||
tzdata \
|
||||
&& apt-get upgrade -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
## Appendix B: Renovate Configuration Updates
|
||||
|
||||
If using Renovate for dependency management, update `renovate.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"regexManagers": [
|
||||
{
|
||||
"fileMatch": ["^Dockerfile$"],
|
||||
"matchStrings": ["ARG CADDY_IMAGE=debian:(?<currentValue>[\\w.-]+)"],
|
||||
"depNameTemplate": "debian",
|
||||
"datasourceTemplate": "docker"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Appendix C: Image Size Comparison (Expected)
|
||||
|
||||
| Component | Alpine Size | Debian Size | Delta |
|
||||
|-----------|-------------|-------------|-------|
|
||||
| Base image | 5 MB | 25 MB | +20 MB |
|
||||
| Runtime packages | 45 MB | 55 MB | +10 MB |
|
||||
| Go binary (Charon) | 30 MB | 30 MB | 0 |
|
||||
| Caddy binary | 45 MB | 45 MB | 0 |
|
||||
| CrowdSec binaries | 25 MB | 25 MB | 0 |
|
||||
| Frontend assets | 10 MB | 10 MB | 0 |
|
||||
| **Total** | **~160 MB** | **~190 MB** | **+30 MB** |
|
||||
|
||||
*Note: Actual sizes may vary. The ~30MB increase is an acceptable trade-off for improved security.*
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Debian Docker Official Images](https://hub.docker.com/_/debian)
|
||||
- [Node.js Docker Official Images](https://hub.docker.com/_/node)
|
||||
- [Go Docker Official Images](https://hub.docker.com/_/golang)
|
||||
- [gosu GitHub Repository](https://github.com/tianon/gosu)
|
||||
- [tonistiigi/xx Cross-Compilation](https://github.com/tonistiigi/xx)
|
||||
- [Alpine vs Debian for Docker](https://docs.docker.com/build/building/best-practices/)
|
||||
- [musl vs glibc Considerations](https://wiki.musl-libc.org/functional-differences-from-glibc.html)
|
||||
Reference in New Issue
Block a user