mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
ci: exercise the presence probe against musl and an address-less interface
Two legs, both about the assumption the presence probe rests on. OpenWrt — the platform dynamic interface binding exists for — is musl, and musl reimplements getifaddrs independently of glibc. `interface_present` reads ifa_flags out of it, and the interfaces this feature exists for (fips-mesh0, fips-ap0) are unbridged with no IP address at all, which is exactly where getifaddrs implementations differ. Every other leg is glibc, so without this one the probe was asserted on a libc no test had ever run it against, on the target it was written for. Building for the musl target rather than inside an Alpine container keeps the leg to a cross-build plus a run, without a second toolchain image to maintain. The address-less interface is created on both this leg and the glibc one. Pinning the contract on both libcs is what turns "glibc and musl agree about getifaddrs" from an assumption into a checked fact, and makes the glibc leg fail first if glibc is the one that changes. Loopback cannot stand in: it has 127.0.0.1, so probing it asks whether getifaddrs works rather than whether it reports this. Deliberately not tolerant of failure — a guard that quietly does not run is worse than no guard. Adds the iface-binding suite to the integration matrix, mirroring testing/ci-local.sh.
This commit is contained in:
@@ -283,6 +283,27 @@ jobs:
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev
|
||||
|
||||
# The same address-less interface the musl leg creates. Pinning the
|
||||
# contract on both libcs is what turns "glibc and musl agree about
|
||||
# `getifaddrs`" from an assumption into a checked fact — and makes this
|
||||
# leg fail first if glibc is the one that changes.
|
||||
- name: Create an address-less interface for the presence probe
|
||||
run: |
|
||||
sudo ip link add fips-probe0 type dummy
|
||||
# `addrgenmode none` before bringing it up: the kernel hands an IPv6
|
||||
# link-local to any interface that comes up, and an interface with a
|
||||
# link-local is not address-less — the fixture would have quietly
|
||||
# tested nothing.
|
||||
sudo ip link set fips-probe0 addrgenmode none
|
||||
sudo ip link set fips-probe0 up
|
||||
ip addr show fips-probe0
|
||||
# Fail rather than test the wrong thing if it acquired one anyway.
|
||||
if ip addr show fips-probe0 | grep -qE "inet6? "; then
|
||||
echo "fips-probe0 has an address; it cannot test the address-less case" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "FIPS_TEST_ADDRLESS_IFACE=fips-probe0" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1
|
||||
with:
|
||||
@@ -365,6 +386,85 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: cargo nextest run --all --profile ci
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Job 2bb – Unit tests (musl)
|
||||
#
|
||||
# OpenWrt — the platform the Ethernet transport's dynamic interface binding
|
||||
# exists for — is musl, and musl reimplements the libc calls that binding is
|
||||
# built on rather than sharing glibc's. `interface_present` reads `ifa_flags`
|
||||
# out of `getifaddrs`, and the interfaces it has to see (`fips-mesh0`,
|
||||
# `fips-ap0`) are deliberately unbridged with no IP address at all, which is
|
||||
# exactly where getifaddrs implementations differ. Every other leg is glibc, so
|
||||
# without this one the presence probe is asserted on a libc no test has ever
|
||||
# run it against, on the target it was written for.
|
||||
#
|
||||
# Built for the musl target on a glibc host rather than inside an Alpine
|
||||
# container. The test binary links musl statically and runs natively on the
|
||||
# runner, so musl's `getifaddrs` is the one under test — while the build
|
||||
# scripts stay host artifacts, which keeps rustables' bindgen on the same
|
||||
# libclang the glibc leg already builds with. Building inside Alpine put
|
||||
# bindgen on a musl toolchain it does not work on: statically linked build
|
||||
# scripts cannot `dlopen` libclang, and turning the static CRT off then left
|
||||
# it loading libclang but unable to parse. None of that is anything this leg
|
||||
# is trying to test.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
test-musl:
|
||||
name: Unit tests (musl)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
|
||||
- name: Set SOURCE_DATE_EPOCH from git
|
||||
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)" >> "$GITHUB_ENV"
|
||||
|
||||
# libdbus for the host build scripts; musl-tools for the musl C
|
||||
# toolchain the `cc`-driven dependencies link against. BLE is excluded on
|
||||
# musl by a Cargo.toml cfg, so bluer is not in this build at all.
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev musl-tools
|
||||
|
||||
# The address-less interface the presence probe has to be tested
|
||||
# against; see the matching step on the glibc leg for why loopback
|
||||
# cannot stand in for it.
|
||||
- name: Create an address-less interface for the presence probe
|
||||
run: |
|
||||
sudo ip link add fips-probe0 type dummy
|
||||
# `addrgenmode none` before bringing it up: the kernel hands an IPv6
|
||||
# link-local to any interface that comes up, and an interface with a
|
||||
# link-local is not address-less — the fixture would have quietly
|
||||
# tested nothing.
|
||||
sudo ip link set fips-probe0 addrgenmode none
|
||||
sudo ip link set fips-probe0 up
|
||||
ip addr show fips-probe0
|
||||
# Fail rather than test the wrong thing if it acquired one anyway.
|
||||
if ip addr show fips-probe0 | grep -qE "inet6? "; then
|
||||
echo "fips-probe0 has an address; it cannot test the address-less case" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "FIPS_TEST_ADDRLESS_IFACE=fips-probe0" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1
|
||||
with:
|
||||
cache: false
|
||||
rustflags: ''
|
||||
target: x86_64-unknown-linux-musl
|
||||
|
||||
- name: Cache Cargo registry + build
|
||||
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: musl-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
musl-cargo-
|
||||
|
||||
- name: Run library tests
|
||||
run: cargo test --lib --target x86_64-unknown-linux-musl
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Job 2c – Unit tests (Windows)
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -455,6 +555,9 @@ jobs:
|
||||
# ── Firewall baseline (fips0 nftables default-deny) ────────────
|
||||
- suite: firewall
|
||||
type: firewall
|
||||
# ── Dynamic interface binding (absent → present → absent) ──────
|
||||
- suite: iface-binding
|
||||
type: iface-binding
|
||||
# ── Outbound LAN gateway integration test ──────────────────────
|
||||
- suite: gateway
|
||||
type: gateway
|
||||
@@ -620,6 +723,22 @@ jobs:
|
||||
run: |
|
||||
docker compose -f testing/firewall/docker-compose.yml down --volumes --remove-orphans
|
||||
|
||||
# ── Dynamic interface binding integration test ─────────────────────────
|
||||
- name: Run interface binding integration test
|
||||
if: matrix.type == 'iface-binding'
|
||||
run: bash testing/iface-binding/test.sh --skip-build --keep-up
|
||||
|
||||
- name: Collect logs on failure (iface-binding)
|
||||
if: matrix.type == 'iface-binding' && failure()
|
||||
run: |
|
||||
docker compose -f testing/iface-binding/docker-compose.yml logs --no-color
|
||||
docker exec fips-ifb-node-a fipsctl show transports || true
|
||||
|
||||
- name: Stop containers (iface-binding)
|
||||
if: matrix.type == 'iface-binding' && always()
|
||||
run: |
|
||||
docker compose -f testing/iface-binding/docker-compose.yml down --volumes --remove-orphans
|
||||
|
||||
# ── Chaos simulation ───────────────────────────────────────────────────
|
||||
- name: Install Python deps (chaos)
|
||||
if: matrix.type == 'chaos'
|
||||
|
||||
Reference in New Issue
Block a user