6.1 KiB
Here’s a practical blueprint for linking what you build to what actually runs, and turning that into proof-grade security decisions.
Static → Binary braid (build-time proof of “what functions are inside”)
Goal: Prove exactly which functions/offsets shipped in an artifact—without exposing full source.
-
What to store (per artifact):
- Minimal call‑stack “entry→sink” traces for relevant code paths (e.g., public handlers → sensitive sinks).
- Symbol map concordance:
{ function, file, address-range, Build‑ID, debug-id }. - Hashes per function-range (e.g., rolling BLAKE3 over
.textsubranges), plus overall.text/.rodatadigests.
-
How to generate:
-
During build, emit:
- ELF/PE/Mach‑O: capture Build‑ID, section ranges, and DWARF/CodeView ↔ symbol table mapping.
- Function-range hashing: disassemble to find prolog/epilog (fallback to symbol boundaries), hash byte ranges.
- Entry→sink traces: from static CFG or unit/integration tests with instrumentation; serialize as compact spans (start fn, end fn, edge list hash).
-
-
Proof object (tiny & replayable):
{ build_id, section_hashes, [ {func: name, addr: start..end, func_hash}, … ], [trace_hashes] }- Sign with DSSE (in‑toto envelope). Auditors can replay using the published Build‑ID + debug symbols to verify function boundaries without your source.
-
Attach & ship:
- Publish as an OCI referrers artifact alongside the image (e.g.,
application/vnd.stellaops.funcproof+json), referenced from SBOM (CycloneDXevidenceor SPDXverificationCodeextension).
- Publish as an OCI referrers artifact alongside the image (e.g.,
-
Why it matters:
- When a CVE names a symbol (not just a package version), you can prove whether that symbol (and exact byte-range) is present in your binary.
Runtime → Build braid (production proof of “what code ran”)
Goal: Observe live stacks (cheaply), canonicalize to symbols, and correlate to SBOM components. If a vulnerable symbol appears in hot paths, automatically downgrade VEX posture.
-
Collection (Linux):
- eBPF sampling for targeted processes/containers; use
bpf_get_stackidto capture stack traces (user & kernel) into a perf map with low overhead. - Collapse stacks (“frameA;frameB;… count”) à la flamegraph format; include PID, container image digest, Build‑ID tuples.
- eBPF sampling for targeted processes/containers; use
-
Canonicalization:
- Resolve PCs → (Build‑ID, function, offset) via
perf-map-agent/eu-stack, or your own resolver using.note.gnu.build-id+ symbol table (preferdebuginfodin lab; ship a slim symbol cache in prod). - Normalize language runtimes: Java/.NET/Python frames mapped to package+symbol via runtime metadata; native frames via ELF.
- Resolve PCs → (Build‑ID, function, offset) via
-
Correlate to SBOM:
- For each frame: map
(image-digest, Build‑ID, function)→ SBOM component (pkg + version) and to your Static→Binary proof entry.
- For each frame: map
-
VEX policy reaction:
-
If a CVE’s vulnerable symbol appears in observed stacks and matches your static proof:
-
Auto‑emit a VEX downgrade (e.g., from
not_affectedtoaffected) with DSSE signatures, including runtime evidence:- Top stacks where the symbol was hot (counts/percentile),
- Build‑ID(s) observed,
- Timestamp window and container IDs.
-
-
If symbol is present in build but never observed (and policy allows), maintain or upgrade to
not_affected(conditions: not_reachable_at_runtime)—with time‑boxed confidence.
-
-
Controls & SLOs:
- Sampling budget per workload (e.g., 49 Hz for N minutes per hour), P99 overhead <1%.
- Privacy guardrails: hash short-lived arguments; only persist canonical frames + counts.
How this lands in Stella Ops (concrete modules & evidence flow)
- Sbomer: add
funcproofgenerator at build (ELF range hashing + entry→sink traces). Emit CycloneDXcomponents.evidencelink to funcproof artifact. - Attestor: wrap funcproof in DSSE, push as OCI referrer; record in Proof‑of‑Integrity Graph.
- Signals/Excititor: eBPF sampler daemonset; push collapsed frames with
(image-digest, Build‑ID)to pipeline. - Concelier: resolver service mapping frames → SBOM components + funcproof presence; maintain hot‑symbol index.
- Vexer/Policy Engine: when hot vulnerable symbol is confirmed, produce signed VEX downgrade; route to Authority for policy‑gated actions (quarantine, canary freeze, diff-aware release gate).
- Timeline/Notify: human‑readable evidence pack: “CVE‑2025‑XXXX observed in
libfoo::parse_hdr(Build‑ID abc…), 17.3% of CPU in api‑gw@prod between 12:00–14:00 UTC; VEX → affected.”
Data shapes (keep them tiny)
-
FuncProof JSON (per binary):
{ "buildId": "ab12…", "sections": {".text": "hash", ".rodata": "hash"}, "functions": [ {"sym": "foo::bar", "start": "0x401120", "end": "0x4013af", "hash": "…"} ], "traces": ["hash(edge-list-1)", "hash(edge-list-2)"], "meta": {"compiler": "clang-18", "flags": "-O2 -fno-plt"} } -
Runtime frame sample (collapsed):
api-gw@sha256:…;buildid=ab12…;foo::bar+0x3a;net/http::Serve;… 97
Rollout plan (short and sweet)
- Phase 1 — Build plumbing: implement function-range hashing + DSSE attestation; publish as OCI referrer; link from SBOM.
- Phase 2 — Runtime sampler: ship eBPF agent with stack collapse + Build‑ID resolution; store only canonical frames.
- Phase 3 — Correlation & VEX: map frames ↔ SBOM ↔ funcproof; auto‑downgrade VEX on hot vulnerable symbols; wire policy actions.
- Phase 4 — Auditor replay:
stella verify --image Xdownloads funcproof + symbols and replays hashes and traces to prove presence/absence without source.
Why this is a moat
- Symbol‑level truth, not just package versions.
- Runtime‑aware VEX that flips based on evidence, not assumptions.
- Tiny proof objects make audits fast and air‑gap‑friendly.
- Deterministic replay: “same inputs → same verdict,” signed.
If you want, I can draft:
- the DSSE schemas,
- the eBPF sampler config for Alpine/Debian/RHEL/SLES/Astra,
- and the exact CycloneDX/SPDX extensions to carry
funcprooflinks.