Files
Redflag/aggregator-server/Dockerfile
jpetree331 f97d4845af feat(security): A-1 Ed25519 key rotation + A-2 replay attack fixes
Complete RedFlag codebase with two major security audit implementations.

== A-1: Ed25519 Key Rotation Support ==

Server:
- SignCommand sets SignedAt timestamp and KeyID on every signature
- signing_keys database table (migration 020) for multi-key rotation
- InitializePrimaryKey registers active key at startup
- /api/v1/public-keys endpoint for rotation-aware agents
- SigningKeyQueries for key lifecycle management

Agent:
- Key-ID-aware verification via CheckKeyRotation
- FetchAndCacheAllActiveKeys for rotation pre-caching
- Cache metadata with TTL and staleness fallback
- SecurityLogger events for key rotation and command signing

== A-2: Replay Attack Fixes (F-1 through F-7) ==

F-5 CRITICAL - RetryCommand now signs via signAndCreateCommand
F-1 HIGH     - v3 format: "{agent_id}:{cmd_id}:{type}:{hash}:{ts}"
F-7 HIGH     - Migration 026: expires_at column with partial index
F-6 HIGH     - GetPendingCommands/GetStuckCommands filter by expires_at
F-2 HIGH     - Agent-side executedIDs dedup map with cleanup
F-4 HIGH     - commandMaxAge reduced from 24h to 4h
F-3 CRITICAL - Old-format commands rejected after 48h via CreatedAt

Verification fixes: migration idempotency (ETHOS #4), log format
compliance (ETHOS #1), stale comments updated.

All 24 tests passing. Docker --no-cache build verified.
See docs/ for full audit reports and deviation log (DEV-001 to DEV-019).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:25:47 -04:00

121 lines
4.4 KiB
Docker

# Stage 1: Build server binary
FROM golang:1.24-alpine AS server-builder
WORKDIR /app
# Install git for version detection
RUN apk add --no-cache git
# Copy go.mod and go.sum
COPY aggregator-server/go.mod aggregator-server/go.sum ./
RUN go mod download
# Copy .git to get version info
COPY .git/ ./.git/
# Extract semantic version from git (BASE_VERSION.COMMIT_COUNT)
RUN cd /app && \
# Get latest tag or default to 0.1.0 \
if git describe --tags --dirty --always >/dev/null 2>&1; then \
LATEST_TAG=$(git describe --tags --dirty --always); \
BASE_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//' | cut -d. -f1-3); \
else \
BASE_VERSION="0.1.0"; \
fi && \
# Count commits since tag \
COMMITS_SINCE=$(git rev-list $(git describe --tags --dirty --always 2>/dev/null)..HEAD 2>/dev/null | wc -l | tr -d ' ') && \
if [ "$COMMITS_SINCE" = "" ] || [ "$COMMITS_SINCE" -eq 0 ]; then BUILD=0; else BUILD=$COMMITS_SINCE; fi && \
VERSION="${BASE_VERSION}.${BUILD}" && \
echo "Building server version: $VERSION" && \
echo "$VERSION" > /app/version.txt
# Copy aggregator-server contents to /app (maintains correct directory structure)
COPY aggregator-server/ ./
# Build server with version injection
RUN VERSION=$(cat /app/version.txt) && \
CGO_ENABLED=0 go build \
-ldflags "-X github.com/Fimeg/RedFlag/aggregator-server/internal/version.AgentVersion=$VERSION" \
-o redflag-server cmd/server/main.go
# Stage 2: Build agent binaries for all platforms
FROM golang:1.24-alpine AS agent-builder
WORKDIR /build
# Install git for version detection
RUN apk add --no-cache git
# Copy .git directory to get version info
COPY .git/ ./.git/
# Generate semantic version from git (BASE_VERSION.COMMIT_COUNT)
# Examples:
# Tagged release: v0.1.26.0 → 0.1.26.0
# 5 commits after tag: 0.1.26.5
# No tags: 0.1.0.0
RUN cd /build && \
# Get latest tag or default to 0.1.0 \
if git describe --tags --dirty --always >/dev/null 2>&1; then \
LATEST_TAG=$(git describe --tags --dirty --always); \
BASE_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//' | cut -d. -f1-3); \
else \
BASE_VERSION="0.1.0"; \
fi && \
# Count commits since tag (0 if on tag) \
COMMITS_SINCE=$(git rev-list $(git describe --tags --dirty --always 2>/dev/null)..HEAD 2>/dev/null | wc -l | tr -d ' ') && \
if [ "$COMMITS_SINCE" = "" ] || [ "$COMMITS_SINCE" -eq 0 ]; then BUILD=0; else BUILD=$COMMITS_SINCE; fi && \
# Write semantic version (base.commits) \
VERSION="${BASE_VERSION}.${BUILD}" && \
echo "Building agent version: $VERSION" && \
echo "$VERSION" > /build/version.txt
# Copy agent source code
COPY aggregator-agent/ ./
# Build for Linux amd64
RUN VERSION=$(cat /build/version.txt) && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags "-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.Version=$VERSION" \
-o binaries/linux-amd64/redflag-agent ./cmd/agent
# Build for Linux arm64
RUN VERSION=$(cat /build/version.txt) && \
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build \
-ldflags "-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.Version=$VERSION" \
-o binaries/linux-arm64/redflag-agent ./cmd/agent
# Build for Windows amd64
RUN VERSION=$(cat /build/version.txt) && \
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \
-ldflags "-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.Version=$VERSION" \
-o binaries/windows-amd64/redflag-agent.exe ./cmd/agent
# Build for Windows arm64
RUN VERSION=$(cat /build/version.txt) && \
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build \
-ldflags "-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.Version=$VERSION" \
-o binaries/windows-arm64/redflag-agent.exe ./cmd/agent
# Stage 3: Final image with server and all agent binaries
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata bash
WORKDIR /app
# Copy server binary
COPY --from=server-builder /app/redflag-server .
COPY --from=server-builder /app/internal/database ./internal/database
# Copy all agent binaries
COPY --from=agent-builder /build/binaries ./binaries
# Copy and setup entrypoint script
# File is in aggregator-server/ directory relative to build context
COPY aggregator-server/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
EXPOSE 8080
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["./redflag-server"]