Skip to content

ci: add GitHub Actions test pipeline#6

Open
Frauschi wants to merge 2 commits into
mainfrom
ci
Open

ci: add GitHub Actions test pipeline#6
Frauschi wants to merge 2 commits into
mainfrom
ci

Conversation

@Frauschi

Copy link
Copy Markdown
Contributor

ci: add GitHub Actions test pipeline

Summary

Adds a full GitHub Actions CI pipeline for wolfCert, plus the supporting scripts and a couple of adjacent fixes surfaced while building it out. CI builds (or restores from cache) a named wolfSSL configuration, builds wolfCert to match, and runs the tests. The design splits work into a fast per-PR merge gate and a nightly job set that owns the heavier matrix, cache seeding, sanitizers, and interop.

Workflows

Workflow Trigger What it does
pr.yml PR + push to main/master Merge gate: canonical config on CMake (-Werror), autoconf, and ASan/UBSan, plus per-PR feature/config gating — EST-only, SCEP-only, server-off, key-alg variants (NO_RSA, ECC-only, RSA-only, no-3DES), TLS 1.3-only, ML-DSA per-level, static-mem, no-malloc, the header-only user_settings.h build, a macOS build, and the two cheapest configure-must-fail assertions.
lint.yml PR + push GPL license-header check and CMake↔autoconf parity of both the library and test source lists. No wolfSSL build — fails in seconds.
nightly.yml schedule + dispatch Re-runs the wolfSSL-variant matrix against fresh wolfSSL master, seeds the wolfSSL prefix caches so next-day PRs restore instead of build, and runs the macOS extras + full negative-config set.
sanitizers.yml schedule + dispatch ASan/UBSan over the full suite, TSan over the threaded roundtrips, valgrind over a representative subset.
interop.yml schedule + dispatch Third-party EST/SCEP interop (openssl, micromdm/scep, globalsign/est, cisco/libest, smallstep/step-ca). Best-effort: a dependency that fails to install exits 77 (neutral skip); a real regression fails.

Key design decisions

  • PR gate vs nightly split. Feature-gating and build-config variants run on every PR so #ifdef/feature-flag breakage is caught before merge, not after. Nightly keeps the same variant matrix to (a) seed the caches and (b) retest against a fresh wolfSSL master, and owns the slow work (sanitizers, interop, macOS extras, full negative-config set).
  • Caching strategy. scripts/ci/build-wolfssl.sh is the single source of truth mapping a config name → wolfSSL ./configure flags. The build-wolfssl composite action caches the install prefix, keyed on the resolved wolfSSL commit + a hash of the exact flags. Nightly owns the shared caches (save: true); PR and interop jobs are restore-only (save: false) so concurrent scheduled jobs never race to save the same key. Footprint is ~5–8 MB/entry, ~0.1 GB steady-state (worst case ~0.6 GB), well under GitHub's self-evicting 10 GB-per-repo cache.
  • No source changes for CI plumbing. SIGPIPE from a benign socket teardown race is handled by sigpipe-launcher.sh (CMake, via CMAKE_CROSSCOMPILING_EMULATOR) and a trap '' PIPE shell (autoconf) — neither touches library or test source.
  • macOS is bash 3.2. Every step that can run on macos-* stays bash-3.2-clean (no mapfile, guarded empty-array expansion).

Build-system parity (make check == ctest)

  • Registered the 6 EST integration tests Makefile.am was missing, so the autoconf make check and CMake ctest now run the identical test set.
  • Extended check-buildsystem-parity.sh to machine-enforce test-source parity in addition to library sources — a test added to one build system but not the other now fails lint.yml in seconds.

Code change (not CI): CSR-attribute enforcement

src/est/est_server.c — refactor + hardening of the est_require_csr_attributes enforcement path:

  • Collapsed csr_attrs_enforce's err_oid_cap / err_oid_len out-parameters into a single value-result size_t* (capacity in, length out), matching wolfCrypt's word32* outLen convention.
  • Hardening: the out-length is reset to 0 on every non-missing path and the caller gates on it, so no current or future error path can render an unpopulated stack buffer into the client-visible HTTP 400 body.
  • Added an end-to-end test asserting the 400 response body names the exact missing OID (1.2.840.113549.1.9.7), covering the OID copy that the client-path test can't observe.

Testing

Reproduce a CI job locally:

