build: minimize HAProxy runtime on Alpine 3.24 #21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security release gate | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - 'verification/load-balancer-controller-*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: load-balancer-controller-security-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| build-test-scan: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| env: | |
| SERVICE_IMAGE: local/pasturestack/load-balancer-service:security-${{ github.sha }} | |
| TRIVY_IMAGE: ghcr.io/aquasecurity/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 | |
| VERSION_OVERRIDE: security-${{ github.sha }} | |
| steps: | |
| - name: Check out candidate | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Install checksum-pinned Go and govulncheck | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| archive="$RUNNER_TEMP/go1.27.0.linux-amd64.tar.gz" | |
| curl --fail --silent --show-error --location --output "$archive" \ | |
| 'https://go.dev/dl/go1.27.0.linux-amd64.tar.gz' | |
| printf '%s %s\n' \ | |
| '675c26c449cbb18fc24b74650de1eabbae6e16f64326fd85a283fb3b58280685' \ | |
| "$archive" | sha256sum --check | |
| tar -C "$RUNNER_TEMP" -xzf "$archive" | |
| export PATH="$RUNNER_TEMP/go/bin:$PATH" | |
| export GOBIN="$RUNNER_TEMP/security-bin" | |
| export GOTELEMETRY=off | |
| mkdir -p "$GOBIN" | |
| go install golang.org/x/vuln/cmd/govulncheck@v1.7.0 | |
| printf '%s\n' "$RUNNER_TEMP/go/bin" "$GOBIN" >> "$GITHUB_PATH" | |
| printf 'GOROOT=%s\n' "$RUNNER_TEMP/go" >> "$GITHUB_ENV" | |
| - name: Test the maintained source path | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p evidence | |
| git diff --check HEAD -- . ':(exclude)vendor/**' | |
| ./scripts/test | |
| ./scripts/validate | |
| govulncheck -show verbose ./... | tee evidence/govulncheck.txt | |
| - name: Scan source and create source SBOM | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| docker pull "$TRIVY_IMAGE" | |
| cache="$RUNNER_TEMP/trivy-cache" | |
| scan="$RUNNER_TEMP/trivy-output" | |
| mkdir -p "$cache" "$scan" | |
| docker run --rm -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" image --download-db-only | |
| docker run --rm -v "$PWD:/work:ro" -v "$scan:/scan" \ | |
| -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" fs \ | |
| --skip-db-update --offline-scan --scanners vuln,secret \ | |
| --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --exit-code 1 \ | |
| --format json --output /scan/source-security.json /work | |
| docker run --rm -v "$PWD:/work:ro" -v "$scan:/scan" \ | |
| -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" fs \ | |
| --skip-db-update --offline-scan --format cyclonedx \ | |
| --output /scan/source.cdx.json /work | |
| cp "$scan/source-security.json" "$scan/source.cdx.json" evidence/ | |
| - name: Build reproducible binary and runtime image | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| source_epoch="$(git show -s --format=%ct HEAD)" | |
| scripts/build | |
| cp bin/lb-controller "$RUNNER_TEMP/lb-controller.first" | |
| rm -f bin/lb-controller | |
| scripts/build | |
| cmp "$RUNNER_TEMP/lb-controller.first" bin/lb-controller | |
| sha256sum bin/lb-controller > evidence/product-binary.sha256 | |
| ARCH=amd64 TAG="security-${GITHUB_SHA}" REPO=local/pasturestack \ | |
| SOURCE_DATE_EPOCH="$source_epoch" scripts/package | |
| test "$(wc -l < dist/images)" -eq 1 | |
| grep -Fxq "$SERVICE_IMAGE" dist/images | |
| docker run --rm --entrypoint /usr/bin/lb-controller "$SERVICE_IMAGE" --version \ | |
| | grep -Fq "security-${GITHUB_SHA}" | |
| docker run --rm --entrypoint /usr/sbin/haproxy "$SERVICE_IMAGE" -vv \ | |
| | tee evidence/haproxy-version.txt | |
| grep -Fq 'HAProxy version 3.4.3' evidence/haproxy-version.txt | |
| test "$(docker image inspect --format '{{.Config.User}}' "$SERVICE_IMAGE")" = '10001:10001' | |
| - name: Scan binary and runtime image | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cache="$RUNNER_TEMP/trivy-cache" | |
| docker run --rm -v "$PWD/bin:/product:ro" -v "$PWD/evidence:/evidence" \ | |
| -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" rootfs \ | |
| --skip-db-update --offline-scan --scanners vuln \ | |
| --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --exit-code 1 \ | |
| --format json --output /evidence/product-security.json /product | |
| docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ | |
| -v "$PWD/evidence:/evidence" -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" image \ | |
| --skip-db-update --offline-scan --scanners vuln \ | |
| --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --exit-code 1 \ | |
| --format json --output /evidence/image-security.json "$SERVICE_IMAGE" | |
| docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ | |
| -v "$PWD/evidence:/evidence" -v "$cache:/root/.cache/trivy" "$TRIVY_IMAGE" image \ | |
| --skip-db-update --offline-scan --format cyclonedx \ | |
| --output /evidence/image.cdx.json "$SERVICE_IMAGE" | |
| python3 - <<'PY' | |
| import json | |
| from pathlib import Path | |
| for name in ('source-security.json', 'product-security.json', 'image-security.json'): | |
| report = json.loads((Path('evidence') / name).read_text()) | |
| vulnerabilities = sum(len(item.get('Vulnerabilities') or []) for item in report.get('Results') or []) | |
| secrets = sum(len(item.get('Secrets') or []) for item in report.get('Results') or []) | |
| assert vulnerabilities == 0, (name, vulnerabilities) | |
| assert secrets == 0, (name, secrets) | |
| for name in ('source.cdx.json', 'image.cdx.json'): | |
| bom = json.loads((Path('evidence') / name).read_text()) | |
| assert bom.get('serialNumber') | |
| assert len(bom.get('components') or []) > 0 | |
| assert len(bom.get('dependencies') or []) > 0 | |
| PY | |
| - name: Upload verification evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: load-balancer-controller-security-${{ github.sha }} | |
| path: evidence/ | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 9 | |
| - name: Clean runner resources | |
| if: always() | |
| shell: bash | |
| run: | | |
| set +e | |
| docker image rm -f "$SERVICE_IMAGE" "$TRIVY_IMAGE" 2>/dev/null | |
| docker builder prune --all --force >/dev/null 2>&1 | |
| sudo rm -rf bin dist |