From 78e85ed80c50b10b5e98ce9e1f36b667f69eee8c Mon Sep 17 00:00:00 2001 From: Thomas Henry Thirlwall Date: Tue, 12 May 2026 22:31:18 -0500 Subject: [PATCH 1/2] feat: add cryptographically signed Docker images with Cosign - Build and push Docker images to GHCR on every release - Sign images with Cosign using keyless GitHub OIDC authentication - Publish both full (all-in-one) and minimal (lightweight) image variants - Tag images with both version and latest/minimal tags - Add comprehensive user documentation for signature verification - Update README to reference Docker signature verification guide Closes #133 --- .github/workflows/release-traceway.yml | 74 +++++++++++- DOCKER_SIGNATURES.md | 157 +++++++++++++++++++++++++ README.md | 2 + 3 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 DOCKER_SIGNATURES.md diff --git a/.github/workflows/release-traceway.yml b/.github/workflows/release-traceway.yml index 12dcd878..51e5abe6 100644 --- a/.github/workflows/release-traceway.yml +++ b/.github/workflows/release-traceway.yml @@ -31,6 +31,8 @@ on: permissions: contents: write actions: write + packages: write + id-token: write jobs: release: @@ -160,10 +162,76 @@ jobs: gh workflow run release-website.yml --ref "${{ github.ref_name }}" gh workflow run release-docs.yml --ref "${{ github.ref_name }}" + build-docker: + runs-on: ubuntu-latest + needs: release + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push full image + id: docker_build_full + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: | + ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }} + ghcr.io/${{ github.repository_owner }}/traceway:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build and push minimal image + id: docker_build_minimal + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.minimal + push: true + tags: | + ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}-minimal + ghcr.io/${{ github.repository_owner }}/traceway:minimal + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + with: + cosign-release: 'v2.2.0' + + - name: Sign full image + env: + COSIGN_EXPERIMENTAL: 1 + run: | + cosign sign --yes ghcr.io/${{ github.repository_owner }}/traceway@${{ steps.docker_build_full.outputs.digest }} + + - name: Sign minimal image + env: + COSIGN_EXPERIMENTAL: 1 + run: | + cosign sign --yes ghcr.io/${{ github.repository_owner }}/traceway@${{ steps.docker_build_minimal.outputs.digest }} + - name: Summary run: | - echo "Released backend/v${{ inputs.version }}" >> "$GITHUB_STEP_SUMMARY" + echo "### Docker Images (signed with Cosign)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Full Image** (includes ClickHouse, PostgreSQL, supervisord):" >> "$GITHUB_STEP_SUMMARY" + echo "\`ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Minimal Image** (external databases only):" >> "$GITHUB_STEP_SUMMARY" + echo "\`ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}-minimal\`" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Users can now: \`go get github.com/tracewayapp/traceway/backend@v${{ inputs.version }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "Both images also tagged as \`latest\` and \`minimal\` respectively." >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Website and docs deploys will run automatically on release publish." >> "$GITHUB_STEP_SUMMARY" + echo "See [DOCKER_SIGNATURES.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/DOCKER_SIGNATURES.md) to verify image signatures." >> "$GITHUB_STEP_SUMMARY" diff --git a/DOCKER_SIGNATURES.md b/DOCKER_SIGNATURES.md new file mode 100644 index 00000000..56192cab --- /dev/null +++ b/DOCKER_SIGNATURES.md @@ -0,0 +1,157 @@ +# Docker Image Signatures + +All official Traceway Docker images are cryptographically signed using [Cosign](https://docs.sigstore.dev/cosign/overview/), a CNCF tool for container image signing and verification. + +## Why Image Signatures Matter + +Signed images provide: +- **Authenticity**: Verify the image comes from the official Traceway project +- **Integrity**: Ensure the image hasn't been modified or tampered with +- **Transparency**: Signatures are publicly verifiable using GitHub's OIDC token + +## Available Images + +Traceway publishes two Docker images to GitHub Container Registry (GHCR): + +| Image | Purpose | Size | Best For | +|-------|---------|------|----------| +| `ghcr.io/tracewayapp/traceway:v*` | **Full** — includes ClickHouse, PostgreSQL, supervisord | ~600MB | All-in-one self-hosted deployments | +| `ghcr.io/tracewayapp/traceway:v*-minimal` | **Minimal** — backend + frontend only | ~20-30MB | External ClickHouse/PostgreSQL, resource-constrained environments | + +Both images are signed. Latest release: + +```bash +docker pull ghcr.io/tracewayapp/traceway:latest # Full image +docker pull ghcr.io/tracewayapp/traceway:minimal # Minimal image +``` + +## Verifying Signatures + +### Install Cosign + +**macOS:** +```bash +brew install cosign +``` + +**Linux:** +```bash +wget https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 +sudo mv cosign-linux-amd64 /usr/local/bin/cosign +sudo chmod +x /usr/local/bin/cosign +``` + +**Windows (PowerShell):** +```powershell +Invoke-WebRequest -Uri "https://github.com/sigstore/cosign/releases/latest/download/cosign-windows-amd64.exe" -OutFile cosign.exe +# Add cosign.exe to your PATH +``` + +### Verify Signature + +```bash +# Full image +cosign verify \ + --certificate-identity-regexp '.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + ghcr.io/tracewayapp/traceway:latest + +# Minimal image +cosign verify \ + --certificate-identity-regexp '.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + ghcr.io/tracewayapp/traceway:minimal +``` + +### Example Output + +A successful verification looks like: +``` +Verification successful! +``` + +If verification fails, the image may have been tampered with and should not be used. + +## How Signatures Are Generated + +1. **Build & Push**: GitHub Actions builds the Docker image and pushes it to GHCR +2. **Sign**: Cosign signs the image using GitHub's OIDC token (keyless signing) +3. **Store**: Signature is stored in GHCR alongside the image + +## Using in Docker Compose + +```yaml +version: '3.8' + +services: + traceway: + image: ghcr.io/tracewayapp/traceway:latest + # Optionally verify the signature before running: + # Run: cosign verify ... ghcr.io/tracewayapp/traceway:latest + ports: + - "80:80" + - "8082:8082" + volumes: + - clickhouse_data:/var/lib/clickhouse + - postgres_data:/var/lib/postgresql/data + environment: + GIN_MODE: release + +volumes: + clickhouse_data: + postgres_data: +``` + +## Minimal Image with External Databases + +For the minimal image with external ClickHouse/PostgreSQL: + +```yaml +version: '3.8' + +services: + traceway: + image: ghcr.io/tracewayapp/traceway:minimal + ports: + - "80:80" + - "8082:8082" + environment: + GIN_MODE: release + CLICKHOUSE_SERVER: clickhouse:9000 + POSTGRES_HOST: postgres + POSTGRES_PORT: 5432 + # ... other env vars + + clickhouse: + image: clickhouse/clickhouse-server:latest + # ... configuration + + postgres: + image: postgres:15 + # ... configuration +``` + +## Troubleshooting + +**"Verification failed" error:** +- Ensure you're using the correct image URI (with version tag or `latest`) +- Check your internet connection (Cosign needs to fetch OIDC tokens) +- Try verifying a different version tag + +**"cosign not found":** +- Ensure Cosign is installed and in your `$PATH` +- Run `cosign version` to verify installation + +**"Certificate verification failed":** +- This indicates the image signature is invalid +- Do not use this image — report it to the Traceway team + +## More Information + +- [Cosign Documentation](https://docs.sigstore.dev/cosign/overview/) +- [Traceway Self-Hosting Guide](https://docs.tracewayapp.com/server) +- [SBOM (Software Bill of Materials)](https://docs.sigstore.dev/cosign/sbom/) + +## Questions? + +If you have questions about Docker image security or signatures, [open an issue](https://github.com/tracewayapp/traceway/issues) or ask in the [Traceway Discord](https://discord.gg/9tPn2SB3). diff --git a/README.md b/README.md index 913c42fb..a2219076 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ cd traceway && docker compose up -d Point any OTel SDK at `http://localhost/api/otel/v1/traces` (or `/metrics`, `/logs`) and traces start flowing. See the [self-hosting docs](https://docs.tracewayapp.com/server/docker-compose) for production deployment, TLS, and storage configuration. +**Docker images are cryptographically signed.** See [DOCKER_SIGNATURES.md](./DOCKER_SIGNATURES.md) to verify images before deploying. + ### Embedded mode (inside your Go app) Run Traceway inside your Go process — no Docker, no external databases, SQLite under the hood: From c990dc6336353652baba594492f168203b9ad094 Mon Sep 17 00:00:00 2001 From: Thomas Henry Thirlwall Date: Wed, 13 May 2026 12:55:23 -0500 Subject: [PATCH 2/2] feat: add SQLite image variant and improve Docker documentation - Add SQLite image (Dockerfile.sqlite) for embedded deployments - Build and sign SQLite image alongside full and minimal variants - Update documentation with all three image variants and use cases - Add Docker Compose example for SQLite embedded mode - Improve image descriptions and deployment guidance Addresses feedback on #136 --- .github/workflows/release-traceway.yml | 28 ++++++++++-- DOCKER_SIGNATURES.md | 61 +++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-traceway.yml b/.github/workflows/release-traceway.yml index 51e5abe6..0ee63e74 100644 --- a/.github/workflows/release-traceway.yml +++ b/.github/workflows/release-traceway.yml @@ -205,6 +205,19 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Build and push SQLite image + id: docker_build_sqlite + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.sqlite + push: true + tags: | + ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}-sqlite + ghcr.io/${{ github.repository_owner }}/traceway:sqlite + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Install Cosign uses: sigstore/cosign-installer@v3 with: @@ -222,16 +235,25 @@ jobs: run: | cosign sign --yes ghcr.io/${{ github.repository_owner }}/traceway@${{ steps.docker_build_minimal.outputs.digest }} + - name: Sign SQLite image + env: + COSIGN_EXPERIMENTAL: 1 + run: | + cosign sign --yes ghcr.io/${{ github.repository_owner }}/traceway@${{ steps.docker_build_sqlite.outputs.digest }} + - name: Summary run: | echo "### Docker Images (signed with Cosign)" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "**Full Image** (includes ClickHouse, PostgreSQL, supervisord):" >> "$GITHUB_STEP_SUMMARY" + echo "**Full Image** (ClickHouse + PostgreSQL + supervisord):" >> "$GITHUB_STEP_SUMMARY" echo "\`ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}\`" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "**Minimal Image** (external databases only):" >> "$GITHUB_STEP_SUMMARY" + echo "**Minimal Image** (external ClickHouse/PostgreSQL):" >> "$GITHUB_STEP_SUMMARY" echo "\`ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}-minimal\`" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Both images also tagged as \`latest\` and \`minimal\` respectively." >> "$GITHUB_STEP_SUMMARY" + echo "**SQLite Image** (embedded SQLite, single binary, no dependencies):" >> "$GITHUB_STEP_SUMMARY" + echo "\`ghcr.io/${{ github.repository_owner }}/traceway:v${{ inputs.version }}-sqlite\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "All images tagged with version, plus \`latest\`, \`minimal\`, and \`sqlite\` respectively." >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "See [DOCKER_SIGNATURES.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/DOCKER_SIGNATURES.md) to verify image signatures." >> "$GITHUB_STEP_SUMMARY" diff --git a/DOCKER_SIGNATURES.md b/DOCKER_SIGNATURES.md index 56192cab..23376a87 100644 --- a/DOCKER_SIGNATURES.md +++ b/DOCKER_SIGNATURES.md @@ -11,18 +11,25 @@ Signed images provide: ## Available Images -Traceway publishes two Docker images to GitHub Container Registry (GHCR): +Traceway publishes three Docker image variants to GitHub Container Registry (GHCR): | Image | Purpose | Size | Best For | |-------|---------|------|----------| -| `ghcr.io/tracewayapp/traceway:v*` | **Full** — includes ClickHouse, PostgreSQL, supervisord | ~600MB | All-in-one self-hosted deployments | -| `ghcr.io/tracewayapp/traceway:v*-minimal` | **Minimal** — backend + frontend only | ~20-30MB | External ClickHouse/PostgreSQL, resource-constrained environments | +| `ghcr.io/tracewayapp/traceway:v*` | **Full** — ClickHouse + PostgreSQL + supervisord | ~600MB | Large deployments, multi-service setup | +| `ghcr.io/tracewayapp/traceway:v*-minimal` | **Minimal** — single binary with external databases | ~20-30MB | Kubernetes, scalable setups, external database infrastructure | +| `ghcr.io/tracewayapp/traceway:v*-sqlite` | **SQLite** — embedded SQLite, single binary, zero dependencies | ~50-80MB | Small VPS, testing, single-server deployments, embedded mode | -Both images are signed. Latest release: +All images are cryptographically signed. Pull the latest release: ```bash -docker pull ghcr.io/tracewayapp/traceway:latest # Full image -docker pull ghcr.io/tracewayapp/traceway:minimal # Minimal image +# Full image (all services included) +docker pull ghcr.io/tracewayapp/traceway:latest + +# Minimal image (external databases) +docker pull ghcr.io/tracewayapp/traceway:minimal + +# SQLite image (embedded, no external dependencies) +docker pull ghcr.io/tracewayapp/traceway:sqlite ``` ## Verifying Signatures @@ -61,6 +68,12 @@ cosign verify \ --certificate-identity-regexp '.*' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ ghcr.io/tracewayapp/traceway:minimal + +# SQLite image +cosign verify \ + --certificate-identity-regexp '.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + ghcr.io/tracewayapp/traceway:sqlite ``` ### Example Output @@ -104,7 +117,7 @@ volumes: ## Minimal Image with External Databases -For the minimal image with external ClickHouse/PostgreSQL: +For scalable deployments with external ClickHouse/PostgreSQL: ```yaml version: '3.8' @@ -131,6 +144,40 @@ services: # ... configuration ``` +## SQLite Image (Embedded, Zero Dependencies) + +For small deployments, testing, or single-server setups: + +```yaml +version: '3.8' + +services: + traceway: + image: ghcr.io/tracewayapp/traceway:sqlite + ports: + - "80:80" + - "8082:8082" + volumes: + - traceway_data:/data + environment: + GIN_MODE: release + +volumes: + traceway_data: +``` + +The SQLite image: +- Stores all data in `/data/traceway.db` (SQLite database) +- Stores blobs in `/data/storage` (if using local storage) +- Requires only one volume mount for all persistent state +- Perfect for VPS, small instances, or development + +To persist data across restarts, mount a host folder or named volume at `/data`: + +```bash +docker run -v /var/lib/traceway:/data ghcr.io/tracewayapp/traceway:sqlite +``` + ## Troubleshooting **"Verification failed" error:**