# Supply-Chain & Security Audit — Runbound
This document describes the tooling, cadence, and procedures used to keep Runbound's
dependency tree free of known vulnerabilities, licence violations, and stale crates,
and to guide internal or third-party source-code audits.
---
## Tools
| [`cargo audit`](https://github.com/rustsec/rustsec/tree/main/cargo-audit) | CVE / RUSTSEC advisory scan | `cargo install cargo-audit` |
| [`cargo deny`](https://github.com/EmbarkStudios/cargo-deny) | Licence policy + advisory gate + ban rules | `cargo install cargo-deny` |
| [`cargo cyclonedx`](https://github.com/CycloneDX/cyclonedx-rust-cargo) | SBOM generation (CycloneDX 1.4, JSON) | `cargo install cargo-cyclonedx` |
| [`cargo outdated`](https://github.com/kbknapp/cargo-outdated) | Stale dependency detection | `cargo install cargo-outdated` |
---
## Makefile targets
```bash
make audit # cargo audit --deny warnings — zero-CVE gate
make deny # cargo deny check — licence + advisory + ban policy
make sbom # cargo cyclonedx → sbom.cdx.json — full dependency inventory
make audit-full # runs all three above + cargo outdated
```
---
## Recommended cadence
| `make audit` | Every release + every week in CI |
| `make deny` | Every dependency addition or `Cargo.lock` change |
| `make sbom` | Every git tag (attach `sbom.cdx.json` as a release asset) |
| `cargo outdated` | Monthly + before any minor or major release |
---
## Reproducibility
**`Cargo.lock` is committed.** Every build resolves to the exact same crate versions.
Dependency updates are a conscious, audited step — not a side-effect of a stale lockfile.
**Rust toolchain pinning.** Pin the compiler in `rust-toolchain.toml` to prevent silent
breakage when a new toolchain ships between CI runs:
```toml
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
```
---
## Licence policy (`deny.toml`)
Runbound is AGPL-3.0-or-later (with a commercial licence option).
The `deny.toml` licence policy allows:
**Permitted:** MIT · Apache-2.0 · Apache-2.0 WITH LLVM-exception · BSD-2-Clause ·
BSD-3-Clause · ISC · Zlib · Unicode-3.0 · CDLA-Permissive-2.0
**Blocked by omission (not listed = denied):**
GPL-2.0, LGPL-2.x/3.x without a linking exception — incompatible with static linking
and with the commercial dual-licence model.
Any dependency introducing an unlisted licence causes `cargo deny check` to fail,
blocking the merge.
---
## Critical third-party dependencies (manual review on every update)
These crates sit on the security-critical path. Every version bump requires a manual
check of the RUSTSEC advisory database and the upstream changelog before merging.
| `rustls` / `rustls-webpki` | `src/main.rs`, `src/sync.rs` | TLS stack — CVE here breaks DoT/DoH/DoQ security |
| `tokio` | everywhere | Async runtime — attack surface for all network I/O |
| `hickory-server` / `hickory-resolver` | `src/dns/server.rs` | DNS parsing — processes untrusted network input |
| `axum` / `hyper` | `src/api/mod.rs` | HTTP server — injection, path traversal, request smuggling |
| `serde_json` | `src/store.rs`, `src/api/mod.rs` | JSON deserialisation — parses external (API) input |
| `ring` | transitive via `rustls` | Cryptographic backend — AEAD, HMAC, ECDSA |
| `rcgen` / `instant-acme` | `src/tls.rs` | Certificate management — ACME key handling |
| `cryptoki` | `src/hsm.rs` | PKCS#11 FFI — HSM key extraction, unsafe at the boundary |
---
## Release procedure
The following gates must all pass with zero errors before tagging a release:
```bash
# 1. Zero known CVEs or unsound/unmaintained advisories
make audit # cargo audit --deny warnings
# 2. Zero licence or dependency ban violations
make deny # cargo deny check
# 3. Generate and attach SBOM
make sbom # → sbom.cdx.json
# Upload sbom.cdx.json as a GitHub release asset.
# 4. Tag with GPG signature
git tag -s v0.X.Y -m "release v0.X.Y"
git push origin v0.X.Y
```
Steps 1 and 2 are blocking — a failed gate prevents the release.
Step 3 is mandatory for enterprise and government customers.
Step 4 ensures every release can be traced to a verified committer identity.
---
## SBOM (Software Bill of Materials)
The file `sbom.cdx.json` (CycloneDX 1.4 format) lists every transitive dependency
with its version, source hash, and SPDX licence identifier. It is generated by
`cargo cyclonedx` and attached to every GitHub release.
**Use cases:**
| Enterprise customers | Verify full dependency tree matches their approved software inventory |
| Government / ANSSI | Required artefact for CSPN qualification and supply-chain risk assessment |
| Security auditors (CC EAL) | Cross-reference component versions against national CVE databases |
| Incident response | Immediately determine whether a newly published advisory affects a running deployment |
**Download:** [github.com/redlemonbe/Runbound/releases/latest](https://github.com/redlemonbe/Runbound/releases/latest) → `sbom.cdx.json`
---
## Manual source audit — priority areas
For formal security evaluations (CSPN, Common Criteria EAL, enterprise red team,
government qualification), the following source areas have the largest attack surface
and should be reviewed first.
| **Constant-time auth** | `src/api/mod.rs` → `constant_time_eq`, `security_middleware` | Timing side-channels in Bearer token comparison; brute-force brake placement (must be pre-comparison) |
| **SSRF prevention** | `src/feeds/mod.rs` → `SsrfSafeDnsResolver`, `ssrf_safe_client` | Private IP and RFC-1918 range filtering at DNS-resolution time, not just URL parse time |
| **HSM key management** | `src/hsm.rs` → `load_and_store`, `extract_key` | Key extraction path, `Zeroizing<T>` usage, session close after extraction, fatal-on-failure guarantee |
| **Store integrity** | `src/store.rs`, `src/integrity.rs` | HMAC-SHA256 verification before deserialisation; timing-safe MAC comparison |
| **DNS name parsing** | `src/api/mod.rs` → `validate_dns_name` | RFC 1035 §2.3.4 boundaries (253-char total, 63-char label, ASCII only); applies to `name` field AND CNAME/MX/NS/PTR/SRV targets |
| **DNS query handler** | `src/dns/server.rs` | ACL bypass vectors, ANY/AXFR blocking, CHAOS-class identity probe suppression |
| **HA sync** | `src/sync.rs` | mTLS TOFU cert pinning, constant-time sync-token comparison, write-block on slave |
| **Rate limiting** | `src/api/mod.rs` → `ApiRateLimiter`, `src/dns/ratelimit.rs` | Token-bucket correctness, IPv4-mapped IPv6 normalisation, XFF rejection |
### HSM threat model note
When HSM is enabled, keys are **extracted** from the device at startup into
`Zeroizing<Vec<u8>>` buffers and the PKCS#11 session is closed immediately.
The HSM is therefore not required during normal operation, but the key material
exists in process memory until shutdown (where `Zeroizing` scrubs it on drop).
For deployments requiring keys to **never leave the HSM** (banking, government FIPS 140-3 L3),
a custom integration that performs all HMAC/signing operations inside the device is needed —
this is beyond the current `src/hsm.rs` scope. See [`docs/hsm.md`](hsm.md) for details.
---
## Acknowledged advisories
| RUSTSEC-2025-0134 | `rustls-pemfile` | Maintenance notice only, no CVE. `rustls-pemfile 2.x` is a thin wrapper around `rustls-pki-types`. Migration planned at next rustls update cycle. |
New advisories that cannot be ignored must block the release until the affected crate
is updated or the advisory is explicitly acknowledged in `deny.toml` with a dated justification.
---
## Cargo deny configuration
The full policy is in [`deny.toml`](../deny.toml) at the repository root:
- **`[advisories]`** — blocks CVEs, unsound crates, yanked versions; acknowledges RUSTSEC-2025-0134
- **`[licenses]`** — enforces the permit list above; exempts `runbound` itself (AGPL-3.0-or-later)
- **`[bans]`** — warns on multiple major versions of the same crate; bans wildcard version specs; skip-list for known transitive duplicates (rand/getrandom ecosystem, windows-sys, rcgen)
- **`[sources]`** — restricts to crates.io only; blocks unknown registries and git sources