Skip to content

Security: nhowardtli/virp

Security

SECURITY.md

Security Policy

Reporting Vulnerabilities

VIRP is a security-critical protocol. If you discover a vulnerability in the cryptographic verification path, chain integrity, HMAC signing, or trust tier enforcement:

Do NOT open a public issue.

Email: nhoward@thirdlevelit.com

Include:

  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact assessment
  • Suggested fix (if you have one)

We will acknowledge receipt within 48 hours and provide a timeline for remediation.

Scope

The following are in scope for security reports:

  • HMAC-SHA256 signing bypass or forgery
  • Trust tier escalation (e.g., RED command executing as GREEN)
  • Chain database tampering without detection
  • O-Node socket authentication bypass
  • Device credential exposure through the API layer
  • Session handshake state machine violations

Out of Scope

  • Denial of service against the O-Node (known limitation — single-process architecture)
  • Issues requiring physical access to the host machine
  • Social engineering

Socket Peer Authentication

The O-Node Unix domain socket is gated by SO_PEERCRED (Linux). VIRP currently supports Linux only; the BSD getpeereid equivalent is not implemented. Every accept() reads the connecting process's UID and compares it against a startup-loaded allowlist:

  • VIRP_ALLOWED_UIDS — comma-separated UID list (e.g. VIRP_ALLOWED_UIDS=0,1001)
  • Prod builds also honor socket_allowed_uids in the JSON config
  • If neither is set, the allowlist defaults to the daemon's own effective UID — closed to every other local user

Rejected connections are closed immediately without reading any bytes and produce a single REJECTED connection: peer uid=... log line.

The socket itself is created mode 0660 atomically via umask(0117) set around bind() (with a belt-and-suspenders chmod(0660) after), so there is no window in which a world-accessible node exists on disk.

Trust Boundaries and Transport Paths

VIRP defines two distinct paths that reach the O-Node. Their protections are not the same, and a protection that applies to one does not automatically apply to the other.

Local Unix domain socket/run/virp/onode.sock (prod) or /tmp/virp-onode.sock (dev). Protected by:

  • SO_PEERCRED peer-UID allowlist (see previous section) — the kernel reports the caller's real UID, which is checked against the startup-loaded allowlist before any bytes are read.
  • Filesystem mode 0660 with ownership restricted to the daemon's service user/group.
  • VIRP message-layer session handshake (SESSION_HELLO / SESSION_HELLO_ACK with nonces, followed by HKDF session-key derivation) on every fresh connection.
  • HMAC-SHA256 signing of every observation returned to the caller, using an O-Key the caller does not possess.

TCP path (CT 210 dashboard ↔ CT 211 O-Node, ports 9998/9999) — the dashboard's virp-bridge.py listens on TCP 9998 locally on CT 210 and opens a TCP connection to CT 211:9999, where a socat forwarder proxies to the Unix socket. On this path:

  • SO_PEERCRED sees the local socat process's UID, not the remote dashboard's identity. It cannot distinguish authorized dashboard traffic from any other process on CT 211 that can reach the socat forwarder.
  • The VIRP message-layer session handshake and HMAC signing of observations still apply — observations returned across the TCP bridge are signed at collection time with the O-Key and are as verifiable as on the local path. An attacker who intercepts or injects on the TCP path cannot forge observations without the O-Key.
  • The TCP path itself is not currently TLS-protected, and the session handshake authenticates session establishment via nonce exchange but does not cryptographically bind to a TCP endpoint identity. Confidentiality, integrity of requests in flight, and mutual authentication of the two containers currently rely on:
    • W1 egress isolation (CT 210 can reach only allowlisted ports on CT 211),
    • network segmentation between the two containers,
    • the O-Key's secrecy on CT 211 (observations remain unforgeable even if the request channel is compromised).

Open work. TCP-path mutual authentication — either mTLS between the dashboard bridge and the socat endpoint, or request-side signing at the VIRP message layer — is not yet implemented and is tracked as follow-up hardening.

Supported Versions

Version Supported
main branch
Older commits Best-effort

Recognition

Security researchers who report valid vulnerabilities will be credited in the CHANGELOG (unless they prefer anonymity).

There aren’t any published security advisories