diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..bf52060 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,16 @@ +# Code owners for goceleris/loadgen. +# +# GitHub requests a review from the matching owners on every pull request +# that touches these paths; with "Require review from Code Owners" enabled on +# the main branch protection, their approval is mandatory before merging. +# Later rules take precedence over earlier ones — keep the catch-all first. +# +# Syntax: https://docs.github.com/articles/about-code-owners + +# Default owner for everything in the repository. +* @FumingPower3925 + +# Area delegation example — uncomment and adjust when a second maintainer +# takes ownership of a subsystem (the more specific rule wins): +# /cmd/loadgen/ @FumingPower3925 @WdnLiu +# /.github/ @FumingPower3925 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..1ed0005 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## Summary + +Brief description of the change and why it is needed. + +## Changes + +- + +## Test Plan + +- [ ] Unit tests added/updated (`go test -race -count=1 ./...`) +- [ ] `golangci-lint run` and `go vet ./...` pass +- [ ] Integration matrix passes if the H1/H2/WS/SSE client paths changed (`go test -tags integration -run TestIntegrationH2CMatrix .`) +- [ ] README cluster-bench contract updated if the JSON result schema or CLI flags changed + +Closes # diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..556d91b --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,31 @@ +# Automatically generated release notes (GitHub "Generate release notes"). +# Same categories as goceleris/celeris so the org's release pages read alike. +# Labels are applied on the pull request; dependabot / actions bumps are +# excluded because they are noise for consumers of the binaries. +changelog: + exclude: + labels: + - dependencies + - github_actions + categories: + - title: Breaking Changes + labels: + - breaking + - title: Security + labels: + - security + - title: Fixes + labels: + - bug + - fix + - title: Performance + labels: + - performance + - perf + - title: Features + labels: + - enhancement + - feature + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d4d2b6..e189cbf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,13 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - uses: actions/setup-go@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.27.0" - - uses: golangci/golangci-lint-action@v9 + - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: version: v2.13 - name: actionlint @@ -33,8 +35,10 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - uses: actions/setup-go@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.27.0" - name: Run tests @@ -45,8 +49,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v7 - - uses: actions/setup-go@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.27.0" # The testserver helper imports github.com/goceleris/celeris (pinned in diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 71a04f6..c8f14de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,8 +12,10 @@ jobs: name: Publish to Go Package Registry runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - uses: actions/setup-go@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.27.0" - name: Verify module @@ -33,6 +35,10 @@ jobs: # orchestrators can fetch them without re-cloning + rebuilding. name: Build cmd/loadgen binaries (${{ matrix.goos }}/${{ matrix.goarch }}) runs-on: ubuntu-latest + permissions: + contents: write # action-gh-release uploads the tarball to the release + id-token: write # OIDC token that signs the SLSA provenance (Sigstore) + attestations: write # store the attestation in the repository's attestation store strategy: matrix: include: @@ -45,8 +51,8 @@ jobs: - goos: darwin goarch: arm64 steps: - - uses: actions/checkout@v7 - - uses: actions/setup-go@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: "1.27.0" - name: Build @@ -67,7 +73,13 @@ jobs: run: | cd dist tar czf "loadgen_${GOOS}_${GOARCH}.tar.gz" "loadgen-${GOOS}-${GOARCH}" + # SLSA build provenance for the exact archive that ships. Consumers + # verify with: gh attestation verify -R goceleris/loadgen + - name: Attest build provenance + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: dist/loadgen_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz - name: Attach to release - uses: softprops/action-gh-release@v3 + uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: files: dist/loadgen_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f3e2718 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,66 @@ +# Contributing to loadgen + +Thanks for helping improve loadgen, the load generator behind the celeris +benchmark suite. loadgen is deliberately dependency-light (two direct Go +dependencies) and is exercised on real hardware by +[goceleris/probatorium](https://github.com/goceleris/probatorium), so +changes are held to the same standard as the engine they measure. + +## Prerequisites + +- Go **1.27.0** (the version pinned in `go.mod` and in CI) +- [golangci-lint](https://golangci-lint.run/) v2.13+ (the CI pin) +- Linux or macOS. The client itself is portable; the process-CPU sampler + and recv-queue probe have Linux implementations and `_other.go` + fallbacks. + +## Build and test + +```bash +go build ./... # compile everything, including cmd/loadgen +go vet ./... +golangci-lint run # same config CI uses (.golangci.yml) +go test -race -count=1 -timeout 120s ./... # unit tests, exactly as CI runs them + +# Integration matrix against a live celeris server. The testserver helper is +# a nested module that imports celeris; the tests spawn it as a subprocess. +go test -tags integration -race -count=1 -timeout 180s -v -run TestIntegrationH2CMatrix . +``` + +`gofmt` and `goimports` (with `github.com/goceleris/loadgen` as the local +prefix) are enforced by golangci-lint, so run it before pushing. + +## Pull request flow + +1. Fork the repository and create a topic branch from `main` + (`feat/…`, `fix/…`, `perf/…`, `chore/…`). +2. Keep each PR focused on a single change and include tests — a + correctness fix without a regression test is not complete. +3. Write commit messages in the `type: description` format + (`feat:`, `fix:`, `perf:`, `security:`, `test:`, `ci:`, `docs:`, `chore:`) + and explain *why* in the body when it is not obvious from the diff. +4. Fill in the pull request template (Summary, Changes, Test Plan, + `Closes #…`). Changes to the JSON result schema or the CLI flags must + also update the README's cluster-bench contract section, because + probatorium parses that output. +5. Make sure the CI workflow (lint, actionlint, unit tests, integration + matrix) is green. + +## Merge rule + +loadgen follows the same governance as celeris — see +[celeris/GOVERNANCE.md](https://github.com/goceleris/celeris/blob/main/GOVERNANCE.md). +In short: `main` is protected, every change lands through a pull request +that passes CI and is approved by a code owner (see `.github/CODEOWNERS`), +and pull requests are merged by a maintainer — never force-pushed. Releases +are tagged from `main` and the release workflow builds, attests and +publishes the binaries. + +## Reporting security issues + +Do not open a public issue. Follow [SECURITY.md](SECURITY.md). + +## License + +By contributing you agree that your contributions are licensed under the +[Apache License 2.0](LICENSE) that covers the project. diff --git a/README.md b/README.md index ecc7a7c..4252caf 100644 --- a/README.md +++ b/README.md @@ -328,6 +328,16 @@ chmod +x /tmp/loadgen-${OS}-${ARCH} GitHub displays the SHA-256 of each release asset on the release page — `gh release download ` and the web UI verify checksums automatically, so no separate `.sha256` sidecar ships. +### Verify a release + +Every release tarball is built by the [release workflow](.github/workflows/release.yml) and carries a signed [SLSA build provenance](https://slsa.dev/provenance/) attestation, generated with `actions/attest-build-provenance` and recorded in GitHub's attestation store. Before running a downloaded binary on a bench host, check that the asset really came from this repository's release pipeline: + +```bash +gh attestation verify loadgen_linux_amd64.tar.gz -R goceleris/loadgen +``` + +The command fails if the archive was tampered with after the build or was not produced by a `goceleris/loadgen` workflow. Add `--format json` to inspect the attested source commit, workflow path and builder. Releases published before the attestation step was added (v1.4.13 and earlier) have no attestation and will fail verification. + ### Reference orchestrator The probatorium cluster bench drives this contract: it cross-compiles loadgen (or fetches a release tarball), pushes it to the load host, executes it, and parses the stdout JSON — decoding `histogram` for cross-cell re-aggregation. See [goceleris/probatorium](https://github.com/goceleris/probatorium). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..6f68a87 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,60 @@ +# Security Policy + +loadgen is a load-generation *client*: it opens outbound connections to a +target you name on the command line and never listens on a socket in +production use. Its security surface is therefore small — a malicious +server could still try to exploit the HTTP/1.1, HTTP/2, WebSocket and SSE +response parsers, and the JSON result written to stdout is consumed by +orchestrators — so we treat parser and output-handling bugs as security +issues. + +## Reporting a Vulnerability + +**Please do not open a public issue for security problems.** + +Report privately, in order of preference: + +1. **GitHub private vulnerability reporting (preferred)** — open the + repository's **Security** tab and click **"Report a vulnerability"**. + Private vulnerability reporting is enabled on this repository; the report + is visible only to the maintainers and becomes the draft advisory that + ships with the fix. +2. **Email** — [security@goceleris.dev](mailto:security@goceleris.dev). + +Include a description of the issue, the loadgen version (the release tag, or +the `loadgen_version` field of a result), reproduction steps or a +proof-of-concept, and the impact you believe it has. + +You will receive an **acknowledgement within 72 hours**. We will keep you +informed as we triage, fix and disclose, and we credit reporters in the +release notes unless they prefer otherwise. + +## Supported Versions + +Only the latest release of loadgen receives security fixes. Pre-built +binaries and the Go module are cut from the same tag, so upgrade to the most +recent `v1.x` tag to remain covered. + +## Scope + +In scope: + +- The HTTP/1.1, HTTP/2 (prior-knowledge and h2c upgrade), WebSocket and SSE + client implementations and their response parsers +- TLS configuration handling (`-insecure`, custom `tls.Config`) +- The federation peer protocol +- The JSON result output consumed by orchestrators +- The release pipeline (workflows, published binaries and their provenance) + +Out of scope: + +- Vulnerabilities in the server under test +- Denial of service of a target caused by *intended* use of a load generator + +## The celeris engine + +Vulnerabilities in the HTTP server framework that loadgen is built to test +belong to the [goceleris/celeris](https://github.com/goceleris/celeris) +repository — see +[celeris/SECURITY.md](https://github.com/goceleris/celeris/blob/main/SECURITY.md) +for its supported-versions table and reporting channels.