Compare commits

..
Author SHA1 Message Date
Sharang ParnerkarandClaude Opus 4.8 3cedb488ac feat(pipeline): firmware SBOM via tramiton reproducible build (phase 2)
CI / Check (pull_request) Successful in 6m49s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Upgrade the firmware SBOM from analysis-only to a full reproducible build: run
tramiton's NixBackend (`seal_and_build`) over the ingested source to produce a
sealed lock whose libraries are pinned and whose firmware artifact carries a
content hash, then render the SBOM from the lock plus deep binary SCA of any
pre-compiled inputs — the complete bill of materials (toolchain + every fetched
library + the firmware image), mapped into `SbomEntry` rows.

Graceful by design: when no nix backend is available (or the build fails / times
out at 15m), it falls back to the analysis-only components (toolchain +
resolvable libraries), so a nix that can't run in the deployment never breaks a
scan.

Infra: the agent image now installs `nix-portable` (rootless, single binary;
best-effort — a failed download just leaves the analysis-only path). The nix
store lives under `NP_LOCATION=/data/compliance-scanner` — mount a PERSISTENT
volume there in the deployment, otherwise every firmware scan re-fetches
nixpkgs + cross toolchains.

Adds the `tramiton-sbom` dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:07:23 +02:00
4 changed files with 29 additions and 66 deletions
Generated
+13 -14
View File
@@ -2103,7 +2103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3698,7 +3698,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.60.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -4679,7 +4679,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4692,7 +4692,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5570,10 +5570,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
dependencies = [
"fastrand",
"getrandom 0.3.4",
"getrandom 0.4.1",
"once_cell",
"rustix 1.1.4",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -6150,8 +6150,8 @@ dependencies = [
[[package]]
name = "tramiton-core"
version = "0.4.1"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.1#ae4fc1376279f9edb9882605b20877335e7ba8ba"
version = "0.4.0"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.0#e3dc1bf7027a2f6d7b1fe43043d6dfa887ce4af3"
dependencies = [
"serde",
"tempfile",
@@ -6162,11 +6162,10 @@ dependencies = [
[[package]]
name = "tramiton-repro"
version = "0.4.1"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.1#ae4fc1376279f9edb9882605b20877335e7ba8ba"
version = "0.4.0"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.0#e3dc1bf7027a2f6d7b1fe43043d6dfa887ce4af3"
dependencies = [
"serde",
"serde_json",
"sha2",
"tempfile",
"thiserror 1.0.69",
@@ -6177,8 +6176,8 @@ dependencies = [
[[package]]
name = "tramiton-sbom"
version = "0.4.1"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.1#ae4fc1376279f9edb9882605b20877335e7ba8ba"
version = "0.4.0"
source = "git+ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git?tag=v0.4.0#e3dc1bf7027a2f6d7b1fe43043d6dfa887ce4af3"
dependencies = [
"object",
"serde",
@@ -6746,7 +6745,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
+13 -30
View File
@@ -13,12 +13,6 @@ RUN --mount=type=secret,id=tramiton_token \
fi && \
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release -p compliance-agent
# A throwaway stage that packs a real nix store (store paths + the validity DB)
# into a compressed bootstrap tarball. Only the tarball is copied into the final
# image, so we don't carry a raw /nix copy layer.
FROM nixos/nix:latest AS nixseed
RUN tar -C / -czf /nix-bootstrap.tar.gz nix
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates libssl3 git curl python3 python3-pip npm golang-go php-cli && rm -rf /var/lib/apt/lists/*
@@ -46,30 +40,20 @@ RUN pip3 install --break-system-packages semgrep
# Install ruff for Python linting
RUN pip3 install --break-system-packages ruff
# Real nix for the tramiton reproducible-build firmware SBOM.
#
# nix-portable's proot fallback can't run here: user namespaces are blocked by
# the container's default seccomp/apparmor profile, and orca exposes no way to
# relax it. So ship a *real* nix and disable its build sandbox
# (`sandbox = false`) — a plain gcc/make firmware build needs no user namespace,
# so it runs fine under the locked-down profile with no proot involved.
#
# The store is shipped as a bootstrap tarball and seeded onto /nix at first
# start (see docker/agent-entrypoint.sh), so a persistent /nix volume survives
# redeploys. A missing/broken nix just falls back to the analysis-only SBOM.
COPY --from=nixseed /nix-bootstrap.tar.gz /opt/nix-bootstrap.tar.gz
ENV PATH="/nix/var/nix/profiles/default/bin:${PATH}"
RUN mkdir -p /etc/nix && printf '%s\n' \
'experimental-features = nix-command flakes' \
'sandbox = false' \
'build-users-group =' \
'substituters = https://cache.nixos.org' \
'trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' \
> /etc/nix/nix.conf
# Install nix-portable (rootless nix) so the firmware-SBOM pipeline can drive a
# tramiton reproducible build (NixBackend). Best-effort: if the download fails,
# the agent falls back to analysis-only firmware SBOMs (never breaks a scan).
# The nix store lives under NP_LOCATION — mount a PERSISTENT volume there in the
# deployment, else every firmware scan re-fetches nixpkgs + cross toolchains.
ARG NIX_PORTABLE_VERSION=v012
RUN curl -fsSL -o /usr/local/bin/nix-portable \
"https://github.com/DavHau/nix-portable/releases/download/${NIX_PORTABLE_VERSION}/nix-portable-x86_64" \
&& chmod +x /usr/local/bin/nix-portable \
|| { rm -f /usr/local/bin/nix-portable; echo "WARN: nix-portable install skipped; firmware SBOM uses analysis-only fallback"; }
ENV NP_LOCATION=/data/compliance-scanner
RUN mkdir -p /data/compliance-scanner
COPY --from=builder /app/target/release/compliance-agent /usr/local/bin/compliance-agent
COPY docker/agent-entrypoint.sh /usr/local/bin/agent-entrypoint.sh
RUN chmod +x /usr/local/bin/agent-entrypoint.sh
# Copy documentation for the help chat assistant
COPY --from=builder /app/README.md /app/README.md
@@ -81,6 +65,5 @@ RUN mkdir -p /data/compliance-scanner/ssh
EXPOSE 3001 3002
# Seeds /nix (fresh volume) from the bootstrap tarball, then runs the agent.
ENTRYPOINT ["/usr/local/bin/agent-entrypoint.sh"]
ENTRYPOINT ["compliance-agent"]
+3 -3
View File
@@ -14,12 +14,12 @@ compliance-dast = { path = "../compliance-dast" }
# Same-company IP, used directly (not via CLI) so the whole tramiton suite is
# available to the onboarding classifier. NOTE: CI must be able to fetch this
# private repo (see the git-auth step in .gitea/workflows/ci.yml).
tramiton-core = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.1" }
tramiton-core = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.0" }
# tramiton-repro drives the reproducible build (NixBackend seal_and_build) that
# yields a sealed lock; `libraries_from_inputs` is the analysis-only fallback.
tramiton-repro = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.1" }
tramiton-repro = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.0" }
# tramiton-sbom renders the bill of materials from a sealed lock (+ binary SCA).
tramiton-sbom = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.1" }
tramiton-sbom = { git = "ssh://git@gitea.meghsakha.com:22222/sharang/tramiton.git", tag = "v0.4.0" }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
# Seed the nix store on first start, then run the agent.
#
# The firmware-SBOM pipeline drives a real `nix` build (tramiton NixBackend).
# The image ships the store as a bootstrap tarball rather than baking /nix, so a
# persistent /nix volume (mounted empty on first deploy) gets populated once and
# then survives redeploys. Seeding is best-effort: if it fails, the agent still
# starts and firmware SBOMs fall back to analysis-only.
if [ ! -e /nix/store ]; then
echo "agent-entrypoint: seeding /nix store from image bootstrap..."
mkdir -p /nix
if tar -C / -xzf /opt/nix-bootstrap.tar.gz; then
echo "agent-entrypoint: /nix store seeded."
else
echo "agent-entrypoint: WARN nix seed failed; firmware SBOM will use analysis-only fallback."
fi
fi
exec compliance-agent "$@"