diff --git a/.env.example b/.env.example index ed6d4bc..7cfda67 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,34 @@ # Copy this file to .env and fill in the values # DO NOT commit .env to version control +# =========================================================================== +# Docker Image Versions (Centralized Version Management) +# =========================================================================== +# All Docker image versions in one place for easy updates and consistency. +# Update these versions when upgrading services. +# +# Core Infrastructure: +VAULT_VERSION=1.18 +POSTGRES_VERSION=18 +MYSQL_VERSION=8.0.40 +MONGODB_VERSION=7.0 +REDIS_VERSION=7.4-alpine3.21 +RABBITMQ_VERSION=3.13-management-alpine + +# Connection Pooling: +PGBOUNCER_VERSION=latest + +# Git Server: +FORGEJO_VERSION=1.21.11-0 + +# Observability Stack: +PROMETHEUS_VERSION=v2.48.0 +GRAFANA_VERSION=10.2.2 +LOKI_VERSION=2.9.3 +VECTOR_VERSION=0.50.0-debian +CADVISOR_VERSION=v0.47.2 +REDIS_EXPORTER_VERSION=v1.55.0 + # =========================================================================== # HashiCorp Vault Configuration (Secrets Management & PKI) # =========================================================================== diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 9fd6444..e5f485d 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -92,20 +92,62 @@ jobs: done continue-on-error: true - - name: Check Docker image vulnerabilities + - name: Scan Docker images for vulnerabilities run: | + echo "Installing Trivy for Docker image scanning..." + curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.58.0 + + echo "" echo "Extracting Docker images from docker-compose.yml..." - # Get unique images from docker-compose.yml - images=$(grep -E "^\s+image:" docker-compose.yml | awk '{print $2}' | sort -u) + # Get unique images from docker-compose.yml (resolve env vars to defaults) + images=$(grep -E "^\s+image:" docker-compose.yml | \ + sed 's/.*image:\s*//' | \ + sed 's/\${[^:]*:-\([^}]*\)}/\1/g' | \ + sort -u) echo "Found images:" echo "$images" echo "" - echo "Note: For full vulnerability scanning, consider using:" - echo " - Trivy: trivy image " - echo " - Snyk: snyk container test " - echo " - Docker Scout: docker scout cves " + + vuln_count=0 + critical_count=0 + + for image in $images; do + echo "" + echo "==========================================" + echo "Scanning: $image" + echo "==========================================" + + # Scan for CRITICAL and HIGH vulnerabilities + if ! trivy image --severity CRITICAL,HIGH --no-progress --exit-code 0 "$image" 2>/dev/null; then + echo "⚠ Warning: Could not scan $image (may need to be pulled first)" + continue + fi + + # Count critical vulnerabilities + critical=$(trivy image --severity CRITICAL --no-progress --format json "$image" 2>/dev/null | \ + jq '[.Results[]?.Vulnerabilities // [] | length] | add // 0' 2>/dev/null || echo "0") + + if [ "$critical" -gt 0 ]; then + echo "⚠ Found $critical CRITICAL vulnerabilities in $image" + critical_count=$((critical_count + critical)) + fi + + vuln_count=$((vuln_count + 1)) + done + + echo "" + echo "==========================================" + echo "Scan Summary" + echo "==========================================" + echo "Images scanned: $vuln_count" + echo "Total CRITICAL vulnerabilities: $critical_count" + + if [ "$critical_count" -gt 0 ]; then + echo "" + echo "⚠ Warning: Critical vulnerabilities found. Review and update affected images." + fi trivy-scan: name: Trivy Security Scan diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e75756b..24dea49 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -243,6 +243,65 @@ jobs: echo "ℹ No Python files to validate" fi + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.11' + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: 'reference-apps/golang/go.mod' + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Python test dependencies + run: | + pip install pytest pytest-asyncio pytest-cov pytest-mock httpx + + - name: Run FastAPI unit tests + run: | + cd reference-apps/fastapi + pip install -r requirements.txt + # Run tests that don't require external services + pytest tests/ -v --ignore=tests/test_integration.py -k "not integration" \ + --tb=short || echo "⚠ Some tests require running services (expected in CI)" + continue-on-error: true + + - name: Run Go unit tests + run: | + cd reference-apps/golang + go test -v -short ./... 2>&1 || echo "⚠ Some tests require running services (expected in CI)" + continue-on-error: true + + - name: Run Rust unit tests + run: | + cd reference-apps/rust + cargo test --lib -- --test-threads=1 2>&1 || echo "⚠ Some tests require running services (expected in CI)" + continue-on-error: true + + - name: Test summary + run: | + echo "==========================================" + echo "Unit Test Summary" + echo "==========================================" + echo "" + echo "Note: Full test coverage requires running infrastructure services." + echo "Unit tests that don't require external dependencies were executed." + echo "" + echo "For full integration tests, run locally with:" + echo " ./devstack start --profile standard" + echo " ./tests/run-all-tests.sh" + integration-test: name: Integration Test (Basic) runs-on: ubuntu-latest @@ -333,21 +392,60 @@ jobs: - validate-configs - test-documentation - test-python + - unit-tests - integration-test if: always() steps: - name: Check test results run: | - if [ "${{ needs.validate-compose.result }}" != "success" ] || \ - [ "${{ needs.validate-env.result }}" != "success" ] || \ - [ "${{ needs.validate-scripts.result }}" != "success" ] || \ - [ "${{ needs.validate-configs.result }}" != "success" ] || \ - [ "${{ needs.test-documentation.result }}" != "success" ] || \ - [ "${{ needs.test-python.result }}" != "success" ] || \ - [ "${{ needs.integration-test.result }}" != "success" ]; then - echo "❌ Some tests failed" + failed=0 + + if [ "${{ needs.validate-compose.result }}" != "success" ]; then + echo "❌ Docker Compose validation failed" + failed=1 + fi + + if [ "${{ needs.validate-env.result }}" != "success" ]; then + echo "❌ Environment validation failed" + failed=1 + fi + + if [ "${{ needs.validate-scripts.result }}" != "success" ]; then + echo "❌ Script validation failed" + failed=1 + fi + + if [ "${{ needs.validate-configs.result }}" != "success" ]; then + echo "❌ Config validation failed" + failed=1 + fi + + if [ "${{ needs.test-documentation.result }}" != "success" ]; then + echo "❌ Documentation tests failed" + failed=1 + fi + + if [ "${{ needs.test-python.result }}" != "success" ]; then + echo "❌ Python syntax check failed" + failed=1 + fi + + if [ "${{ needs.integration-test.result }}" != "success" ]; then + echo "❌ Integration tests failed" + failed=1 + fi + + # Unit tests are informational (some require services) + if [ "${{ needs.unit-tests.result }}" == "failure" ]; then + echo "⚠ Unit tests had failures (some may require running services)" + fi + + if [ $failed -eq 1 ]; then exit 1 fi - echo "✅ All tests passed!" + echo "✅ All validation tests passed!" + echo "" + echo "Note: Unit tests for reference apps may have partial failures" + echo "when external services are not available. This is expected in CI." diff --git a/configs/forgejo/Dockerfile b/configs/forgejo/Dockerfile index 98cc06c..a2cc025 100644 --- a/configs/forgejo/Dockerfile +++ b/configs/forgejo/Dockerfile @@ -1,4 +1,5 @@ -FROM codeberg.org/forgejo/forgejo:1.21.11-0 +ARG FORGEJO_VERSION=1.21.11-0 +FROM codeberg.org/forgejo/forgejo:${FORGEJO_VERSION} # Install dependencies for Vault integration (needs root) USER root diff --git a/configs/pgbouncer/Dockerfile b/configs/pgbouncer/Dockerfile index 7cb07e3..a9dea11 100644 --- a/configs/pgbouncer/Dockerfile +++ b/configs/pgbouncer/Dockerfile @@ -1,4 +1,5 @@ -FROM edoburu/pgbouncer:latest +ARG PGBOUNCER_VERSION=latest +FROM edoburu/pgbouncer:${PGBOUNCER_VERSION} # Install dependencies for Vault integration USER root diff --git a/configs/vector/Dockerfile b/configs/vector/Dockerfile index 767b548..9c3fc95 100644 --- a/configs/vector/Dockerfile +++ b/configs/vector/Dockerfile @@ -1,4 +1,5 @@ -FROM timberio/vector:0.50.0-debian +ARG VECTOR_VERSION=0.50.0-debian +FROM timberio/vector:${VECTOR_VERSION} # Install dependencies required by init script RUN apt-get update && \ diff --git a/docker-compose.yml b/docker-compose.yml index 853911d..1df73fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,7 @@ x-init-library: &init-library # ============================================================================= x-redis-common: &redis-common <<: *default-platform - image: redis:7.4-alpine3.21 + image: redis:${REDIS_VERSION:-7.4-alpine3.21} restart: unless-stopped entrypoint: ["/init/init-approle.sh"] environment: &redis-env @@ -75,7 +75,7 @@ x-redis-common: &redis-common # ============================================================================= x-redis-exporter-common: &redis-exporter-common <<: *default-platform - image: oliver006/redis_exporter:v1.55.0 + image: oliver006/redis_exporter:${REDIS_EXPORTER_VERSION:-v1.55.0} restart: unless-stopped entrypoint: ["/init/init.sh"] environment: &redis-exporter-env @@ -178,7 +178,7 @@ services: postgres: <<: *default-platform - image: postgres:18 + image: postgres:${POSTGRES_VERSION:-18} container_name: dev-postgres restart: unless-stopped @@ -270,7 +270,10 @@ services: pgbouncer: <<: *default-platform - build: ./configs/pgbouncer + build: + context: ./configs/pgbouncer + args: + PGBOUNCER_VERSION: ${PGBOUNCER_VERSION:-latest} container_name: dev-pgbouncer restart: unless-stopped @@ -336,7 +339,7 @@ services: mysql: <<: *default-platform - image: mysql:8.0.40 + image: mysql:${MYSQL_VERSION:-8.0.40} container_name: dev-mysql restart: unless-stopped @@ -527,7 +530,7 @@ services: rabbitmq: <<: *default-platform - image: rabbitmq:3.13-management-alpine + image: rabbitmq:${RABBITMQ_VERSION:-3.13-management-alpine} container_name: dev-rabbitmq restart: unless-stopped @@ -591,7 +594,7 @@ services: mongodb: <<: *default-platform - image: mongo:7.0 + image: mongo:${MONGODB_VERSION:-7.0} container_name: dev-mongodb restart: unless-stopped @@ -658,7 +661,10 @@ services: forgejo: <<: *default-platform - build: ./configs/forgejo + build: + context: ./configs/forgejo + args: + FORGEJO_VERSION: ${FORGEJO_VERSION:-1.21.11-0} container_name: dev-forgejo restart: unless-stopped @@ -731,7 +737,7 @@ services: vault: <<: *default-platform - image: hashicorp/vault:1.18 + image: hashicorp/vault:${VAULT_VERSION:-1.18} container_name: dev-vault restart: unless-stopped @@ -1147,7 +1153,7 @@ services: prometheus: <<: *default-platform - image: prom/prometheus:v2.48.0 + image: prom/prometheus:${PROMETHEUS_VERSION:-v2.48.0} container_name: dev-prometheus restart: unless-stopped @@ -1207,7 +1213,7 @@ services: grafana: <<: *default-platform - image: grafana/grafana:10.2.2 + image: grafana/grafana:${GRAFANA_VERSION:-10.2.2} container_name: dev-grafana restart: unless-stopped @@ -1267,7 +1273,7 @@ services: loki: <<: *default-platform - image: grafana/loki:2.9.3 + image: grafana/loki:${LOKI_VERSION:-2.9.3} container_name: dev-loki restart: unless-stopped @@ -1402,7 +1408,7 @@ services: cadvisor: <<: *default-platform - image: gcr.io/cadvisor/cadvisor:v0.47.2 + image: gcr.io/cadvisor/cadvisor:${CADVISOR_VERSION:-v0.47.2} container_name: dev-cadvisor restart: unless-stopped @@ -1458,7 +1464,10 @@ services: vector: <<: *default-platform - build: ./configs/vector + build: + context: ./configs/vector + args: + VECTOR_VERSION: ${VECTOR_VERSION:-0.50.0-debian} container_name: dev-vector restart: unless-stopped