diff --git a/.github/workflows/build-cicd.yaml b/.github/workflows/build-cicd.yaml index 24e9d32..c1527dc 100644 --- a/.github/workflows/build-cicd.yaml +++ b/.github/workflows/build-cicd.yaml @@ -30,7 +30,8 @@ jobs: echo "TAG=${BRANCH_NAME}" >> $GITHUB_OUTPUT fi - - name: Build Docker image (no push) + # Build standard Ubuntu-based image + - name: Build Docker image (standard) uses: docker/build-push-action@v5 with: context: . @@ -40,22 +41,42 @@ jobs: push: false load: true + # Build minimal Chainguard static image + - name: Build Docker image (Chainguard) + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.chainguard + platforms: linux/amd64 + tags: cbaugus/rust_loadtest:${{ steps.docker_meta.outputs.TAG }}-Chainguard + push: false + load: true + - name: Install Syft run: | curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin - - name: Generate SBOM with Syft + # Generate SBOM for standard image + - name: Generate SBOM with Syft (standard) run: | syft --version - syft "docker:cbaugus/rust_loadtest:${{ steps.docker_meta.outputs.TAG }}" -o cyclonedx-json > sbom.cyclonedx.json + syft "docker:cbaugus/rust_loadtest:${{ steps.docker_meta.outputs.TAG }}" -o cyclonedx-json > sbom-standard.cyclonedx.json + + # Generate SBOM for Chainguard image + - name: Generate SBOM with Syft (Chainguard) + run: | + syft "docker:cbaugus/rust_loadtest:${{ steps.docker_meta.outputs.TAG }}-Chainguard" -o cyclonedx-json > sbom-chainguard.cyclonedx.json - - name: Upload SBOM artifact + - name: Upload SBOM artifacts uses: actions/upload-artifact@v4 with: name: sbom - path: sbom.cyclonedx.json + path: | + sbom-standard.cyclonedx.json + sbom-chainguard.cyclonedx.json - - name: Push Docker image + # Push standard Ubuntu image + - name: Push Docker image (standard) uses: docker/build-push-action@v5 with: context: . @@ -65,3 +86,14 @@ jobs: provenance: true push: true + # Push minimal Chainguard image + - name: Push Docker image (chainguard) + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.chainguard + platforms: linux/amd64 + tags: cbaugus/rust_loadtest:${{ steps.docker_meta.outputs.TAG }}-Chainguard + provenance: true + push: true + diff --git a/Dockerfile.chainguard b/Dockerfile.chainguard new file mode 100644 index 0000000..8bd53d9 --- /dev/null +++ b/Dockerfile.chainguard @@ -0,0 +1,38 @@ +# Multi-stage build for static binary with Chainguard +# This produces a minimal, secure image (~10-15 MB) with zero CVEs + +# --- Stage 1: Build static binary with musl --- +FROM rust:alpine AS builder + +WORKDIR /usr/src/app + +# Install musl development tools for static linking +RUN apk add --no-cache musl-dev + +# Copy source code +COPY . . + +# Add musl target for Rust +RUN rustup target add x86_64-unknown-linux-musl + +# Build static binary with all dependencies compiled in +# Using release profile for optimizations +RUN cargo build --release --target x86_64-unknown-linux-musl + +# --- Stage 2: Ultra-minimal Chainguard static runtime --- +# This image contains only: filesystem structure, CA certs, timezone data +# Size: ~2-5 MB base +# CVEs: Typically 0 +FROM cgr.dev/chainguard/static:latest + +# Copy the static binary from builder +COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/rust_loadtest /usr/local/bin/rust_loadtest + +# Expose Prometheus metrics port +EXPOSE 9090 + +# Chainguard images run as non-root user by default (UID 65532) +# No shell available in this image - maximum security + +# Run the application +ENTRYPOINT ["/usr/local/bin/rust_loadtest"] diff --git a/README.md b/README.md index 8b90e3f..c93c51a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,37 @@ Before you begin, ensure you have the following installed: * [**Rust**](https://www.rust-lang.org/tools/install): Rust toolchain (version 1.77 or newer recommended). * [**Docker**](https://docs.docker.com/get-docker/): Docker Engine to build and run the containerized application. +## Available Docker Images + +This tool is available in two image variants to suit different deployment scenarios: + +### Standard Image (Ubuntu-based) +**Tag:** `cbaugus/rust-loadtester:latest` or `cbaugus/rust-loadtester:` + +- **Base:** Ubuntu latest +- **Size:** ~80-100 MB +- **Use case:** Development, testing, debugging in lab environments +- **Features:** + - Full shell access for troubleshooting + - Standard system utilities available + - Easy to debug and inspect +- **Build:** `Dockerfile` + +### Static Image (Chainguard-based) +**Tag:** `cbaugus/rust-loadtester:latest-Chainguard` or `cbaugus/rust-loadtester:-Chainguard` + +- **Base:** Chainguard static (distroless) +- **Size:** ~10-15 MB (75% smaller) +- **Use case:** Production, secure environments, minimal attack surface +- **Features:** + - Zero to minimal CVEs (typically 0-2) + - No shell or unnecessary binaries + - Static binary with all dependencies compiled in + - Maximum security posture +- **Build:** `Dockerfile.static` + +**Recommendation:** Use the **static image** for production deployments in secure environments. Use the **standard image** for development and troubleshooting. + ## Project Structure ``` @@ -28,7 +59,8 @@ Before you begin, ensure you have the following installed: ├── Cargo.toml ├── src │ └── main.rs -└── Dockerfile +├── Dockerfile # Standard Ubuntu-based build +└── Dockerfile.static # Minimal Chainguard static build ```