| Version | Status |
|---|---|
| v0.1.0 (alpha) | Supported — best-effort during alpha. |
| < v0.1.0 / pre-release | Not maintained. |
Please do not open public GitHub issues for security vulnerabilities.
File a private security advisory: https://github.com/vicondoa/d2b/security/advisories/new
For v0.1.0 (alpha), GitHub Security Advisories are the only supported disclosure channel. Email is not monitored and there is no PGP key published. Future versions may add additional channels — see the CHANGELOG for any expansion of the supported set.
GitHub's advisory tooling gates the disclosure timeline with coordinated-disclosure primitives (private discussion, CVE allocation, draft advisory) so a report filed there is the fastest path to a coordinated fix.
- A clear description of the vulnerability.
- Affected version(s) — commit hash or tag.
- Minimal reproduction (PoC if available, otherwise prose).
- Suggested severity (Critical / High / Medium / Low, optional).
- Disclosure preferences (timeline, attribution).
- Acknowledgment within 7 days (best-effort during alpha).
- An assessment + mitigation plan within 30 days.
- A coordinated-disclosure timeline negotiated case-by-case.
- A public advisory + CVE (where applicable) when the fix is ready.
In scope:
- The d2b host-side modules (
nixos-modules/). - The d2b CLI (
nixos-modules/cli.nix). - The per-VM sidecars (
nixos-modules/host-sidecars.nix,nixos-modules/components/). - The framework's SSH key management (
d2b-keysactivation, virtiofs injection). - Network isolation / NAT / firewalling (
nixos-modules/net.nix,nixos-modules/network.nix). - The Rust workspace (
packages/) — bootstrap surface, supply-chain gates, and long-lived control-plane behavior.
Out of scope:
- Vulnerabilities in upstream
nixpkgs,microvm.nix,cloud-hypervisor,crosvm,swtpm— report those to their respective maintainers; we'll coordinate. - Vulnerabilities in consumer-side code that uses d2b (your own
/etc/nixosis your concern; d2b provides primitives). - Physical attacks (encrypted disk + TPM-bound unlock is a Lanzaboote concern, not d2b's).
- Side-channel attacks on shared CPU cache / SMT — out of scope (hardware-level concern).
- Supply-chain attacks on the Nix store (defer to upstream Nix + nixpkgs).
For the full threat model, see docs/explanation/design.md.
The short version: d2b defends against compromised-guest-userspace and cross-VM lateral movement. It does NOT defend against compromised host kernel, multi-user trust on a single host, or hardware-level adversaries.
The portability work introduces a non-root d2bd daemon plus a
minimal root-owned d2b-priv-broker (see ADRs 0001-0008 under
docs/adr/). The new trust boundaries the daemon work
will introduce are:
-
A single public CLI socket at
/run/d2b/public.sock, mode0660groupd2b. Membership in thed2bgroup (populated fromd2b.site.launcherUsers) is the only connection gate — there is no secondd2b-adminsocket or group. Destructive / admin verbs (vm exec,audit, andconfig sync's guest read) are gated a second time inside the daemon: theSO_PEERCREDpeer identity must also appear ind2b.site.adminUsers. Authorization isSO_PEERCREDplus the system account database — never polkit at runtime. -
A private broker socket at
/run/d2b/priv.sockreachable only by thed2bdservice uid. The broker re-derives every privileged parameter from its own copy of the root-owned bundle and writes an append-only root-owned audit log. -
Per-role minijail profiles for every VM runner and sidecar, with declared uid/gid, capability sets, namespace plan, bind mounts, seccomp policy, and cgroup placement.
requiresStartRootis permitted only for audited carve-outs.v1.1.2 update (ADR 0021): the virtiofsd
requiresStartRoot=truecarve-out from ADR 0003 is RETIRED. virtiofsd profiles now declare zero host capabilities (capabilities = []),requiresStartRoot = false, and auserNamespaceblock mapping in-NS UID/GID 0 to the per-share principal. Normal VM shares map to the per-VM runner principal; the guest-control token share maps to the narrowerd2b-<vm>-gctlfsprincipal. The broker pre-establishes the namespace viaclone3(CLONE_NEWUSER)+/proc/<pid>/uid_mapwrites before exec; virtiofsd runs fake-root only inside the per-share user NS. This is strictly stronger than v1.1.1: a compromised virtiofsd cannot access host resources outside its bind-mounted share, even with kernel exploits that bypass the sandbox, because the host kernel sees its credentials as the unprivileged share principal — there are no in-host caps to escalate from.
The first non-NixOS target is Ubuntu 24.04 LTS x86_64 with kernel
= 6.6, Nix daemon install, KVM, cgroup v2 unified hierarchy, nftables, NetworkManager, Cloud Hypervisor, and a Nix-built minijail. macOS/vfkit, WSL, containers as hosts, Alpine/musl, non-systemd autostart, rootless Nix, Firecracker feature parity, crosvm-as-full-VMM parity, and aarch64 runtime graphics/audio are explicitly rejected at the first milestone; adding any of these requires a new ADR + panel sign-off.
Telemetry posture is preserved: d2bd makes no outbound network
connections by default; any future opt-in lands behind an explicit
--enable-diagnostics flag and an update to this file.
The v2 constellation layer (ADR 0032)
preserves the host's no-realm-egress posture: the host daemon and
broker still open no realm relay/provider connections. Realm egress
(relay rendezvous, provider APIs) is opt-in and confined to a per-realm
gateway guest VM; the host holds none of that realm's relay,
session, or provider credentials. A realm relay is treated as an
untrusted, ciphertext-only rendezvous: it sees connection metadata and
traffic shape but never plaintext operations, and relay credentials
authenticate relay access only — never a constellation principal or
local Admin. Later realm-policy work (a separate ADR/reference) is
expected to add endpoint-allowlisted, region-pinned realm egress as
gateway-held realm configuration; that enforcement is not a current host
guarantee and is not provided by the host daemon.
This section documents the daemon trust-boundary delta for consumers.
The broker's closed-enum surface covers host-prepare
mutation: cgroup v2 delegation + pidfd handoff (ADR 0011), per-link
sysctls + bridge/TAP + NetworkManager unmanaged config + /etc/hosts
managed-block + route preflight (ADR 0012), inet d2b nftables
table apply + USBIP firewall-rule skeleton (ADR 0013), and
modprobe/device-node opens + runner-shape preflight (ADR 0014).
The full operation catalog with audit/destructive/secret flags is
docs/reference/privileges.md; the
conceptual model + recovery runbook is
docs/explanation/host-prepare.md.
The new trust-boundary statements are:
- The broker mutates network, cgroup, sysctl,
/etc/hosts, NetworkManager unmanaged config, andmodprobestate on behalf ofd2bd, gated entirely by the closed broker enum plus the trusted bundle. Every operation has a typed handler underpackages/d2b-priv-broker/src/ops/and re-derives its operating paths from the bundle, never from caller input. - Compromise of
d2bdcannot escalate to arbitrary host mutation beyond the declared broker enum variants. Unknown variants and unknown fields in security-sensitive artifacts are refused (defaultForUnknown: deny). - The broker audit log
(
/var/lib/d2b/audit/broker-<utc-date>.jsonl) is root-owned, append-only via a pre-openedO_APPENDfd, and rotated daily. Retention defaults to 14 days, overridable viad2b.site.audit.retentionDays(set to0to disable pruning). Reserved: broker prune-on-rotate is shipping, but the NixOS option is not yet threaded into the broker invocation; broker uses the 14-day default regardless of overrides until then. The legacy/var/lib/d2b/broker-audit.logcompatibility shim has been retired: both the writer (AuditLog::write_entryandAuditLog::write_op_record) and the reader (AuditLog::export_lines, which now enumerates the full daily-file directory in chronological order) operate solely againstbroker-<utc-date>.jsonlfiles — seedocs/reference/daemon-api.md"Retention" and "Legacy retirement". - An admin can pause the broker (
d2b admin broker --pause); the post-compromise rotation/repair runbook lives atdocs/explanation/host-prepare.md§ Recovery runbook. - USBIP live device routing (
UsbipBind/UsbipUnbind/UsbipProxyReconcile) is explicitly out of scope for this trust-boundary delta; only the per-busidUsbipBindFirewallRuleskeleton is covered.
d2b vm exec runs a command inside a VM over
the authenticated guest-control vsock channel — there is no SSH. The
trust-boundary statements are:
- Admin-only, destructive. Guest exec is a destructive verb: the
SO_PEERCREDcaller must be ind2b.site.adminUsers(the daemon-side role gate above), on top of thed2b-group connection gate. Per-VM exec must also be enabled in the bundle (guest.control.enable+guest.exec.enable). Every exec runs the requested command as the VM's workload user (ssh.user) — never root — inside a real PAM login session (systemd-run --property=PAMName=login --uid=<user>); the wireuserfield is host-fixed by guestd and ignored, and operators elevate withsudoinside the session. - Leak-safe daemon-side audit. The daemon records attached exec
lifecycle events (
GuestControlExecEstablished/GuestControlExecTerminated) to its owndaemon-events-<utc-date>.jsonl, carrying ONLY the VM name, the adminpeer_uid, and the negotiatedttyshape. Detached create and kill/cancel write separate redacted daemon audit events carrying ONLY the VM name, adminpeer_uid, closed action/result enums, and the opaqueexec_id. The session handle, argv, env, cwd, exit status, and any stdin/stdout/stderr bytes are NEVER recorded. This daemon-side exec audit is distinct from the brokerOpAuditRecordstream (which covers privileged host mutation, not guest exec). - Containment / DoS limits. Exec is bounded at multiple layers: per-VM concurrent session caps; detached-exec slot and retained-log quotas; bounded per-op deadlines (each long-poll op gets a fresh deadline rather than an aging shared one); a hard in-flight op cap whose over-cap response is close-only — the owner session is torn down through the single existing teardown path with no reader-side socket write, preserving the single-writer invariant — so a stalled or abusive owner cannot pin unbounded work, and owner EOF/POLLHUP is always observed promptly; and bounded teardown on disconnect. Detached exec adds startup reconciliation, valid runner/workload re-adoption, orphan workload cleanup, terminal-record retention, and a periodic reaper that releases retained-log slots.
- Design / threat model
docs/explanation/design.md— defense-in-depth list- CHANGELOG — version history including security-relevant fixes
- docs/reference/security-runbook.md — operator incident-response, USBIP containment, and recovery procedures