# Build the wolfSSL config the job uses.
scripts/ci/build-wolfssl.sh full --prefix /tmp/wolfssl

# Build wolfCert against it and run the tests (CMake).
cmake -S . -B build -DWITH_WOLFSSL=/tmp/wolfssl -DWOLFCERT_ENABLE_TESTS=ON
cmake --build build -j
ctest --test-dir build --output-on-failure

# autoconf equivalent
./autogen.sh
PKG_CONFIG_PATH=/tmp/wolfssl/lib/pkgconfig ./configure \
    --with-wolfssl=/tmp/wolfssl --enable-tests
make -j && make check
  • All 18 EST/CSR/SCEP unit + integration tests pass locally against the canonical full config.
  • A skoll code review of the diff is clean at the Medium+ confidence threshold; the findings it surfaced (interop cache-save race, the CSR-attr enforcement hardening, the missing OID-body assertion) are addressed here.

Notes

  • wolfSSL is tracked at master, unpinned (per project decision).
  • docs/CI.md documents the pipeline, the config names, and how to reproduce and debug a job locally.

Frauschi added 2 commits July 13, 2026 19:30
Add the wolfCert CI pipeline and supporting scripts.

Workflows (.github/workflows):
- pr.yml: the merge gate. Canonical config on CMake (-Werror) + autoconf +
  ASan/UBSan, plus per-PR feature/config gating -- EST-only, SCEP-only,
  server-off, key-alg variants (NO_RSA, ECC-only, RSA-only, no-3DES),
  TLS 1.3-only, ML-DSA per-level, static-mem, no-malloc, the header-only
  user_settings build, a macOS build, and the cheap configure-must-fail
  assertions.
- nightly.yml: re-runs the wolfSSL-variant matrix against fresh master, seeds
  the wolfSSL prefix caches for PR restores, and runs the macOS extras and
  full negative-config set.
- lint.yml: GPL header check and CMake<->autoconf parity of both the library
  and test source lists.
- sanitizers.yml, interop.yml: ASan/UBSan/TSan/valgrind and third-party
  EST/SCEP interop (best-effort, neutral-skip on dependency failure).

Scripts and actions (scripts/ci, .github/actions):
- build-wolfssl.sh: single source of truth mapping a config name to wolfSSL
  ./configure flags; the composite build-wolfssl action caches the install
  prefix keyed on the wolfSSL commit + flag hash. interop restores only.
- assert-configure-fails.sh, check-buildsystem-parity.sh (guards both the
  library- and test-source lists), sigpipe-launcher.sh.

Build-system parity:
- Register the EST integration tests Makefile.am was missing so `make check`
  and ctest run the same set.

docs/CI.md documents the pipeline and how to reproduce a job locally.
The CI matrix exercises wolfCert against reduced wolfSSL builds (NO_RSA,
NO_ECC, per-level ML-DSA, EST/SCEP/server subsets, static memory, NO_MALLOC).
Make the library and tests portable across all of them, and land the
CSR-attribute enforcement hardening alongside.

Tests:
- tls_test_util.h: mint self-signed identities with whatever signature
  algorithm the build provides (RSA, else ECC P-256); collapse the six
  duplicated identity/CA generators into shared helpers (mint_self_id,
  gen_server_identity).
- Gate key types on WOLFCERT_HAVE_RSA/ECC and each ML-DSA level on
  WOLFSSL_NO_ML_DSA_{44,65,87}; add a TEST_ENROLL_KEY_TYPE default for
  enrollments where the key algorithm is incidental.
- test_static_mem.h: load a wolfSSL static-memory pool and register it as the
  global heap (before wolfcert_init) so the NO_MALLOC unit tests can allocate.

Library:
- cli/wolfcert_server.c: guard the EST-only --csrattrs-file validation with
  WOLFCERT_HAVE_EST so a SCEP-only build links.
- Move wolfcert_oid_to_dotted from est_server.c to csr_attrs.c so it is
  available in EST builds without the server.
- internal.h: include <sys/types.h> for pid_t under HAVE_GETPID (static
  memory builds reference it in wolfssl/wolfcrypt/random.h).
- csr_attrs_enforce: collapse the OID out-parameters into a single
  value-result size_t*, reset it to 0 on every non-missing path, and gate the
  caller so no error path can render an unpopulated stack buffer into the
  HTTP 400 body; add an end-to-end test asserting the reported OID.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants