796 lines
23 KiB
Markdown
796 lines
23 KiB
Markdown
# 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 `curl`. The entrypoint uses curl for the Caddy readiness check. All `curl` calls must be replaced with `curl` equivalents.
|
|
|
|
#### Step 3.0: Replace curl with curl for Caddy Readiness Check
|
|
|
|
```bash
|
|
# BEFORE (Alpine - uses curl)
|
|
curl -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)
|