diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 762af12..fba03b2 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -275,6 +275,20 @@ jobs: if: steps.src-tarball.outputs.cache-hit != 'true' run: wget -q -O "${{ matrix.dir }}.tar.gz" "${{ matrix.url }}" + - name: Verify nginx tarball PGP signature + if: matrix.flavor == 'nginx' + run: | + set -euo pipefail + wget -q -O "${{ matrix.dir }}.tar.gz.asc" "${{ matrix.url }}.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "${{ matrix.dir }}.tar.gz.asc" "${{ matrix.dir }}.tar.gz" + rm -rf "$gnupghome" + - name: Configure (full debug build) run: | tar -xzf "${{ matrix.dir }}.tar.gz" @@ -425,6 +439,20 @@ jobs: if: steps.nginx-tarball-old.outputs.cache-hit != 'true' run: wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" + - name: Verify nginx tarball PGP signature + run: | + set -euo pipefail + wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz.asc" \ + -O "nginx-${NGINX_VERSION}.tar.gz.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "nginx-${NGINX_VERSION}.tar.gz.asc" "nginx-${NGINX_VERSION}.tar.gz" + rm -rf "$gnupghome" + - name: Configure & build nginx with module against libzstd 1.4.x run: | tar -xzf "nginx-${NGINX_VERSION}.tar.gz" @@ -501,6 +529,20 @@ jobs: if: steps.nginx-tarball.outputs.cache-hit != 'true' run: wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" + - name: Verify nginx tarball PGP signature + run: | + set -euo pipefail + wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz.asc" \ + -O "nginx-${NGINX_VERSION}.tar.gz.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "nginx-${NGINX_VERSION}.tar.gz.asc" "nginx-${NGINX_VERSION}.tar.gz" + rm -rf "$gnupghome" + - name: Configure & build nginx with ASAN + UBSAN run: | tar -xzf "nginx-${NGINX_VERSION}.tar.gz" diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index e5df458..b7b2692 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -1,18 +1,18 @@ name: Bump versions # Weekly: check nginx.org/angie.software for newer nginx-stable/angie -# releases than what's pinned in ci-deep.yml's build-flavors matrix, and pull -# the vendored nginx-tests submodule to its upstream HEAD. nginx mainline is -# NOT touched here -- it's resolved dynamically at CI run time (see -# build-test.yml's `resolve` job / ci-deep.yml's per-job resolve steps), so -# there's no static mainline pin to bump. +# releases than what's pinned in ci-deep.yml's build-flavors matrix. nginx +# mainline is NOT touched here -- it's resolved dynamically at CI run time +# (see build-test.yml's `resolve` job / ci-deep.yml's per-job resolve steps), +# so there's no static mainline pin to bump. # # tools/bump-versions.sh does the actual work (also runnable locally with -# --dry-run to preview). Commits and pushes straight to master -- this repo -# has no branch protection gate for this path, and a version/digest/submodule -# bump is exactly the kind of low-risk, easily-revertable change that doesn't -# need a human in the loop before landing. The next build-test.yml run on -# master is what actually proves the bump didn't break anything. +# --dry-run to preview). Opens a PR rather than pushing straight to master -- +# master carries a required-pull-request ruleset with no bypass actor, so a +# direct push is always rejected (audit sha e289021 F4); the bot instead +# pushes a branch and opens a PR via BUMP_PR_TOKEN (a classic PAT -- the +# default GITHUB_TOKEN cannot create PRs on myguard-labs repos). Normal +# review/required-checks still gate the merge. # # Action pins (keep in sync with build-test.yml): # actions/checkout@v5 -> 93cb6efe18208431cddfb8368fd83d5badbf9bfd @@ -28,36 +28,44 @@ concurrency: cancel-in-progress: true permissions: - contents: write + contents: read jobs: bump: - name: Bump nginx-stable/angie/nginx-tests pins + name: Bump nginx-stable/angie pins runs-on: [self-hosted, builder02, lxc] timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: - persist-credentials: true - submodules: true + persist-credentials: false fetch-depth: 0 - name: Run bump script id: bump run: bash tools/bump-versions.sh - - name: Commit and push if anything changed + - name: Open PR if anything changed + env: + GH_TOKEN: ${{ secrets.BUMP_PR_TOKEN }} run: | set -euo pipefail if [ -z "$(git status --porcelain)" ]; then - echo "nothing changed, skipping commit" + echo "nothing changed, skipping PR" exit 0 fi + branch="bump/versions-$(date -u +%Y%m%d)" git config user.name "myguard-bump-bot" git config user.email "actions@users.noreply.github.com" + git checkout -b "$branch" git add -A - git commit -m "chore: bump nginx-stable/angie/nginx-tests pins + git commit -m "chore: bump nginx-stable/angie pins Automated weekly version check (tools/bump-versions.sh)." - git push origin HEAD:master + git push origin "$branch" + gh pr create \ + --title "chore: bump nginx-stable/angie pins ($(date -u +%Y-%m-%d))" \ + --body "Automated weekly version check (tools/bump-versions.sh). Review the diff and let required CI checks gate the merge." \ + --base master \ + --head "$branch" diff --git a/.github/workflows/ci-deep.yml b/.github/workflows/ci-deep.yml index 3376183..068c39f 100644 --- a/.github/workflows/ci-deep.yml +++ b/.github/workflows/ci-deep.yml @@ -2,12 +2,11 @@ name: CI Deep # Exhaustive dynamic analysis — monthly cron + on-demand only, NEVER on PR. # The fast PR-time gates live in standalone workflows: fuzzing.yml (120s fuzz -# regression), valgrind.yml (60s memcheck-lite soak) and security-scanners.yml -# (scanners) run on every PR/push; THIS file stays the exhaustive monthly + -# workflow_dispatch run. codeql.yml is dropped entirely: -# CodeQL / the GitHub Security tab is GitHub-Advanced-Security only and does -# nothing on a private repo, so the SARIF-upload path is removed too — the -# scanners still run and GATE on findings, they just don't upload. +# regression), valgrind.yml (60s memcheck-lite soak), security-scanners.yml +# (scanners), and codeql.yml (semantic analysis) all run on every PR/push; +# THIS file stays the exhaustive monthly + workflow_dispatch run: the +# multi-flavor build matrix (mainline/stable/angie) plus hours-long fuzzing +# and full Memcheck/Helgrind soaks that would blow the PR-gate time budget. # # Jobs: build-flavors (nginx mainline/stable + angie) · fuzz (14400s/target) · # memcheck soak · helgrind soak · scanners. @@ -130,11 +129,23 @@ jobs: sudo apt-get install -y clang - name: Select fuzz duration + env: + RAW_FUZZ_SECONDS: ${{ github.event.inputs.fuzz_seconds }} run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "FUZZ_SECS=${{ github.event.inputs.fuzz_seconds }}" >> "$GITHUB_ENV" + case "$RAW_FUZZ_SECONDS" in + ''|*[!0-9]*) + echo "::error::fuzz_seconds must be a non-negative integer, got: $RAW_FUZZ_SECONDS" + exit 1 + ;; + esac + if [ "$RAW_FUZZ_SECONDS" -gt 14400 ]; then + echo "::error::fuzz_seconds exceeds the 14400s (4h) budget: $RAW_FUZZ_SECONDS" + exit 1 + fi + printf 'FUZZ_SECS=%s\n' "$RAW_FUZZ_SECONDS" >> "$GITHUB_ENV" else - echo "FUZZ_SECS=3600" >> "$GITHUB_ENV" + printf 'FUZZ_SECS=3600\n' >> "$GITHUB_ENV" fi - name: Build fuzz target diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cea4496..a499669 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -71,6 +71,20 @@ jobs: if: steps.nginx-tarball.outputs.cache-hit != 'true' run: wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" + - name: Verify nginx tarball PGP signature + run: | + set -euo pipefail + wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz.asc" \ + -O "nginx-${NGINX_VERSION}.tar.gz.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "nginx-${NGINX_VERSION}.tar.gz.asc" "nginx-${NGINX_VERSION}.tar.gz" + rm -rf "$gnupghome" + - name: Initialize CodeQL uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 with: diff --git a/.github/workflows/security-scanners.yml b/.github/workflows/security-scanners.yml index ce19819..bb3dcc0 100644 --- a/.github/workflows/security-scanners.yml +++ b/.github/workflows/security-scanners.yml @@ -53,6 +53,15 @@ jobs: ver=$(curl -fsSL https://nginx.org/en/download.html \ | grep -oP 'nginx-\K[0-9]+\.[0-9]+\.[0-9]+(?=\.tar\.gz)' | sort -V | tail -1) wget -q "https://nginx.org/download/nginx-${ver}.tar.gz" + wget -q "https://nginx.org/download/nginx-${ver}.tar.gz.asc" -O "nginx-${ver}.tar.gz.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in "$GITHUB_WORKSPACE"/tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "nginx-${ver}.tar.gz.asc" "nginx-${ver}.tar.gz" + rm -rf "$gnupghome" tar -xzf "nginx-${ver}.tar.gz"; cd "nginx-${ver}" CC=gcc ./configure --with-compat --add-dynamic-module="$GITHUB_WORKSPACE" >/dev/null echo "NGINX_SRC_TREE=$PWD" >> "$GITHUB_ENV" diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index cece4d3..6e89760 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -56,6 +56,16 @@ jobs: test -f "nginx-${NGINX_VERSION}.tar.gz" || \ wget -q -O "nginx-${NGINX_VERSION}.tar.gz" \ "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" + wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz.asc" \ + -O "nginx-${NGINX_VERSION}.tar.gz.asc" + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + for keyfile in tools/keys/*.key; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + gpg --quiet --verify "nginx-${NGINX_VERSION}.tar.gz.asc" "nginx-${NGINX_VERSION}.tar.gz" + rm -rf "$gnupghome" tar -xzf "nginx-${NGINX_VERSION}.tar.gz" cd "nginx-${NGINX_VERSION}" ./configure \ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ee6b53..c3e0fb5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,8 +58,12 @@ Your PR merges when **all** checks are green. If a gate fails and you believe the gate is wrong, say so in the PR — with evidence, not vibes. Before pushing a parser or fuzz-harness change, fuzz locally first: -`fuzz/build.sh` compiles every `fuzz/fuzz_*.c` with `-Werror`, so a stale -harness signature fails right there instead of in CI. +`fuzz/build.sh` compiles the target under ASAN+UBSAN +(`-fsanitize=address,undefined`), so a stale harness signature or a +sanitizer-caught bug surfaces right there instead of in CI. (The +module's own C sources are compiled `-Werror` — see +[`build-test.yml`](.github/workflows/build-test.yml) — the fuzz +harness itself is not.) ## Coding conventions diff --git a/README.md b/README.md index cab9995..21d2e3c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![CI Deep](https://github.com/myguard-labs/nginx-zstd-module/actions/workflows/ci-deep.yml/badge.svg)](https://github.com/myguard-labs/nginx-zstd-module/actions/workflows/ci-deep.yml) [![CodeQL](https://github.com/myguard-labs/nginx-zstd-module/actions/workflows/codeql.yml/badge.svg)](https://github.com/myguard-labs/nginx-zstd-module/actions/workflows/codeql.yml) -📖 **Background reading:** +📖 **Background reading:** - [zstd nginx module: what it does, bugs fixed](https://deb.myguard.nl/2026/05/zstd-nginx-module-what-it-does-bugs-fixed/) - [nginx zstd vs brotli vs zlib-ng — a compression comparison](https://deb.myguard.nl/2026/05/nginx-zstd-vs-brotli-vs-zlib-ng-compression/) @@ -13,7 +13,7 @@ An nginx module for [Zstandard (zstd)](https://facebook.github.io/zstd/) compression. Zstandard typically achieves better compression ratios than gzip at comparable or faster speeds, making it a good choice for reducing transmitted response sizes. -This is a hardened fork: every build is exercised against **nginx mainline and [Angie](https://angie.software/)**, the full test suite runs under **ASAN/UBSAN**, the `Accept-Encoding` parser is **continuously fuzzed**, and flawfinder/semgrep/clang-tidy run on every change (see the badges above and [Testing & CI](#testing--ci)). +This is a hardened fork: every push/PR is exercised against **nginx mainline**, the full test suite runs under **ASAN/UBSAN**, the `Accept-Encoding` parser is **continuously fuzzed**, flawfinder/semgrep/clang-tidy run on every change, and a monthly deep pass additionally covers **nginx stable** and **[Angie](https://angie.software/)** (see the badges above and [Testing & CI](#testing--ci)). # Table of Contents @@ -173,7 +173,7 @@ load_module modules/ngx_http_zstd_static_module.so; | Component | Minimum | Recommended | CI-verified | |---|---|---|---| -| **nginx** | 1.9.11 (first `--add-dynamic-module` release) | latest mainline / stable | **1.31.0 mainline** | +| **nginx** | 1.9.11 (first `--add-dynamic-module` release) | latest mainline / stable | **latest mainline** (resolved dynamically every CI run, not pinned) + **stable** and **Angie**, both pinned in CI Deep's monthly matrix | | **Angie** | 1.x | latest | **1.11.5** | | **libzstd** | **1.4.0** | **≥ 1.5.6** | 1.5.x (full suite) + **1.4.x** fallback-paths build | | **OS** | Linux/BSD/RHEL-family | — | Ubuntu (GitHub runners) | @@ -843,16 +843,19 @@ log_format zstd '$request in=$zstd_bytes_in out=$zstd_bytes_out ' # Testing & CI -Five workflows guard the module (badges at the top): four gate every -push & PR, and CI Deep runs the exhaustive monthly pass: +Six workflows guard the module (badges at the top): five gate every +push & PR, and CI Deep runs the exhaustive monthly pass. A seventh, +Bump, runs weekly but only opens a version-bump PR — it does not gate +anything itself. | Workflow | Cadence | What it does | |---|---|---| -| **Build & Test** | every push & PR | Compiles the module against **nginx 1.31.0 mainline** and **Angie 1.11.5** with strict `-Werror` flags, then runs the full test suite: 46 `Test::Nginx::Socket` filter tests, 21 static-module tests, and end-to-end Python smoke tests (truncation, `Vary`, boundary sizes, repeated/concurrent requests, terminal-frame, the proxy-unbuffered and compression-matrix regressions, per-request CCtx isolation, reload-under-load, `zstd_long`/LDM, `$zstd_ratio`). A separate matrix entry rebuilds against **libzstd 1.4.x** (from source) to exercise the `< 1.5.6` and `≥ 1.4.0` fallback paths, and a parallel job rebuilds with **ASAN+UBSAN** and re-runs the smoke tests plus a `zstd_dict_file` config-reload leak check. A 10-minute mixed-load soak under ASAN+UBSAN runs on the weekly schedule. | +| **Build & Test** | every push & PR | Compiles the module against **nginx mainline** (resolved at run time — see [Compatibility](#compatibility)) with strict `-Werror` flags, then runs the full test suite: 79 `Test::Nginx::Socket` filter tests, 28 static-module tests, 4 config-warning tests, and end-to-end Python smoke tests (truncation, `Vary`, boundary sizes, repeated/concurrent requests, terminal-frame, the proxy-unbuffered and compression-matrix regressions, per-request CCtx isolation, reload-under-load, `zstd_long`/LDM, `$zstd_ratio`). A separate matrix entry rebuilds against **libzstd 1.4.x** (from source) to exercise the `< 1.5.6` and `≥ 1.4.0` fallback paths, and a parallel job rebuilds with **ASAN+UBSAN** and re-runs the smoke tests plus a `zstd_dict_file` config-reload leak check. **Angie is not built here** — see CI Deep below, the only workflow that exercises it. | | **Security scanners** | every push & PR | flawfinder, clang-tidy (`cert-*`, `clang-analyzer-security.*`), and semgrep, with the reports uploaded as build artifacts. | | **Fuzzing** | every push & PR | A 120-second libFuzzer regression run for the `ngx_http_zstd_accept_encoding()` / `ngx_http_zstd_eval_qvalue()` RFC 9110 `Accept-Encoding`/q-value parser. The fuzz target is sliced from the shipped header at build time, so there is no copy drift. See [`fuzz/README.md`](fuzz/README.md). | | **Valgrind** | every push & PR | A 60-second Memcheck-lite soak against a debug nginx build, catching uninitialised-value reads and leaks that ASAN cannot. | -| **CI Deep** | monthly + manual dispatch | The exhaustive run: hours-long fuzzing on the same target, full Memcheck and Helgrind soaks (a valgrind soak is ~20–50× slower than native), and the same security scanners. | +| **CodeQL** | every push & PR + monthly | GitHub's semantic C/C++ analysis (`security-extended` query pack) over the filter/static module sources. | +| **CI Deep** | monthly + manual dispatch | The exhaustive run: a `build-flavors` matrix that compiles and runs the full `Test::Nginx::Socket` suite against **nginx mainline, nginx stable, and Angie** (the only workflow that builds Angie at all), hours-long fuzzing on the same target, full Memcheck and Helgrind soaks (a valgrind soak is ~20–50× slower than native), and the same security scanners. | The test suite includes a dedicated regression test for every known historical bug class: diff --git a/SECURITY.md b/SECURITY.md index 26465a7..8045f04 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -79,4 +79,5 @@ bash fuzz/build.sh && ./fuzz/fuzz_accept_encoding -max_total_time=60 fuzz/corpus ``` See [`.github/workflows/`](.github/workflows/) (build-test, codeql, -security-scanners, fuzzing) and [`AGENTS.md`](AGENTS.md). +security-scanners, fuzzing, valgrind, ci-deep, bump) and the +[Testing & CI](README.md#testing--ci) section of the README. diff --git a/fuzz/extract_parser.sh b/fuzz/extract_parser.sh index bf2b958..0e686da 100755 --- a/fuzz/extract_parser.sh +++ b/fuzz/extract_parser.sh @@ -39,18 +39,18 @@ awk ' print if ($0 == "}") { capture = 0 } } -' "$HEADER" > "$OUT" +' "$HEADER" >"$OUT" -if ! grep -q 'ngx_http_zstd_skip_quoted' "$OUT" \ - || ! grep -q 'ngx_http_zstd_eval_qvalue' "$OUT" \ - || ! grep -q 'ngx_http_zstd_accept_encoding' "$OUT" \ - || [ "$(tail -n1 "$OUT")" != "}" ]; then +if ! grep -q 'ngx_http_zstd_skip_quoted' "$OUT" || + ! grep -q 'ngx_http_zstd_eval_qvalue' "$OUT" || + ! grep -q 'ngx_http_zstd_accept_encoding' "$OUT" || + [ "$(tail -n1 "$OUT")" != "}" ]; then echo "✗ failed to extract the Accept-Encoding parser from $HEADER" >&2 echo " (header layout changed? update extract_parser.sh)" >&2 rm -f "$OUT" exit 1 fi -LINES=$(wc -l < "$OUT") +LINES=$(wc -l <"$OUT") echo "✓ extracted ngx_http_zstd_skip_quoted() + ngx_http_zstd_eval_qvalue()" \ - "+ ngx_http_zstd_accept_encoding() — $LINES lines -> $OUT" + "+ ngx_http_zstd_accept_encoding() — $LINES lines -> $OUT" diff --git a/ngx_http_zstd_common.h b/ngx_http_zstd_common.h index 4a94612..33cf508 100644 --- a/ngx_http_zstd_common.h +++ b/ngx_http_zstd_common.h @@ -211,6 +211,19 @@ ngx_http_zstd_eval_qvalue(ngx_str_t *ae, u_char *p) while (p < end && (*p == ' ' || *p == '\t')) { p++; } + + /* + * After the OWS that may trail any parameter (its q-specific + * check above only catches junk BEFORE this OWS skip, e.g. + * "q=1x"), only ';' (another parameter), ',' (next element), or + * end may follow -- anything else is trailing junk the loop's own + * "while (*p == ';')" condition would otherwise silently accept + * as "no more parameters" instead of rejecting (e.g. + * "zstd;q=1 garbage" previously parsed as zstd;q=1). + */ + if (p < end && *p != ';' && *p != ',') { + return -1; + } } return q; diff --git a/t/00-filter.t b/t/00-filter.t index 8392f87..40ea56f 100644 --- a/t/00-filter.t +++ b/t/00-filter.t @@ -1975,3 +1975,47 @@ Content-Encoding: zstd Content-Type: text/wgsl --- no_error_log [error] + + + +=== TEST 78: qvalue followed by whitespace then trailing junk is malformed +--- config + location /filter { + zstd on; + zstd_types text/plain; + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test; + } + location /test { + root $TEST_NGINX_PERL_PATH/suite/; + } +--- request +GET /filter +--- more_headers +Accept-Encoding: zstd;q=1 garbage +--- response_headers +Content-Length: 59738 +!Content-Encoding +--- no_error_log +[error] + + + +=== TEST 79: qvalue followed by tab then trailing junk is malformed +--- config + location /filter { + zstd on; + zstd_types text/plain; + proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test; + } + location /test { + root $TEST_NGINX_PERL_PATH/suite/; + } +--- request +GET /filter +--- more_headers +Accept-Encoding: zstd;q=0.5 junk +--- response_headers +Content-Length: 59738 +!Content-Encoding +--- no_error_log +[error] diff --git a/tools/bump-versions.sh b/tools/bump-versions.sh index 30e7487..76dfa5b 100755 --- a/tools/bump-versions.sh +++ b/tools/bump-versions.sh @@ -15,15 +15,20 @@ # no static mainline pin in this repo to bump. # - nginx STABLE pin -- ci-deep.yml's build-flavors matrix (label: stable) # - angie pin -- ci-deep.yml's build-flavors matrix (label: angie) -# - ANGIE_SHA256 -- tools/ci-build.sh (nginx tarballs are PGP-verified -# at build time instead, so no nginx sha256 table -# exists to bump) +# - NGINX_SHA256 -- tools/ci-build.sh, nginx-stable only (verified +# against the tarball's OWN PGP signature via the +# vendored keys in tools/keys/ before the digest is +# recorded, so a bad bump can't silently pin a +# malicious tarball's hash -- floating mainline has +# no entry here, see above) +# - ANGIE_SHA256 -- tools/ci-build.sh (angie.software publishes no +# PGP signature, so sha256 is the only check) # # A version bump with a stale sha256 pin is worse than no pin (ci-build.sh # treats a missing pin as "print a warning", but a WRONG pin is a hard FATAL -# for angie) -- so every angie version edit here is paired with a digest -# computed from the exact tarball that version resolves to, never carried -# over from a previous entry. +# for a pinned version) -- so every stable/angie version edit here is paired +# with a digest computed from the exact tarball that version resolves to, +# never carried over from a previous entry. set -euo pipefail @@ -41,9 +46,9 @@ cd "$(dirname "$0")/.." latest_nginx_stable() { local page page="$(curl -fsSL https://nginx.org/en/download.html)" - echo "${page#*"Stable version"}" \ - | grep -oE 'nginx-[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -1 \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' + echo "${page#*"Stable version"}" | + grep -oE 'nginx-[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' | head -1 | + grep -oE '[0-9]+\.[0-9]+\.[0-9]+' } latest_angie() { @@ -81,7 +86,7 @@ echo "pinned: nginx stable=$CUR_STABLE angie=$CUR_ANGIE" CHANGED=0 -# --- sha256 helper (angie only -- nginx is PGP-verified at build time) --- +# --- sha256 helpers --- sha256_for_angie() { local version="$1" url tmp digest url="https://download.angie.software/files/angie-${version}.tar.gz" @@ -92,6 +97,41 @@ sha256_for_angie() { echo "$digest" } +# nginx-stable also gets its detached signature checked against the vendored +# keyring (tools/keys/) before we trust the digest we're about to pin -- a +# bump script that records a hash without checking provenance first would +# just be moving the F3 trust gap here instead of fixing it. +sha256_for_nginx_stable() { + local version="$1" url tmp digest gnupghome keyfiles + url="https://nginx.org/download/nginx-${version}.tar.gz" + tmp="$(mktemp)" + curl -fsSL "$url" -o "$tmp" + curl -fsSL "${url}.asc" -o "${tmp}.asc" + + gnupghome="$(mktemp -d)" + GNUPGHOME="$gnupghome" + export GNUPGHOME + chmod 700 "$gnupghome" + shopt -s nullglob + keyfiles=(tools/keys/*.key) + shopt -u nullglob + for keyfile in "${keyfiles[@]}"; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + if ! gpg --quiet --verify "${tmp}.asc" "$tmp" 2>/dev/null; then + rm -rf "$gnupghome" "$tmp" "${tmp}.asc" + unset GNUPGHOME + echo "FATAL: PGP verification failed for nginx-${version}.tar.gz -- refusing to pin an unverified digest" >&2 + exit 1 + fi + rm -rf "$gnupghome" + unset GNUPGHOME + + digest="$(sha256sum "$tmp" | awk '{print $1}')" + rm -f "$tmp" "${tmp}.asc" + echo "$digest" +} + # --- bump a version in ci-deep.yml's build-flavors matrix ---------------- bump_matrix_pin() { local label="$1" old="$2" new="$3" @@ -114,17 +154,27 @@ PYEOF bump_angie_sha256_pin() { local old="$1" new="$2" digest="$3" - grep -q "\[\"${new}\"\]" tools/ci-build.sh && return 0 # already pinned + grep -q "\[\"${new}\"\]" tools/ci-build.sh && return 0 # already pinned # Insert the new pin right after the table's opening line; leave old # entries in place (ci-build.sh keys by version, older callers still work). sed -i "/declare -A ANGIE_SHA256=(/a\\ [\"${new}\"]=\"${digest}\"" tools/ci-build.sh CHANGED=1 } +bump_nginx_sha256_pin() { + local old="$1" new="$2" digest="$3" + grep -q "\[\"${new}\"\]" tools/ci-build.sh && return 0 # already pinned + sed -i "/declare -A NGINX_SHA256=(/a\\ [\"${new}\"]=\"${digest}\"" tools/ci-build.sh + CHANGED=1 +} + if [ "$NEW_STABLE" != "$CUR_STABLE" ]; then echo "bump nginx stable: $CUR_STABLE -> $NEW_STABLE" if [ "$DRY_RUN" = 0 ]; then + DIGEST="$(sha256_for_nginx_stable "$NEW_STABLE")" + echo " sha256 $DIGEST" bump_matrix_pin stable "$CUR_STABLE" "$NEW_STABLE" + bump_nginx_sha256_pin "$CUR_STABLE" "$NEW_STABLE" "$DIGEST" else CHANGED=1 fi diff --git a/tools/ci-build.sh b/tools/ci-build.sh index f1a09b6..7710657 100755 --- a/tools/ci-build.sh +++ b/tools/ci-build.sh @@ -12,15 +12,23 @@ # built under /tmp and deleted it on exit. NO_CACHE=1 forces a from-scratch # rebuild of a given flavor/version pair. # -# nginx tarballs are verified via PGP (nginx.org's own recommended method, -# https://nginx.org/en/pgp_keys.html) — no sha256sum file is published for -# them. angie.software does not publish a PGP signature for release tarballs, -# so angie tarballs are instead checked against a pinned sha256 recorded -# below, computed once from an HTTPS fetch (same approach the sibling -# nginx-skeleton-module repo uses for both flavors). A version not in the -# table still builds (this script tracks moving releases; refusing to build -# an unpinned version would break every future version bump until someone -# updates the table first) but prints a loud warning so the gap is visible. +# nginx tarballs are verified via PGP against the signer keys VENDORED in +# tools/keys/ (committed to this repo) — never fetched from nginx.org at CI +# time. Bootstrapping the verification keys from the same origin that serves +# the tarball+signature gives an origin compromise the ability to substitute +# all three (audit sha e289021 F3); a key rotation is a reviewed PR that adds +# a new file under tools/keys/, not a runtime fetch. For a statically-pinned +# nginx version (nginx-stable in ci-deep.yml's matrix) the sha256 is ALSO +# checked against NGINX_SHA256 below, same as angie -- floating mainline +# (resolved at CI run time, see build-test.yml's `resolve` job) has no static +# pin and relies on PGP alone. angie.software does not publish a PGP +# signature for release tarballs, so angie tarballs are checked by sha256 +# only, pinned in ANGIE_SHA256 below (computed once from an HTTPS fetch, same +# approach the sibling nginx-skeleton-module repo uses for both flavors). A +# version not in a pin table still builds (this script tracks moving +# releases; refusing to build an unpinned version would break every future +# version bump until someone updates the table first) but prints a loud +# warning so the gap is visible. set -euo pipefail @@ -31,7 +39,7 @@ FLAVOR="${1:-nginx}" VERSION="${2:-}" case "$FLAVOR" in - nginx|angie) ;; + nginx | angie) ;; *) echo "ERROR: unsupported flavor: $FLAVOR (want: nginx|angie)" >&2 exit 2 @@ -45,9 +53,9 @@ if [ -z "$VERSION" ]; then fi # Resolve the current mainline release; nginx.org only keeps the newest # mainline tarball, so a hardcoded version eventually 404s. - VERSION="$(curl -fsSL https://nginx.org/en/download.html \ - | grep -oP 'nginx-\K[0-9]+\.[0-9]+\.[0-9]+(?=\.tar\.gz)' \ - | sort -V | tail -1)" + VERSION="$(curl -fsSL https://nginx.org/en/download.html | + grep -oP 'nginx-\K[0-9]+\.[0-9]+\.[0-9]+(?=\.tar\.gz)' | + sort -V | tail -1)" if [ -z "$VERSION" ]; then echo "ERROR: could not resolve mainline nginx version from nginx.org" >&2 exit 1 @@ -59,6 +67,15 @@ declare -A ANGIE_SHA256=( ["1.11.5"]="b5f297c6df2a74b9d0091a7cdd747fffd2d0e1d0be43632da61c1c7539db2043" ) +# --- pinned sha256 for statically-pinned nginx-stable versions we've +# actually verified (floating mainline, resolved at CI run time, has no +# entry here and relies on the PGP check alone) --- +declare -A NGINX_SHA256=( + ["1.30.3"]="e5823dc6f45610993def93ebf6cfce68264af4958c77e874b7d20f3709001b8f" +) + +KEYRING_DIR="$SCRIPT_DIR/keys" + ROOT="${BUILD_ROOT:-$MODULE_DIR/.build}" NO_CACHE="${NO_CACHE:-0}" ZSTD_MODULE_DIR="$MODULE_DIR" @@ -100,56 +117,90 @@ echo "" if [ ! -f "$TARBALL" ]; then wget -q -O "$TARBALL" "$URL" echo "✓ Downloaded ${DIR}.tar.gz" +else + echo "✓ Using cached ${DIR}.tar.gz" +fi - if [ "$FLAVOR" = "nginx" ]; then - # Always fetch over HTTPS and verify the detached PGP signature - # against the nginx release-signing keys before unpacking. A plain - # HTTP download lets a network attacker swap the source that we then - # configure and compile. +# Verification always runs, cache hit or fresh download -- a cached tarball +# is exactly as untrusted as a freshly-downloaded one until re-checked +# (audit sha e289021 F3: "cached archives are not reverified" was a gap in +# the pre-fix version of this script). +if [ "$FLAVOR" = "nginx" ]; then + # Always fetch over HTTPS and verify the detached PGP signature against + # the nginx release-signing keys VENDORED in tools/keys/ (not fetched + # from nginx.org, which also serves the tarball+signature -- see the + # file header). A plain HTTP download lets a network attacker swap the + # source that we then configure and compile. + if [ ! -f "${TARBALL}.asc" ]; then wget -q "${URL}.asc" -O "${TARBALL}.asc" + fi - gnupghome="$(mktemp -d)" - export GNUPGHOME="$gnupghome" - chmod 700 "$gnupghome" - for key in nginx_signing mdounin maxim sb thresh pluknet arut; do - wget -q "https://nginx.org/keys/${key}.key" -O - 2>/dev/null \ - | gpg --quiet --import 2>/dev/null || true - done - - if gpg --quiet --verify "${TARBALL}.asc" "$TARBALL"; then - echo "✓ PGP signature verified for ${DIR}.tar.gz" - else - echo "✗ PGP signature verification FAILED for ${DIR}.tar.gz" >&2 - rm -rf "$gnupghome" "$TARBALL" "${TARBALL}.asc" - exit 1 - fi - rm -rf "$gnupghome" - unset GNUPGHOME + gnupghome="$(mktemp -d)" + export GNUPGHOME="$gnupghome" + chmod 700 "$gnupghome" + shopt -s nullglob + keyfiles=("$KEYRING_DIR"/*.key) + shopt -u nullglob + if [ ${#keyfiles[@]} -eq 0 ]; then + echo "✗ no vendored keys found in $KEYRING_DIR" >&2 + rm -rf "$gnupghome" "$TARBALL" "${TARBALL}.asc" + exit 1 + fi + for keyfile in "${keyfiles[@]}"; do + gpg --quiet --import "$keyfile" 2>/dev/null + done + + if gpg --quiet --verify "${TARBALL}.asc" "$TARBALL"; then + echo "✓ PGP signature verified for ${DIR}.tar.gz" else - EXPECTED="${ANGIE_SHA256[$VERSION]:-}" - if [ -n "$EXPECTED" ]; then - ACTUAL="$(sha256sum "$TARBALL" | awk '{print $1}')" - if [ "$ACTUAL" != "$EXPECTED" ]; then - echo "✗ sha256 mismatch for ${DIR}.tar.gz" >&2 - echo " expected: $EXPECTED" >&2 - echo " actual: $ACTUAL" >&2 - rm -f "$TARBALL" - exit 1 - fi - echo "✓ sha256 verified for ${DIR}.tar.gz" - else - echo "WARNING: no pinned sha256 for angie $VERSION -- add one to" \ - "ANGIE_SHA256 in tools/ci-build.sh (downloaded tarball is" \ - "UNVERIFIED)" >&2 + echo "✗ PGP signature verification FAILED for ${DIR}.tar.gz" >&2 + rm -rf "$gnupghome" "$TARBALL" "${TARBALL}.asc" + exit 1 + fi + rm -rf "$gnupghome" + unset GNUPGHOME + + EXPECTED="${NGINX_SHA256[$VERSION]:-}" + if [ -n "$EXPECTED" ]; then + ACTUAL="$(sha256sum "$TARBALL" | awk '{print $1}')" + if [ "$ACTUAL" != "$EXPECTED" ]; then + echo "✗ sha256 mismatch for ${DIR}.tar.gz" >&2 + echo " expected: $EXPECTED" >&2 + echo " actual: $ACTUAL" >&2 + rm -f "$TARBALL" "${TARBALL}.asc" + exit 1 fi + echo "✓ sha256 verified for ${DIR}.tar.gz" fi else - echo "✓ Using cached ${DIR}.tar.gz" + EXPECTED="${ANGIE_SHA256[$VERSION]:-}" + if [ -n "$EXPECTED" ]; then + ACTUAL="$(sha256sum "$TARBALL" | awk '{print $1}')" + if [ "$ACTUAL" != "$EXPECTED" ]; then + echo "✗ sha256 mismatch for ${DIR}.tar.gz" >&2 + echo " expected: $EXPECTED" >&2 + echo " actual: $ACTUAL" >&2 + rm -f "$TARBALL" + exit 1 + fi + echo "✓ sha256 verified for ${DIR}.tar.gz" + else + echo "WARNING: no pinned sha256 for angie $VERSION -- add one to" \ + "ANGIE_SHA256 in tools/ci-build.sh (downloaded tarball is" \ + "UNVERIFIED)" >&2 + fi fi if [ ! -d "$SRCDIR" ]; then tar -xzf "$TARBALL" -C "$ROOT" - mv "$ROOT/$DIR" "$SRCDIR" + # The tarball's top-level dir name ($DIR = "-") and our + # build-tree name ($SRCDIR) are the same string, so the extracted path + # IS already $SRCDIR -- moving it onto itself fails ("cannot move to a + # subdirectory of itself") on a clean root / NO_CACHE=1 run. Only mv if + # extraction actually landed somewhere else. + if [ "$ROOT/$DIR" != "$SRCDIR" ]; then + mv "$ROOT/$DIR" "$SRCDIR" + fi fi cd "$SRCDIR" diff --git a/tools/keys/arut.key b/tools/keys/arut.key new file mode 100644 index 0000000..55c192b --- /dev/null +++ b/tools/keys/arut.key @@ -0,0 +1,114 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGYXyiQBEAC4jm1y+ODV4+YDGj9vp2BgHB4FJeQdgrBiVX+Mb2qCrEqJgeKV +fVwKjkVYqnb76TTybdOKqCP5wdQrncKAKlXsMq6sdsiwPSrdRcjkeiE29WWrtbB4 +i+VObnoWklMblMxFQ1XQIkjs2wviidKjJw2VV3i4XnLSrHhWaWqviTLZCMQymoPs +F+Tfu1WX9OUfOquekZ5KjkyBxB4ep6+NPeuIkPnW0SiTUhU8tbi8v0aBZEHSZLqE +mq8KLROVuYSPvtU+NtaXAM09BHEVCfb409aDps9p6AFT+IN8yoOegGdEZjp6hJvS +HxbhuwqNEtg4dTEV515YUCgKabqU1QaqI/Y0+Pdkpep1KRFc9YUYttDkCw7Ybu2u +fwTGzwAbD+ThAIOdzmMDodzZaEMf+9fQG4bnO1PdNbXzyP7Kv9qzGa65+9oGCPOS +qTpISR8pvzoI8w/Z/vG71ob/nQ6Xm0L986ksErdGhu16ZI7lW2eDYqy2IoFfbeSz +HHxk484/pEibrlCRbP2Id+zULfxo1HGOGg+PAY9Q2uNzABsGDMnOhIvXHS+hP7oB +sO9A4Prqu6K6cMp3QI219tmmOUegJpmGGPzoNgxR7H30wNcjZPv4PWr/c0fP70Ny +ilgbdcEMDSHks30AmiuIvcUxo3A21p2nnpxsKAKYx42UJkyEK0HILMzcqwARAQAB +tCZSb21hbiBBcnV0eXVueWFuIDxyLmFydXR5dW55YW5AZjUuY29tPokCTgQTAQgA +OBYhBEM4eCXdsbuX7Da6XQB8jXwV2HNpBQJmF8pXAhsDBQsJCAcCBhUKCQgLAgQW +AgMBAh4BAheAAAoJEAB8jXwV2HNppvQP/AjzdPKkGRzJkb1ioto/IEP1YhA/Eayk +hvejJ0vyWVHXXH7FLW9fIZoApcsD1J8/7zIANm+62IfT3QNbL2R44IyhJB3AY22l +t0ToLxodfugegF3NPYYyFOSRUoPD4g2T/dMCPOBX4MNEAnAlCmxAMaJNmQUO76IY +GwELa3CH3Aqf7bthKy8P36G11hu7NgH6V9mVIRIpfnfpXFQIztj+vsWtswu4M5t7 +BNJwx4a2KTCVQpTdff5/0dO/5drQDxLbIg681WZk3Oe8Eu6nSc0Ud02NIkg1TQH/ +MryAp7o/ua3LRem+W/cktnT60p4uXPVZ3Rvg3zOmJSNJ+eIXY2+sDeZEPaROKldA +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 +kZv2zEcTtzlAYNEumy8KrJzICmPLS7BEC8w= +=ilJ3 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/keys/maxim.key b/tools/keys/maxim.key new file mode 100644 index 0000000..9307b36 --- /dev/null +++ b/tools/keys/maxim.key @@ -0,0 +1,81 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=5smL +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/keys/nginx_signing.key b/tools/keys/nginx_signing.key new file mode 100644 index 0000000..656d40c --- /dev/null +++ b/tools/keys/nginx_signing.key @@ -0,0 +1,192 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +vcV0wl+DODz9hw== +=oWlI +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ozw9yNdOZmnk2Prj7+mBuchbT3BJOQ== +=AgHy +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +UYda9m8fOnaIew== +=TEOn +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/keys/pluknet.key b/tools/keys/pluknet.key new file mode 100644 index 0000000..fd3f983 --- /dev/null +++ b/tools/keys/pluknet.key @@ -0,0 +1,65 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGKE4psBEADpHSM/IxFD1nXBmnODYXzcl2A+6b6m9m1m2Y4Dlr0ed+y5Lxne +QidE9I74A2KSm6+eHW2yh4i1ZwZbmwpmQqM+j5BMt7axoXOdKSyN+fYtUakzNbBN +EDRKT79q/zIzkgTJradHkCQkwF1W3go+qPXjR2ZEnLma9dZED9VNI6PmOpeYaASo +IkEfbKbwa/vPrvnDSSYY6Y02RXSRk5U1NvQgVUTJP9WGK7NlPUcTBDELLQv6fFPU +kjBOel6MecsQ+v8iq4RJF2cbVF0hNjbAiNldjLV74Xd7yWVRlCbdb2agyvQjMNrD +jHSvbEMiNB3R8yBHVW2Zldv8q0XjcwoDfdiZYFJe3lRUYmv6I2p+/DptD4r/3ILI +peGZtSeOdQEw+vvODL/Ehq03anTrzcpZ6sDLfLrYJhYcrltj0/LMUnLDAjciwRUq +XI46EfxwqsdLeqoZFQeO3LOFsh0kJKR2xOrUHIVy84NJ4Gmro6WmUkb1NfdjyHzF +z8Lfbo46NKoTcwFsFF0q74jVVIVNUyIS91DusiMqLCsP8jqDOz/kyP4bOJQ+aUXf +BANn4Ll1TFWsJ417moxz+Pi5sTaI0na8z2XB1N9WPsSml3FS75hJPJshN2T3VIea +zB7GFWqk33ynSDt+cAisG5nsK9fFdcH+t5wm59oobyFbFhKxwX6ROuxlZwARAQAB +tCRTZXJnZXkgS2FuZGF1cm92IDxwbHVrbmV0QG5naW54LmNvbT6JAk4EEwEKADgW +IQTWeGzjA9mpAimY3GzIRk1UmvdcCgUCYoTimwIbAwULCQgHAwUVCgkICwUWAwIB +AAIeAQIXgAAKCRDIRk1UmvdcCqbOD/9Htgk3mWvUFmrApkWQTIDNmLACZ1Sw1PXj +Uqte8StYB0bYY+nmAXs7O5eC2h1ViParl7En1joEEMQQmH0qSnw4X1CM/hA8TAYW +mBPITTNWo/R52WoyWeWGFnFNIperQmuIZc+pXm0VEFVPiX/2DXbCIu+jaXySvlCN +LekmOD4VC7dJS8/ohoaXOR2T8ufS+1CsyPXomEb+COhqRZ3EVBa+k7pnElkFft3Y +a1fR0AgatZFQpy+ukePhK7s/M5RGhDJWHgSAZFkf+X2jVV4NRJ+XsY80gU5DD2ZX +QT6Je6Knxqk7FnWNSxkhReH6Ss5flZSoGDCmJ2AsPtGeUhus2fGqeN+waGKTZC35 +die2V4/cro1SWswSI6Y5GFDZT1olIUztPmSXU/A3oyizJI7XZybwUbpk5kK83VXm +el3U/7Qr/VErlDWFefZWeUvT1RILZ8IRoNj4dv158RnKHt9G508A5qz4hUPKoSeq +SiXhYwfkc31WPzIJ4ev+X5Ka2sG/CKbEMJ7qwc0Kadiu+ePPfqqbXjpTWRyrbcRM +hRNcLNUi1SLWMBClOQG+5GNG1dPPHkbj4dO1OZuaUMwQdu8R8NlsGoVWS40bmVv5 +pXstzYCl7k/UnC/Ytlq61GeAoq8ILa6jGj0EWqlhvi0ZNMN+fROhzrRlTzIr/+WE +Xf8EiVNFSbQlU2VyZ2V5IEthbmRhdXJvdiA8cy5rYW5kYXVyb3ZAZjUuY29tPokC +TgQTAQoAOBYhBNZ4bOMD2akCKZjcbMhGTVSa91wKBQJihO2zAhsDBQsJCAcDBRUK +CQgLBRYDAgEAAh4BAheAAAoJEMhGTVSa91wKgLQQANaf4UMndkWoefDQPkJ5qR4K +fuV0WRz59riZEApTkVpPXzl8Y1i8Rgt9pa1v1i12vPyIXKav1rJXQcuDEzqrhQ2G +yvuAE2U/t2mYaMUmwxWO2d8JA3slvBSgOkiYpbLooDizAdKMT5UQWGyw31Wm51iz +HjoztebsyXeXgq9VDjv3D8LUBr/OY3Hguj6HV+zRtC95qgXYadW2FiCtvBK6RTDb +iShTuseLSheGh9dZIUSnzaOiJpDA61ZDYtFZxSpe67vEzhSfHVsF+ZdCjoWhhVv+ ++2wR4E0VQQtOM9uX1PMlZ5Ymr02/gidsXCM0ZjYXx4cDDhnq+nKomN64VloXWY9t +PIi86XmzcSWlGUd+Ac6LyW7/f64bUWs4Ih0Idl0PF0sAr/6axKUsIs1nbn5MEtXk +ZPAjcDLqLb9IIQaXRurm/il8v+bLXVBOJq33YUuGRuz8pu4vPA5Q97zglqhlIgbu +prHMJ9hl5q39JwS3As2rK0o6Q9VVKr29rqSEfk4wEttvk0QMMU5zEvVl8MtqPj42 +qURqpHOadFbYMTwhUmRBUszRZPa5/pWqq0gWOtpyCWFVAsHFWQGJM1Eo6gGEyHZM +YgBp+d29p2p409r1+06U67GBnXvUy0RyIpkLQtU+lyOJ6vvrBmmsDs/gc69GnlSC +tZmCt0pLesJ7ZJzGdDkduQINBGKE4psBEADQr/enuDeVT11v6ejuYrg7aaZaGFUe +3i28bQ4pRUKNfxs7zVYDDHi2i2bhS5j2yQnbsQtGcgoenw6lapmdQRzr4vjQAz9o +kT6l4qpqvFFQM0wZTnigVDmmO9vTHR8Uk3iCKTd2ax3oko/xPWWYJautJ6ex8cOA +coHSDeOjuIWSxCKq0BDFp6LoxkM8nuyLAX2cbhI3LncaZhVveMeN+Fmcsv+WpkKs +yhX92umZuGwlraSyFy23FiRWSZPu9qVIxMMHvVrQJIgfhyWaHFzoF4M4qDoSKx92 +uWfUWgFwPOxOJ6/YcPsX4T8qTl9htmwPN0BibPTlcWaIFXtiU5bE1MivUPeACrI/ +gwUfCR3Mg+GYc13C6jzepREUhI7PLi3+A203PlMZd/aaSZkP6j+h4cwdapH5P4uF +7T1EQ0MSdx3neAvu5p0IM6JpriwxfT3HsG+Y952T6MIeXcjNRebsBrygJhJ0/vyr +wV5t8jL0yQty4CiE/QFnBs42l+rngi7K7Y1AZRBGK7JA09XaoLrfLmS+PrbYPsaJ +flkM8GzUB7BBCLozxDHPzmPkf/A1w3XHZnYuZmS+pvjWCIoKpLQHI99oSUGho/TR +gMRO4v7EAzluqCiepMl0xwFfHB115ND/mATazc4Pt6FxUsqffzfZrN01e1UVPrp5 +4x6YLO80JnOY6QARAQABiQI2BBgBCgAgFiEE1nhs4wPZqQIpmNxsyEZNVJr3XAoF +AmKE4psCGwwACgkQyEZNVJr3XAp9ghAAgCgErxQYn/Lh/mzsxYXPnisggcBpceks +mGw7knj1EGkXqq9CHn3EjCw8dB5N857UFlUr++DHwpFL5O36PRQo33RIUFbmBypG +8C/xX1jWGu3xcaqS3P1ncsSSl6ckdvy9pjMxThm/RkXO0eJCn7FcanwPJXEB3Pbb +mm0wLI2OXl/m7l5QAr7kErnPvGNzcbX6G35Q/MY8mumBWQ9H53R5ZPpi+OS40Wfn +pZNKdh/Acwa7+2RokPqoOcJfxVdBOUigXTzb45qZgqEsSR7bkZAy2E80A/sJKPqs +OGjp9cog3rBYyNBn5dasfR9KeBtluKnjUbzutXsQoKUSECY00YGrtneSXMku5hoE +Dguk68w/L63ZApYHO/JTgJAYvqPOErAVUegPIw2CT1/2qi5vpClBcKkNS7RXrssA +X+lElE0zbzX3bNG+lQuXby7jNUFYltkEiz6vTtc4HuHy8u40DHMswzkoDr0T8IE0 +7ZRAWXwV1nlA/dI337cHCsWMJyqem5wZZO13iqe07qaCg1uvBPeqDo81hOCn1us7 +l5SYRUTlt7KSFEHZ+Sx4bmVneAuRi5okaQdmrepy/ss/vVpRwWuQxsPkvT8boS7s +mqOVsZFcNOuUJPUyOz1dHUL6FMYpk1dw+9n41gO4fLBzJekFTB/fxL6SRbYFWWn7 +x0VGHDmuaYQ= +=HmVo +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/keys/sb.key b/tools/keys/sb.key new file mode 100644 index 0000000..16c68c9 --- /dev/null +++ b/tools/keys/sb.key @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.11 (FreeBSD) + +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 +CoHAyoqJeb/xLBwuKWg0/4U= +=iFlP +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/keys/thresh.key b/tools/keys/thresh.key new file mode 100644 index 0000000..ecc27c5 --- /dev/null +++ b/tools/keys/thresh.key @@ -0,0 +1,147 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=t1B0 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tools/soak.sh b/tools/soak.sh index aeeeada..35ac882 100755 --- a/tools/soak.sh +++ b/tools/soak.sh @@ -26,12 +26,12 @@ mkdir -p "$WORK/conf" "$WORK/logs" "$WORK/html" # Mixed payload sizes: tiny (sub-min_length), medium, large (multi-buffer), # and a chunked/no-Content-Length upstream path. -head -c 50 /dev/urandom | base64 > "$WORK/html/tiny" -head -c 200000 /dev/urandom | base64 > "$WORK/html/medium" -head -c 4000000 /dev/urandom | base64 > "$WORK/html/large" -printf 'AAAAAAAAAA%.0s' $(seq 1 20000) > "$WORK/html/compressible" +head -c 50 /dev/urandom | base64 >"$WORK/html/tiny" +head -c 200000 /dev/urandom | base64 >"$WORK/html/medium" +head -c 4000000 /dev/urandom | base64 >"$WORK/html/large" +printf 'AAAAAAAAAA%.0s' $(seq 1 20000) >"$WORK/html/compressible" -cat > "$WORK/conf/nginx.conf" <"$WORK/conf/nginx.conf" </dev/null && break + if curl -fsS -o /dev/null "http://127.0.0.1:18222/tiny" 2>/dev/null; then + ready=1 + break + fi sleep 0.1 done +if [ "$ready" -ne 1 ]; then + echo "FAIL: nginx never became ready (no successful /tiny request in 10s)" >&2 + kill "$NGINX_PID" 2>/dev/null || true + tail -40 "$WORK/logs/error.log" || true + exit 1 +fi -echo "soak: ${DURATION}s, concurrency ${CONC}$( [ "${USE_VALGRIND:-0}" = 1 ] && echo ' (valgrind)'; [ "${USE_HELGRIND:-0}" = 1 ] && echo ' (helgrind)')" -END=$(( $(date +%s) + DURATION )) +echo "soak: ${DURATION}s, concurrency ${CONC}$( + [ "${USE_VALGRIND:-0}" = 1 ] && echo ' (valgrind)' + [ "${USE_HELGRIND:-0}" = 1 ] && echo ' (helgrind)' +)" +END=$(($(date +%s) + DURATION)) fail=0 worker() { local wid="$1" local paths=(/tiny /medium /large /compressible - "/bypass" "/bypass?nozstd=1") - local i=0 + "/bypass" "/bypass?nozstd=1") + local i=0 ok=0 bad=0 while [ "$(date +%s)" -lt "$END" ]; do p=${paths[$((RANDOM % ${#paths[@]}))]} # Vary Accept-Encoding incl. clients that do not support zstd. @@ -106,31 +119,49 @@ worker() { i=$((i + 1)) body="$WORK/r.${wid}.${i}" if curl -fsS -H "Accept-Encoding: $ae" \ - "http://127.0.0.1:18222$p" -o "$body" 2>/dev/null; then + "http://127.0.0.1:18222$p" -o "$body" 2>/dev/null; then # If it came back zstd-encoded, it must decode cleanly. if head -c4 "$body" | od -An -tx1 | grep -q '28 b5 2f fd'; then - zstd -dq -c "$body" >/dev/null 2>&1 || { echo "BAD zstd $p"; return 1; } + if zstd -dq -c "$body" >/dev/null 2>&1; then + ok=$((ok + 1)) + else + echo "BAD zstd $p" + bad=$((bad + 1)) + fi + else + ok=$((ok + 1)) fi + else + echo "FAIL request $p (curl exit $?)" + bad=$((bad + 1)) fi rm -f "$body" done + echo "worker $wid: $ok ok, $bad bad/failed" + [ "$bad" -eq 0 ] && [ "$ok" -gt 0 ] } pids=() -for w in $(seq 1 "$CONC"); do worker "$w" & pids+=($!); done +for w in $(seq 1 "$CONC"); do + worker "$w" & + pids+=($!) +done for pid in "${pids[@]}"; do wait "$pid" || fail=1; done # Clean shutdown so all pool cleanups (incl. CCtx/CDict) run. kill -QUIT "$NGINX_PID" 2>/dev/null || true -wait "$NGINX_PID" 2>/dev/null; rc=$? +wait "$NGINX_PID" 2>/dev/null +rc=$? problems=0 if ls "$WORK"/logs/asan* >/dev/null 2>&1; then - echo "FAIL: ASAN/UBSAN report:"; cat "$WORK"/logs/asan*; problems=1 + echo "FAIL: ASAN/UBSAN report:" + cat "$WORK"/logs/asan* + problems=1 fi if ls "$WORK"/logs/valgrind.* "$WORK"/logs/helgrind.* >/dev/null 2>&1; then if grep -qE 'ERROR SUMMARY: [1-9]|definitely lost: [1-9]' \ - "$WORK"/logs/valgrind.* "$WORK"/logs/helgrind.* 2>/dev/null; then + "$WORK"/logs/valgrind.* "$WORK"/logs/helgrind.* 2>/dev/null; then echo "FAIL: valgrind/helgrind errors:" grep -E 'ERROR SUMMARY|definitely lost' \ "$WORK"/logs/valgrind.* "$WORK"/logs/helgrind.* 2>/dev/null @@ -148,14 +179,17 @@ if ls "$WORK"/logs/valgrind.* "$WORK"/logs/helgrind.* >/dev/null 2>&1; then fi fi if grep -nE '\[alert\]|\[emerg\]' "$WORK/logs/error.log" 2>/dev/null; then - echo "FAIL: alert/emerg in error.log"; problems=1 + echo "FAIL: alert/emerg in error.log" + problems=1 fi if [ "$fail" -ne 0 ]; then - echo "FAIL: a worker reported a corrupted response"; problems=1 + echo "FAIL: a worker reported a corrupted response" + problems=1 fi # QUIT is a clean exit; valgrind uses 99, ASAN 42 on error. if [ "$rc" -ne 0 ] && [ "$rc" -ne 130 ]; then - echo "FAIL: nginx exited $rc"; tail -40 "$WORK/logs/error.log" || true + echo "FAIL: nginx exited $rc" + tail -40 "$WORK/logs/error.log" || true problems=1 fi diff --git a/tools/test_reload_leak.sh b/tools/test_reload_leak.sh index e3a77d9..4d66168 100755 --- a/tools/test_reload_leak.sh +++ b/tools/test_reload_leak.sh @@ -27,9 +27,9 @@ trap cleanup EXIT mkdir -p "$WORK/conf" "$WORK/logs" "$WORK/html" # A non-trivial dictionary so ZSTD_createCDict() actually allocates. -head -c 8192 /dev/urandom | base64 > "$WORK/html/zstd.dict" +head -c 8192 /dev/urandom | base64 >"$WORK/html/zstd.dict" -cat > "$WORK/conf/nginx.conf" <"$WORK/conf/nginx.conf" </dev/null || true)" ]; then echo "❌ ASAN reported a leak across $RELOADS reloads:" diff --git a/tools/test_test_package_artifact.py b/tools/test_test_package_artifact.py index 943e356..d770227 100644 --- a/tools/test_test_package_artifact.py +++ b/tools/test_test_package_artifact.py @@ -24,8 +24,8 @@ def create_layout(self, root: pathlib.Path, flavor: str) -> pathlib.Path: snippet.write_text( "\n".join( [ - f"load_module modules/ngx_http_zstd_filter_module.so;", - f"load_module modules/ngx_http_zstd_static_module.so;", + "load_module modules/ngx_http_zstd_filter_module.so;", + "load_module modules/ngx_http_zstd_static_module.so;", ] ), encoding="utf-8", diff --git a/tools/test_window_cap.py b/tools/test_window_cap.py index c6f4817..e495688 100755 --- a/tools/test_window_cap.py +++ b/tools/test_window_cap.py @@ -20,7 +20,6 @@ import argparse import http.server import pathlib -import shutil import socket import socketserver import struct