Adds a third matrix job that builds a static-musl binary on Alpine
so future tags ship 4 assets per arch: dynamic + static.
The dynamic x86_64 build (gcc on ubuntu-latest) hits a glibc-version
ceiling — built against glibc 2.39, refuses to run on Debian 12
(2.36), RHEL 8/9, etc. install.sh now fetches the static asset by
default for x86_64; the dynamic remains available via
SKELETONKEY_DYNAMIC=1.
Static build details:
- Alpine container (native musl + linux-headers from apk).
- -DMSG_COPY=040000 covers the only musl-vs-glibc gap
(netfilter_xtcompat uses MSG_COPY, which is a Linux-kernel
constant that glibc exposes but musl omits — kernel header:
include/uapi/linux/msg.h).
- LDFLAGS=-static produces a static-PIE ELF (~1.2 MB).
- Cross-distro verified locally: Alpine-built binary runs on
Debian/Ubuntu/Fedora/RHEL.
Locally-built static binary was uploaded to v0.6.2 by hand to
unblock the one-liner installer immediately.
Adds tests/test_detect.c — a standalone harness that constructs
synthetic struct skeletonkey_host fingerprints (vulnerable / patched /
specific-gate-closed) and asserts each migrated module's detect()
returns the expected verdict. First real test coverage for the corpus;
catches regressions in the host-fingerprint-consuming logic.
Initial coverage — 8 deterministic cases across the 4 modules that
already consume ctx->host:
- dirtydecrypt: 3 cases verifying 'kernel < 7.0 -> predates the bug'
short-circuit on synthetic 6.12 / 6.14 / 6.8 hosts.
- fragnesia: unprivileged_userns_allowed=false -> PRECOND_FAIL.
- pack2theroot: is_debian_family=false -> PRECOND_FAIL.
- pack2theroot: has_dbus_system=false -> PRECOND_FAIL.
- overlayfs: distro=debian / distro=fedora -> 'not Ubuntu' -> OK.
Coverage grows automatically as more modules migrate to ctx->host
(task #12 below adds them). Each new module that consults the host
fingerprint can have its precondition gates tested with a one-line
EXPECT_DETECT call against a pre-built fingerprint.
Wiring:
- Makefile: new MODULE_OBJS var consolidates the module .o list so
both the main binary and the test binary can share it without
duplication. New TEST_BIN := skeletonkey-test target. 'make test'
builds and runs the suite.
- .github/workflows/build.yml: install libglib2.0-dev + pkg-config so
pack2theroot builds with GLib in CI (was previously stub-compiling).
New 'tests — detect() unit suite' step runs 'make test' as a
non-root user so modules' 'already root' gates don't short-circuit
before the synthetic host checks fire.
- Test harness compiles cross-platform but assertions are #ifdef
__linux__ guarded (on non-Linux all module detect() bodies stub-out
to PRECOND_FAIL, making assertions tautological); macOS dev build
reports 'skipped'.
Module change:
- pack2theroot p2tr_detect now consults ctx->host->is_root (with a
geteuid() fallback when ctx->host is null) instead of calling
geteuid() directly. Production behaviour is identical
(host->is_root is populated from geteuid() at startup); tests can
now construct non-root fingerprints regardless of the test
process's actual euid. Exposed a real consistency issue worth
fixing.
Verified in docker as non-root: 8/8 pass on Linux. macOS reports
'skipped' as designed.
The v0.1.0 tag's arm64 job failed with
fatal error: bits/wordsize.h: No such file or directory
because gcc-aarch64-linux-gnu alone doesn't pull in the cross libc
headers on Ubuntu 24.04 runners. Add libc6-dev-arm64-cross +
linux-libc-dev-arm64-cross so the cross-toolchain has its sysroot.
For 'people should say just use iamroot' framing, the install gate is
the single biggest discoverability bottleneck. This commit makes it:
curl -sSL https://github.com/KaraZajac/IAMROOT/releases/latest/download/install.sh | sh
.github/workflows/release.yml:
- Triggers on semver tag push (v*.*.*) + manual dispatch.
- Matrix build for x86_64 (gcc) and arm64 (aarch64-linux-gnu-gcc cross).
- Per-arch sha256sum alongside the binary.
- Auto-generates release notes pointing at CVES.md / ROADMAP.md and
including the install one-liner with the version-specific URL.
- Publishes via softprops/action-gh-release@v2.
install.sh (also uploaded as a release artifact, so the curl|sh
above is stable):
- Detects arch (x86_64 / aarch64 → arm64).
- Pulls iamroot-<arch> + iamroot-<arch>.sha256 from the requested
version (default: latest).
- Verifies sha256 via sha256sum or shasum -a 256.
- Installs to /usr/local/bin/iamroot (or $IAMROOT_PREFIX). Uses sudo
iff /usr/local/bin isn't already writable.
- Prints quickstart hints + ethics pointer at the end.
- Env knobs: IAMROOT_VERSION, IAMROOT_PREFIX, IAMROOT_REPO.
README.md gains a 'Quickstart' section at the top with the four
canonical commands: install, --scan, --audit, --detect-rules,
fleet-scan. Lands the 'curl|bash and go' UX as the first thing
visitors see.
- .github/workflows/build.yml: matrix of {gcc, clang} x {default,
debug} builds on every push + PR. Smoke tests after build:
--version, --list, --scan, --detect-rules auditd, --detect-rules
sigma. Build failure breaks merge gate.
- Static-build job runs continue-on-error (glibc + NSS issue with
static linking — getpwnam pulls in NSS at runtime; legacy DIRTYFAIL
Makefile noted this. Revisit with musl-gcc to get a truly portable
static binary).
- Kernel-VM matrix placeholder commented at the bottom of build.yml.
Real kernel matrix needs self-hosted runners or a paid VM service —
out of scope for tonight, in scope for Phase 4 followup.