Securing the Build
The build system is where source becomes artifact — and where the most dangerous supply chain attacks strike, because tampering there produces a malicious release that looks completely legitimate. Securing the build means making it isolated, reproducible, and trustworthy enough that its provenance actually means something. This post covers hermetic builds, build isolation, and the practices that make "how it was built" a guarantee rather than a hope.
Provenance (post 5) records how an artifact was built, but that record is only worth as much as the build environment’s integrity — provenance from a compromised builder is a lie you can verify. This post secures the builder itself. It’s the direct defense against the SolarWinds class of attack, and the foundation that makes SLSA’s higher levels meaningful.
Why the build is the critical link
Recall the apex threat from post 2: an attacker who compromises the build injects malicious code after source review and before signing, so it ships in a legitimate, signed release. Source-code security and signing both miss it, because the tampering happens in between. That makes the build environment the single most important link to secure — its integrity is what everything downstream (provenance, signing) rests on.
The goal of build security is to make the build trustworthy and its output determined solely by its declared inputs — so that what the provenance says was built is exactly what was built, with nothing injected. Two ideas do most of the work: isolation and hermeticity.
Build isolation
A secure build runs in an isolated, ephemeral, minimal-privilege environment: - Ephemeral — each build runs in a fresh, clean environment (a fresh container/VM) that’s destroyed afterward. This prevents persistence: malware from one build can’t linger and infect the next, and there’s no accumulated state for an attacker to exploit. (This echoes the ephemeral-runners point from the CI/CD security post.) - Isolated — builds are separated from each other and from the outside, so one build can’t interfere with another or reach systems it shouldn’t. A compromised build shouldn’t be able to touch production, other tenants’ builds, or your secrets. - Least privilege — the build environment has only the access it needs (fetch declared dependencies, write the output), and critically, untrusted builds (e.g. from fork PRs) run with no secrets, so a malicious PR can’t exfiltrate signing keys or credentials.
Isolation limits the blast radius: even if a build is compromised, an ephemeral, isolated, low-privilege environment contains the damage — the same “assume compromise, limit reach” principle that runs through all of security.
Hermetic and reproducible builds
The strongest build-integrity properties are hermeticity and reproducibility:
- Hermetic builds declare all their inputs explicitly and run with no network access during the build — every dependency is fetched and verified before the build, then the build runs sealed off. This is powerful because it means the build’s output depends only on its declared, verified inputs — nothing can be pulled in mid-build (no fetching an untrusted dependency at build time, no reaching out to an attacker’s server). A hermetic build can’t be influenced by anything not in its declared inputs, which is exactly what makes its provenance trustworthy.
- Reproducible builds produce bit-for-bit identical output from the same inputs, every time. This is a remarkable verification tool: if two independent builders build the same source and get the same bytes, you have strong evidence neither injected anything. Reproducibility lets you detect build tampering by comparison — a build that doesn’t reproduce is suspect. It also underpins the hash-based verification the whole chain relies on.
Together, hermeticity (output determined only by declared inputs) and reproducibility (same inputs → same bytes) turn the build from a black box into a verifiable function. They’re the technical substance behind SLSA’s higher levels, and while full hermeticity/reproducibility takes real engineering, moving toward them is what makes “we know how this was built” a genuine guarantee.
Trusted, platform-generated provenance
A subtle but critical build-security requirement (and the crux of SLSA’s higher levels): the provenance must be generated by the build platform itself, in a way the build steps cannot forge.
If the build’s own steps generate their provenance, a compromised build can simply write false provenance — claiming it built from clean source when it didn’t. The provenance is only trustworthy if it’s produced by the platform (outside the control of the potentially-compromised build steps), attesting to what it actually observed the build do. This is why “generate provenance” (a lower SLSA level) is weaker than “unforgeable, platform-generated provenance” (a higher one) — the latter can’t be faked by the very thing it’s attesting to. Securing the build includes ensuring its provenance comes from a trustworthy, isolated part of the platform, not from the build steps themselves.
Practical build hardening
Concrete practices that move a real build toward these ideals: - Pin build dependencies to digests — the compiler, base images, CI actions, and tools, so a compromised upstream can’t change what your build runs (post 2/CI-CD security). - Use trusted, maintained build platforms — a managed CI platform with strong isolation and provenance features beats a hand-rolled build server you must secure yourself; several now generate SLSA provenance automatically. - Minimize the build environment — fewer tools and less surface in the build image means less to compromise. - Verify inputs before building — check hashes/signatures of dependencies and base images before the (hermetic) build consumes them, so you’re building only from verified inputs. - Separate build from deploy privileges — the build produces and signs artifacts; a separate, gated step deploys them, so compromising the build doesn’t directly grant production access. - Protect and audit the build config — the pipeline definition is code (CI/CD series); review changes to it and audit who can modify the build.
Securing the build is what makes the rest of the chain honest: provenance means something only if the builder is trustworthy, signing means something only if the signing environment wasn’t compromised. Harden the build, and “we can prove how this was built” stops being a claim and becomes a guarantee — which is the whole point of supply chain security.
Key takeaways
- The build is the critical link — the apex attack (SolarWinds) injects code between source review and signing, producing a legitimate signed malicious release — so build integrity is the foundation everything downstream (provenance, signing) rests on.
- Build isolation: run each build in an ephemeral (fresh, destroyed after — no persistence), isolated (can’t reach production/other builds), least-privilege environment, with untrusted (fork) builds getting no secrets — contain the blast radius of a compromised build.
- Hermetic builds (all inputs declared, no network during build, output determined only by verified inputs) and reproducible builds (same inputs → bit-for-bit identical output, so tampering is detectable by comparison) turn the build into a verifiable function — the substance behind SLSA’s higher levels.
- Provenance must be platform-generated and unforgeable by the build steps — if the build writes its own provenance, a compromised build just fabricates it; trustworthy provenance comes from the isolated platform attesting what it observed.
- Harden pragmatically: pin build deps to digests, use trusted managed build platforms (many auto-generate SLSA provenance), minimize the build environment, verify inputs before building, separate build from deploy privileges, and protect/audit the build config — a trustworthy builder is what makes provenance and signing honest.