diff --git a/.github/actions/apt-retry/action.yml b/.github/actions/apt-retry/action.yml new file mode 100644 index 00000000..a12ff60f --- /dev/null +++ b/.github/actions/apt-retry/action.yml @@ -0,0 +1,53 @@ +name: apt install with retry +description: >- + Install apt packages with bounded retries and an Acquire timeout, so a slow + or flaky mirror retries instead of cancelling the job. Used by the jobs that + cannot move into the ghcr CI image (mixed-OS matrices, version-specific + compilers, large niche package sets). Jobs whose deps are baked into + ghcr.io/wolfssl/wolftpm-ci use `container:` instead of this action. + +inputs: + packages: + description: Space-separated list of apt packages to install. + required: true + retries: + description: Number of attempts before failing. + required: false + default: "3" + retry-delay: + description: Initial delay between attempts (seconds, grows each attempt). + required: false + default: "10" + sudo: + description: Prefix apt with sudo (set "false" when already root). + required: false + default: "true" + +runs: + using: composite + steps: + - name: Install ${{ inputs.packages }} + shell: bash + run: | + set -u + export DEBIAN_FRONTEND=noninteractive + SUDO="" + if [ "${{ inputs.sudo }}" = "true" ] && command -v sudo >/dev/null 2>&1; then + SUDO="sudo" + fi + RETRIES="${{ inputs.retries }}" + DELAY="${{ inputs.retry-delay }}" + TIMEOUT="-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30" + for i in $(seq 1 "$RETRIES"); do + if $SUDO apt-get $TIMEOUT update -q && \ + $SUDO apt-get install -y ${{ inputs.packages }}; then + exit 0 + fi + if [ "$i" -eq "$RETRIES" ]; then + echo "::error::apt-get failed after $RETRIES attempts" + exit 1 + fi + echo "::warning::apt-get failed (attempt $i/$RETRIES), retrying in ${DELAY}s..." + sleep "$DELAY" + DELAY=$((DELAY * 2)) + done diff --git a/.github/actions/setup-ibmswtpm/action.yml b/.github/actions/setup-ibmswtpm/action.yml new file mode 100644 index 00000000..72c3ea80 --- /dev/null +++ b/.github/actions/setup-ibmswtpm/action.yml @@ -0,0 +1,94 @@ +name: Setup ibmswtpm2 +description: >- + Build (or restore from cache) the IBM SW TPM 2.0 simulator for wolfTPM CI. + Resolves the requested ref to a commit so the cache key is stable per + upstream commit, restores the previously built ./ibmswtpm2 tree on a hit, and + otherwise does a shallow clone + parallel build. The built simulator is left + at ./ibmswtpm2/src/tpm_server so callers can launch it (port/lifecycle stay + in the workflow, since they differ per job). + +inputs: + ref: + description: ibmswtpm2 git ref (branch, tag, or commit) to build. + required: false + default: master + max-loaded-objects: + description: >- + When set, patch TpmProfile_Misc.h MAX_LOADED_OBJECTS to this value before + building (tpm2-tools ESYS needs extra transient object slots). Empty keeps + the upstream default. Folded into the cache key so variants do not collide. + required: false + default: "" + parallel: + description: Pass -j$(nproc) to make when "true". + required: false + default: "true" + +outputs: + server-path: + description: Path to the built tpm_server binary. + value: ibmswtpm2/src/tpm_server + +runs: + using: composite + steps: + - name: Resolve ibmswtpm2 ${{ inputs.ref }} and compute cache key + id: key + shell: bash + run: | + set -eo pipefail + # Resolve the ref to a commit so the cache key tracks the upstream tree, + # not a moving branch name. ls-remote returns nothing for a raw SHA, in + # which case the ref already is the commit. + URL=https://github.com/kgoldman/ibmswtpm2.git + SHA=$(git ls-remote "$URL" "${{ inputs.ref }}^{}" | awk 'NR==1{print $1}') + if [ -z "$SHA" ]; then + SHA=$(git ls-remote "$URL" "${{ inputs.ref }}" | awk 'NR==1{print $1}') + fi + if [ -z "$SHA" ]; then + SHA="${{ inputs.ref }}" + fi + # The MAX_LOADED_OBJECTS patch produces a different binary, so fold it + # into the key as a variant suffix (empty -> "default"). + VARIANT="${{ inputs.max-loaded-objects }}" + if [ -z "$VARIANT" ]; then + VARIANT="default" + else + VARIANT="mlo${VARIANT}" + fi + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + echo "cache-key=ibmswtpm2-${{ runner.os }}-${SHA}-${VARIANT}-v1" >> "$GITHUB_OUTPUT" + echo "ibmswtpm2 ${{ inputs.ref }} -> $SHA (variant $VARIANT)" + + - name: Restore ibmswtpm2 build cache + id: cache + uses: actions/cache@v4 + with: + path: ibmswtpm2 + key: ${{ steps.key.outputs.cache-key }} + + - name: Build ibmswtpm2 (cache miss) + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: | + set -eo pipefail + rm -rf ibmswtpm2 + # Fetch the exact resolved commit so the built tree matches the cache + # key (GitHub allows fetching any reachable commit). + git init -q ibmswtpm2 + cd ibmswtpm2 + git remote add origin https://github.com/kgoldman/ibmswtpm2.git + git fetch --depth 1 origin "${{ steps.key.outputs.sha }}" + git checkout -q --detach FETCH_HEAD + cd src + if [ -n "${{ inputs.max-loaded-objects }}" ]; then + # Increase transient object slots: tpm2-tools ESYS creates transient + # salt keys for HMAC sessions which consume extra slots. + sed -i 's/#define MAX_LOADED_OBJECTS.*/#define MAX_LOADED_OBJECTS ${{ inputs.max-loaded-objects }}/' \ + TpmProfile_Misc.h + fi + if [ "${{ inputs.parallel }}" = "true" ]; then + make -j"$(nproc 2>/dev/null || echo 2)" + else + make + fi diff --git a/.github/actions/setup-wolfcose/action.yml b/.github/actions/setup-wolfcose/action.yml new file mode 100644 index 00000000..ca7f98e3 --- /dev/null +++ b/.github/actions/setup-wolfcose/action.yml @@ -0,0 +1,101 @@ +name: Setup wolfCOSE +description: >- + Build (or restore from cache) wolfCOSE for wolfTPM CI. Resolves the requested + ref to a commit so the cache key is stable per upstream commit, restores the + previously built ./wolfcose tree on a hit, and otherwise does a shallow clone + + parallel build. wolfCOSE links against an already-installed wolfSSL (its + sole crypto dependency), so pass the wolfSSL commit it builds against via + wolfssl-sha to keep the cache keyed to that pairing. Run setup-wolfssl first. + +inputs: + ref: + description: wolfCOSE git ref (branch, tag, or commit) to build. + required: false + default: master + wolfssl-sha: + description: >- + The wolfSSL commit wolfCOSE is built against (from setup-wolfssl). Folded + into the cache key so a wolfSSL bump invalidates the wolfCOSE build. + required: false + default: "" + extra-cflags: + description: Extra CFLAGS passed to wolfCOSE make as EXTRA_CFLAGS. + required: false + default: "" + make-target: + description: make target to build (empty builds the default static lib). + required: false + default: "" + cc: + description: C compiler to build wolfCOSE with. + required: false + default: gcc + parallel: + description: Pass -j$(nproc) to make when "true". + required: false + default: "true" + +outputs: + path: + description: Path to the built wolfCOSE source tree. + value: wolfcose + +runs: + using: composite + steps: + - name: Resolve wolfCOSE ${{ inputs.ref }} and compute cache key + id: key + shell: bash + run: | + set -eo pipefail + # Resolve the ref to a commit so the cache key tracks the upstream tree, + # not a moving branch name. ls-remote returns nothing for a raw SHA. + URL=https://github.com/wolfSSL/wolfCOSE.git + SHA=$(git ls-remote "$URL" "${{ inputs.ref }}^{}" | awk 'NR==1{print $1}') + if [ -z "$SHA" ]; then + SHA=$(git ls-remote "$URL" "${{ inputs.ref }}" | awk 'NR==1{print $1}') + fi + if [ -z "$SHA" ]; then + SHA="${{ inputs.ref }}" + fi + # Fold the wolfSSL SHA and build-affecting inputs into the key so the + # wolfCOSE cache is invalidated when any of them change. + HASH_CMD="sha256sum" + command -v sha256sum >/dev/null 2>&1 || HASH_CMD="shasum -a 256" + CFGHASH=$(printf '%s\037%s\037%s\037%s' \ + "${{ inputs.wolfssl-sha }}" \ + "${{ inputs.extra-cflags }}" \ + "${{ inputs.make-target }}" \ + "${{ inputs.cc }}" | $HASH_CMD | cut -c1-16) + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + echo "cache-key=wolfcose-${{ runner.os }}-${SHA}-${CFGHASH}-v1" >> "$GITHUB_OUTPUT" + echo "wolfCOSE ${{ inputs.ref }} -> $SHA (cfg $CFGHASH)" + + - name: Restore wolfCOSE build cache + id: cache + uses: actions/cache@v4 + with: + path: wolfcose + key: ${{ steps.key.outputs.cache-key }} + + - name: Build wolfCOSE (cache miss) + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: | + set -eo pipefail + rm -rf wolfcose + # Fetch the exact resolved commit so the built tree matches the key. + git init -q wolfcose + cd wolfcose + git remote add origin https://github.com/wolfSSL/wolfCOSE.git + git fetch --depth 1 origin "${{ steps.key.outputs.sha }}" + git checkout -q --detach FETCH_HEAD + ARGS="" + if [ -n "${{ inputs.extra-cflags }}" ]; then + ARGS="$ARGS EXTRA_CFLAGS=\"${{ inputs.extra-cflags }}\"" + fi + JOBS="" + if [ "${{ inputs.parallel }}" = "true" ]; then + JOBS="-j$(nproc 2>/dev/null || echo 2)" + fi + eval CC="${{ inputs.cc }}" make $JOBS ${{ inputs.make-target }} $ARGS diff --git a/.github/docker/wolftpm-ci/Dockerfile b/.github/docker/wolftpm-ci/Dockerfile new file mode 100644 index 00000000..0843b758 --- /dev/null +++ b/.github/docker/wolftpm-ci/Dockerfile @@ -0,0 +1,37 @@ +# wolfTPM CI base image. +# +# Bakes the apt dependencies that wolfTPM CI jobs install on every run so +# the apt mirror is never contacted on the happy path (the source of the +# intermittent `apt-get update` timeouts). Mirrors the wolfBoot CI image +# model (ghcr.io/wolfssl/wolfboot-ci-*): jobs run inside this image via +# `container: image: ghcr.io/wolfssl/wolftpm-ci:` instead of calling +# `sudo apt-get install`. +# +# Built and pushed by .github/workflows/publish-ci-image.yml. Pin consumers +# to a version tag (e.g. :v1.0), never :latest, and bump the tag when this +# file changes. +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install --no-install-recommends -y \ + autoconf \ + automake \ + build-essential \ + ca-certificates \ + clang \ + clang-tools \ + cmake \ + git \ + iproute2 \ + libclang-rt-18-dev \ + libssl-dev \ + libtool \ + libtss2-tcti-mssim0 \ + openssl \ + pkg-config \ + sudo \ + tpm2-tools \ + valgrind \ + zip \ + && rm -rf /var/lib/apt/lists/* diff --git a/.github/workflows/cmake-build.yml b/.github/workflows/cmake-build.yml index fdc54cf6..4223ab71 100644 --- a/.github/workflows/cmake-build.yml +++ b/.github/workflows/cmake-build.yml @@ -139,12 +139,13 @@ jobs: #pull wolfTPM - uses: actions/checkout@master -# Install cmake +# Install cmake (matrix includes a Windows leg, so this job stays on the host +# runner; apt is made resilient instead of moving into the Linux CI image) - name: Install cmake if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y cmake + uses: ./.github/actions/apt-retry + with: + packages: cmake #pull and build wolfssl # Every matrix entry builds the identical wolfSSL (only the wolfTPM options @@ -220,14 +221,15 @@ jobs: name: CMake static export check if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + # cmake baked into the CI image — no apt mirror on the happy path. + container: + image: ghcr.io/wolfssl/wolftpm-ci:v1.0 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@master - - name: Install cmake - run: | - sudo apt-get update - sudo apt-get install -y cmake - # Same wolfSSL config as the matrix build above, so it shares that cache # entry (key includes only OS + commit, not the wolfTPM options). - name: Resolve wolfSSL commit diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index dd9bef4d..6e91c787 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,10 +28,12 @@ jobs: - name: Checkout wolfTPM uses: actions/checkout@v4 + # CodeQL's action is fiddly inside a custom container, so keep this job + # on the host runner and just make apt resilient to mirror timeouts. - name: Install build tools - run: | - sudo apt-get update - sudo apt-get install -y autoconf automake libtool + uses: ./.github/actions/apt-retry + with: + packages: autoconf automake libtool # Full-feature wolfSSL so every wolfTPM code path is compiled and # therefore visible to CodeQL. diff --git a/.github/workflows/coverity-scan-fixes.yml b/.github/workflows/coverity-scan-fixes.yml index 801a0621..c37ac43b 100644 --- a/.github/workflows/coverity-scan-fixes.yml +++ b/.github/workflows/coverity-scan-fixes.yml @@ -15,17 +15,35 @@ jobs: with: ref: master + # Cache the built wolfSSL tree keyed on the resolved upstream commit so it + # is rebuilt only when wolfSSL master moves, not on every run. + - name: Resolve wolfSSL commit + id: wolfssl_rev + run: echo "sha=$(git ls-remote https://github.com/wolfSSL/wolfssl.git master | awk 'NR==1{print $1}')" >> "$GITHUB_OUTPUT" + + - name: Cache wolfSSL build + id: wolfssl_cache + uses: actions/cache@v4 + with: + path: wolfssl + key: wolfssl-coverity-fixes-${{ runner.os }}-${{ steps.wolfssl_rev.outputs.sha }}-v1 + - uses: actions/checkout@master + if: steps.wolfssl_cache.outputs.cache-hit != 'true' with: repository: wolfssl/wolfssl + ref: ${{ steps.wolfssl_rev.outputs.sha }} path: wolfssl - name: wolfssl autogen + if: steps.wolfssl_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure + if: steps.wolfssl_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl run: ./configure --enable-wolftpm --enable-pkcallbacks --enable-keygen CFLAGS="-DWC_RSA_NO_PADDING" - name: wolfssl make + if: steps.wolfssl_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl run: make - name: wolfssl make install diff --git a/.github/workflows/fwtpm-test.yml b/.github/workflows/fwtpm-test.yml index d0ee848e..82df1974 100644 --- a/.github/workflows/fwtpm-test.yml +++ b/.github/workflows/fwtpm-test.yml @@ -210,11 +210,13 @@ jobs: - name: Checkout wolfTPM uses: actions/checkout@v4 + # Matrix includes a macOS leg, so this job stays on the host runner; + # apt is made resilient rather than moving into the Linux CI image. - name: Install tpm2-tools if: ${{ !matrix.build_only && runner.os != 'macOS' }} - run: | - sudo apt-get update - sudo apt-get install -y tpm2-tools libtss2-tcti-mssim0 + uses: ./.github/actions/apt-retry + with: + packages: tpm2-tools libtss2-tcti-mssim0 - name: macOS build deps (skip tpm2-tools) if: ${{ !matrix.build_only && runner.os == 'macOS' }} @@ -310,40 +312,37 @@ jobs: ibmswtpm-tpm2tools: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + # Host runner: tpm2-tools activatecredential hits a tpm2-tools OpenSSL + # credential-integrity quirk inside a container, so stay on the host + # (where it passes) and just make apt resilient to mirror timeouts. steps: - uses: actions/checkout@master + - name: Install tpm2-tools + uses: ./.github/actions/apt-retry + with: + packages: tpm2-tools libtss2-tcti-mssim0 + - name: Build IBM SW TPM - run: | - git clone --depth=1 https://github.com/kgoldman/ibmswtpm2.git - cd ibmswtpm2/src + uses: ./.github/actions/setup-ibmswtpm + with: # Increase transient object slots — tpm2-tools ESYS creates # transient salt keys for HMAC sessions which consume extra slots - sed -i 's/#define MAX_LOADED_OBJECTS.*/#define MAX_LOADED_OBJECTS 7/' \ - TpmProfile_Misc.h - make -j$(nproc) - - - name: Install tpm2-tools - run: | - sudo apt-get update - sudo apt-get install -y tpm2-tools libtss2-tcti-mssim0 + max-loaded-objects: 7 - - name: Start IBM SW TPM + # Start the simulator and run the tests in one step: GitHub reaps a + # step's background processes between steps in container jobs, so a + # server started in a separate step would be gone by the test step. + - name: Run tpm2-tools tests run: | ibmswtpm2/src/tpm_server & - echo $! > /tmp/tpm_server.pid + TPM_PID=$! sleep 1 - kill -0 $(cat /tmp/tpm_server.pid) - - - name: Run tpm2-tools tests - run: scripts/tpm2_tools_test.sh --no-start - - - name: Stop IBM SW TPM - if: always() - run: | - if [ -f /tmp/tpm_server.pid ]; then - kill $(cat /tmp/tpm_server.pid) 2>/dev/null || true - fi + kill -0 "$TPM_PID" + scripts/tpm2_tools_test.sh --no-start + rc=$? + kill "$TPM_PID" 2>/dev/null || true + exit $rc # ---------------------------------------------------------------- # fwTPM STM32 emulator test (m33mu Cortex-M33 simulator) @@ -369,10 +368,13 @@ jobs: ref: fwtpm_stm32h5 path: wolftpm-examples + # Runs inside the m33mu CI container (already root, no sudo); just make + # apt resilient to mirror timeouts. - name: Install build dependencies - run: | - apt-get update -qq - apt-get install -y -qq autoconf automake libtool + uses: ./.github/actions/apt-retry + with: + packages: autoconf automake libtool + sudo: "false" - name: Install STM32Cube H5 SDK run: | @@ -427,6 +429,14 @@ jobs: fwtpm-valgrind: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + # Deps baked into the CI image — no apt mirror on the happy path. The added + # caps let the fwtpm tests create a network namespace (unshare --net). + container: + image: ghcr.io/wolfssl/wolftpm-ci:v1.0 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --cap-add=SYS_ADMIN --cap-add=NET_ADMIN --cap-add=NET_RAW timeout-minutes: 30 strategy: fail-fast: false @@ -443,11 +453,6 @@ jobs: - name: Checkout wolfTPM uses: actions/checkout@v4 - - name: Install valgrind + tpm2-tools - run: | - sudo apt-get update - sudo apt-get install -y valgrind tpm2-tools libtss2-tcti-mssim0 - - name: Setup wolfSSL uses: ./.github/actions/setup-wolfssl with: diff --git a/.github/workflows/make-test-swtpm.yml b/.github/workflows/make-test-swtpm.yml index 2edb13da..f790c8df 100644 --- a/.github/workflows/make-test-swtpm.yml +++ b/.github/workflows/make-test-swtpm.yml @@ -372,36 +372,45 @@ jobs: configure-flags: ${{ matrix.wolfssl_config || '--enable-wolftpm --enable-pkcallbacks' }} cflags: ${{ matrix.wolfssl_cflags }} - # For old-wolfssl test: checkout and build old wolfSSL for linking - - name: Checkout old wolfSSL + # For old-wolfssl test: checkout and build old wolfSSL for linking. + # Pinned tag + fixed patch, so the cache is keyed on both and the build + # is restored on virtually every run. + - name: Cache old wolfSSL build if: matrix.name == 'old-wolfssl' + id: wolfssl_old_cache + uses: actions/cache@v4 + with: + path: wolfssl-old + key: wolfssl-old-${{ runner.os }}-v4.7.0-stable-${{ hashFiles('.github/workflows/wolfssl-v4.7.0.patch') }}-v1 + - name: Checkout old wolfSSL + if: matrix.name == 'old-wolfssl' && steps.wolfssl_old_cache.outputs.cache-hit != 'true' uses: actions/checkout@master with: repository: wolfssl/wolfssl path: wolfssl-old ref: v4.7.0-stable - name: Apply wolfssl-v4.7.0.patch to old wolfSSL - if: matrix.name == 'old-wolfssl' + if: matrix.name == 'old-wolfssl' && steps.wolfssl_old_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl-old run: | git apply ../.github/workflows/wolfssl-v4.7.0.patch - - name: Setup old wolfSSL for linking - if: matrix.name == 'old-wolfssl' + - name: Build old wolfSSL for linking + if: matrix.name == 'old-wolfssl' && steps.wolfssl_old_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl-old run: | ./autogen.sh ./configure --enable-wolftpm --disable-examples CFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_TEST_CERT -DWOLFSSL_KEY_GEN -DNO_WRITEV" - make + - name: Install old wolfSSL + if: matrix.name == 'old-wolfssl' + working-directory: ./wolfssl-old + run: | sudo make install sudo ldconfig - name: Setup ibmswtpm2 if: matrix.needs_swtpm == true || matrix.needs_swtpm == null - uses: actions/checkout@master - with: - repository: kgoldman/ibmswtpm2 - path: ibmswtpm2 + uses: ./.github/actions/setup-ibmswtpm - name: Generate TPM port if: matrix.needs_swtpm == true || matrix.needs_swtpm == null @@ -417,14 +426,16 @@ jobs: if: matrix.needs_swtpm == true || matrix.needs_swtpm == null working-directory: ./ibmswtpm2/src run: | - make -j"$(nproc)" echo "Starting TPM simulator on port $TPM_PORT" ./tpm_server -port $TPM_PORT & + # Mono is a large, niche package set not baked into the CI image; just + # make apt resilient to mirror timeouts. - name: Install mono if: matrix.needs_mono == true - run: | - sudo apt-get install -y mono-mcs mono-tools-devel nunit nunit-console + uses: ./.github/actions/apt-retry + with: + packages: mono-mcs mono-tools-devel nunit nunit-console - name: Build wolfTPM run: | diff --git a/.github/workflows/multi-compiler.yml b/.github/workflows/multi-compiler.yml index d25915fe..0d091170 100644 --- a/.github/workflows/multi-compiler.yml +++ b/.github/workflows/multi-compiler.yml @@ -20,18 +20,38 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: + # Cache the built wolfSSL tree keyed on the resolved upstream commit so it + # is rebuilt only when wolfSSL master moves, not on every CI run. + - name: Resolve wolfSSL commit + id: wolfssl_rev + run: echo "sha=$(git ls-remote https://github.com/wolfSSL/wolfssl.git master | awk 'NR==1{print $1}')" >> "$GITHUB_OUTPUT" + + - name: Cache wolfSSL build + id: wolfssl_cache + uses: actions/cache@v4 + with: + path: wolfssl + key: wolfssl-multicompiler-${{ runner.os }}-${{ steps.wolfssl_rev.outputs.sha }}-v1 + - name: Checkout wolfSSL + if: steps.wolfssl_cache.outputs.cache-hit != 'true' uses: actions/checkout@v4 with: repository: wolfssl/wolfssl path: wolfssl + ref: ${{ steps.wolfssl_rev.outputs.sha }} - name: Build wolfSSL + if: steps.wolfssl_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl run: | ./autogen.sh ./configure --enable-wolftpm --enable-pkcallbacks make -j$(nproc) + + - name: Install wolfSSL + working-directory: ./wolfssl + run: | sudo make install sudo ldconfig @@ -49,7 +69,9 @@ jobs: name: ${{ matrix.cc }} if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest - timeout-minutes: 5 + # Headroom for an occasionally-slow apt mirror installing a version-specific + # compiler (apt-retry handles real failures; this avoids a hard cancel). + timeout-minutes: 12 needs: build_wolfssl strategy: fail-fast: false @@ -69,14 +91,17 @@ jobs: cxx: clang++-17 steps: - - name: Install compiler - run: | - sudo apt-get update - sudo apt-get install -y ${{ matrix.cc }} - + # Checkout first so the local apt-retry composite action is on disk. - name: Checkout wolfTPM uses: actions/checkout@v4 + # Version-specific compilers are not baked into the CI image; keep this + # job on the host runner and just make apt resilient to mirror timeouts. + - name: Install compiler + uses: ./.github/actions/apt-retry + with: + packages: ${{ matrix.cc }} + - name: Download wolfSSL uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/pqc-examples.yml b/.github/workflows/pqc-examples.yml index e8af760e..789b7cd5 100644 --- a/.github/workflows/pqc-examples.yml +++ b/.github/workflows/pqc-examples.yml @@ -17,16 +17,19 @@ jobs: pqc-examples: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + # Host runner: tpm2-tools make-check (activatecredential) hits a tpm2-tools + # OpenSSL credential-integrity quirk inside a container, so stay on the host + # (where it passes) and just make apt resilient to mirror timeouts. timeout-minutes: 30 steps: - name: Checkout wolfTPM uses: actions/checkout@v4 - - name: Install build deps + tpm2-tools - run: | - sudo apt-get update - sudo apt-get install -y tpm2-tools libtss2-tcti-mssim0 + - name: Install tpm2-tools + uses: ./.github/actions/apt-retry + with: + packages: tpm2-tools libtss2-tcti-mssim0 - name: Setup wolfSSL with PQC uses: ./.github/actions/setup-wolfssl diff --git a/.github/workflows/publish-ci-image.yml b/.github/workflows/publish-ci-image.yml new file mode 100644 index 00000000..83487ec1 --- /dev/null +++ b/.github/workflows/publish-ci-image.yml @@ -0,0 +1,76 @@ +name: Publish wolfTPM CI image + +# Builds .github/docker/wolftpm-ci/Dockerfile and pushes it to +# ghcr.io//wolftpm-ci:. CI jobs consume this image via +# `container: image: ghcr.io/wolfssl/wolftpm-ci:v1.0` +# so they no longer run `sudo apt-get install` on every run (the source of +# mirror-timeout flakes). +# +# Mirrors the wolfBoot CI image model: pin a version tag and bump it when +# the Dockerfile changes (do not rely on :latest). The image namespace follows +# the repository owner, so a fork publishes to ghcr.io//wolftpm-ci +# and the consumer workflows pull from that same owner automatically. +# +# ONE-TIME MANUAL SETUP after the first successful push: make the +# `wolftpm-ci` package PUBLIC (Profile/Org > Packages > wolftpm-ci > package +# settings > change visibility) so fork PRs can pull it anonymously. Until +# then, only same-owner runs can pull it and forks fall back to apt. +# +# Triggers: +# - push to master/main when the Dockerfile or this workflow changes +# - workflow_dispatch for manual rebuilds + +on: + push: + branches: [ 'master', 'main' ] + paths: + - '.github/docker/wolftpm-ci/**' + - '.github/workflows/publish-ci-image.yml' + workflow_dispatch: {} + +concurrency: + # Single global group: every run publishes to the shared version tag, so + # serialize to avoid two runs racing to overwrite it. + group: publish-ci-image + cancel-in-progress: false + +permissions: + contents: read + packages: write + +jobs: + publish: + # Runs for any owner: the job only ever pushes to its own ghcr namespace + # (GITHUB_TOKEN has packages:write to the owner running it), so forks can + # publish their own wolftpm-ci image for testing. + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Compute lowercase owner + id: owner + run: echo "lc=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: .github/docker/wolftpm-ci + file: .github/docker/wolftpm-ci/Dockerfile + push: true + # ghcr image paths must be lowercase (the wolfSSL org has a capital S). + tags: ghcr.io/${{ steps.owner.outputs.lc }}/wolftpm-ci:v1.0 + cache-from: type=registry,ref=ghcr.io/${{ steps.owner.outputs.lc }}/wolftpm-ci:v1.0 + cache-to: type=inline diff --git a/.github/workflows/release-checks.yml b/.github/workflows/release-checks.yml index 221647e3..ea4e20af 100644 --- a/.github/workflows/release-checks.yml +++ b/.github/workflows/release-checks.yml @@ -26,14 +26,29 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + # Cache the wolfSSL install prefix keyed on the resolved upstream commit + # so wolfSSL is rebuilt only when master moves, not on every CI run. + - name: Resolve wolfSSL commit + id: wolfssl_rev + run: echo "sha=$(git ls-remote https://github.com/wolfSSL/wolfssl.git master | awk 'NR==1{print $1}')" >> "$GITHUB_OUTPUT" + + - name: Cache wolfSSL install + id: wolfssl_cache + uses: actions/cache@v4 + with: + path: /tmp/wolfssl-install + key: wolfssl-release-checks-${{ runner.os }}-${{ steps.wolfssl_rev.outputs.sha }}-v1 + - name: Checkout wolfSSL + if: steps.wolfssl_cache.outputs.cache-hit != 'true' uses: actions/checkout@v4 with: repository: wolfssl/wolfssl path: wolfssl - ref: master + ref: ${{ steps.wolfssl_rev.outputs.sha }} - name: Build wolfSSL + if: steps.wolfssl_cache.outputs.cache-hit != 'true' working-directory: ./wolfssl run: | ./autogen.sh @@ -95,14 +110,15 @@ jobs: name: scan-build (clang static analysis) if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + # clang-tools (scan-build) baked into the CI image — no apt mirror. + container: + image: ghcr.io/wolfssl/wolftpm-ci:v1.0 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 needs: build_wolfssl steps: - - name: Install clang tools - run: | - sudo apt-get update - sudo apt-get install -y clang-tools - - name: Checkout wolfTPM uses: actions/checkout@v4 diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index 58d6a8d9..262e0968 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -26,6 +26,9 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false needs: discover runs-on: ubuntu-latest + # Host runner: tpm2-tools make-check (activatecredential) hits a tpm2-tools + # OpenSSL credential-integrity quirk inside a container, so stay on the host + # (where it passes) and just make apt resilient to mirror timeouts. timeout-minutes: 30 strategy: fail-fast: false @@ -102,9 +105,9 @@ jobs: uses: actions/checkout@v4 - name: Install tpm2-tools - run: | - sudo apt-get update - sudo apt-get install -y tpm2-tools libtss2-tcti-mssim0 + uses: ./.github/actions/apt-retry + with: + packages: tpm2-tools libtss2-tcti-mssim0 - name: Setup wolfSSL with ${{ matrix.name }} uses: ./.github/actions/setup-wolfssl diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index 9b383e90..67ca27bb 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -63,16 +63,12 @@ jobs: - name: Setup ibmswtpm2 simulator if: matrix.config.needs_swtpm - uses: actions/checkout@v4 - with: - repository: kgoldman/ibmswtpm2 - path: ibmswtpm2 + uses: ./.github/actions/setup-ibmswtpm - name: Start TPM simulator if: matrix.config.needs_swtpm working-directory: ./ibmswtpm2/src run: | - make -j"$(nproc)" ./tpm_server & - name: Build wolfTPM (${{ matrix.config.name }}) diff --git a/.github/workflows/wolfssl-versions-pqc.yml b/.github/workflows/wolfssl-versions-pqc.yml index 8e69bf68..adb51986 100644 --- a/.github/workflows/wolfssl-versions-pqc.yml +++ b/.github/workflows/wolfssl-versions-pqc.yml @@ -59,6 +59,12 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false needs: discover-versions runs-on: ubuntu-latest + # Build tools baked into the CI image — no apt mirror on the happy path. + container: + image: ghcr.io/wolfssl/wolftpm-ci:v1.0 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 25 strategy: fail-fast: false @@ -68,11 +74,6 @@ jobs: - name: Checkout wolfTPM uses: actions/checkout@v4 - - name: Install build deps - run: | - sudo apt-get update - sudo apt-get install -y autoconf automake libtool - - name: Cache wolfSSL ${{ matrix.wolfssl-version }} if: matrix.wolfssl-version != 'master' id: cache-wolfssl diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index caca23c3..02562ace 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -26,23 +26,27 @@ jobs: # This should be a safe limit for the tests to run. timeout-minutes: 25 steps: + # Checkout first so the local apt-retry composite action is on disk. + - name: Checkout wolfTPM + uses: actions/checkout@v4 + + # Large, version-pinned zephyr package set — not baked into the wolfTPM + # CI image; just make apt resilient to mirror timeouts. - name: Install dependencies - run: | - # Don't prompt for anything - export DEBIAN_FRONTEND=noninteractive - sudo apt-get update - # most of the ci-base zephyr docker image packages - sudo apt-get install -y zip bridge-utils uml-utilities \ - git cmake ninja-build gperf ccache dfu-util device-tree-compiler wget \ - python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \ - make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 \ - autoconf automake bison build-essential ca-certificates cargo ccache chrpath cmake \ - cpio device-tree-compiler dfu-util diffstat dos2unix doxygen file flex g++ gawk gcc \ - gcovr git git-core gnupg gperf gtk-sharp2 help2man iproute2 lcov libcairo2-dev \ - libglib2.0-dev libgtk2.0-0 liblocale-gettext-perl libncurses5-dev libpcap-dev \ - libpopt0 libsdl1.2-dev libsdl2-dev libssl-dev libtool libtool-bin locales make \ - net-tools openssh-client parallel pkg-config python3-dev python3-pip \ - python3-ply python3-setuptools python-is-python3 qemu-kvm rsync socat srecord sudo \ + uses: ./.github/actions/apt-retry + with: + packages: >- + zip bridge-utils uml-utilities + git cmake ninja-build gperf ccache dfu-util device-tree-compiler wget + python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file + make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 + autoconf automake bison build-essential ca-certificates cargo ccache chrpath cmake + cpio device-tree-compiler dfu-util diffstat dos2unix doxygen file flex g++ gawk gcc + gcovr git git-core gnupg gperf gtk-sharp2 help2man iproute2 lcov libcairo2-dev + libglib2.0-dev libgtk2.0-0 liblocale-gettext-perl libncurses5-dev libpcap-dev + libpopt0 libsdl1.2-dev libsdl2-dev libssl-dev libtool libtool-bin locales make + net-tools openssh-client parallel pkg-config python3-dev python3-pip + python3-ply python3-setuptools python-is-python3 qemu-kvm rsync socat srecord sudo texinfo unzip wget ovmf xz-utils - name: Install west