Security Hardening
Status: Roadmap
Last reviewed: 2026-05-15
This roadmap records the security hardening work around certificate pinning, local config persistence, secret handling, rekey policy, Noise first-packet behavior, and REALITY key rotation.
Current Status
| Area | State | Current behavior |
|---|---|---|
| Upstream TLS SPKI pinning | Complete | TCP upstream TLS rules carry upstream_tls_spki_pins_sha256. The agent verifies SHA-256 over the leaf certificate SPKI DER and rejects upstream TLS rules without pins. |
| Agent local config cache | Removed | The agent no longer reads or writes local config cache files. Applied L4/L3 config remains only in process memory and is replayed from the control plane after restart. |
| Secret zeroization | Complete + Planned | Control PSK decode buffers, generated PSK material, and control-channel ephemeral private keys use Zeroizing. Full runtime secret wrapper coverage remains planned. |
| Secret logging | Complete | Runtime fingerprints no longer include raw private-key hex; secret material is represented by short SHA-256 identifiers where a stable identity is needed. |
| Control channel rekey | Complete | The agent reconnects secure control sessions after NEXUS_CONTROL_REKEY_INTERVAL_SECS, defaulting to 6 hours. |
| QUIC reconnect age | Complete | QUIC REALITY clients close and reconnect after NEXUS_QUIC_MAX_CONNECTION_AGE_SECS, defaulting to 6 hours. |
| Noise first payload | Complete | Noise handshake messages no longer carry business payload. Initial payload is queued and sent after transport keys exist. |
| REALITY dual profile rotation | Planned | Current runtime rejects private-key mixing among QUIC REALITY routes that share one UDP listener and points operators toward dual profile/epoch rotation. Full active/next/retiring credential lifecycle is not yet implemented. |
Security Goals
The current hardening slice is designed to:
- Avoid creating a local disk copy of control-plane L4/L3 config.
- Keep already-applied data-plane listeners running in process memory while the agent process remains alive, even if the control plane disconnects.
- Require authoritative control-plane replay after an agent restart instead of trusting a local last-known-good cache.
- Provide audit-ready config apply signals for future comparison between local applied state and backend intent.
- Remove ordinary business-path TLS no-verify in favor of explicit SPKI pins.
- Avoid carrying business payload in the first Noise handshake packet.
The implementation does not claim to defend against a fully privileged runtime attacker that can read process memory, attach a debugger, or dump the agent process. Zeroization reduces exposure windows; it is not a guarantee against root-level memory observation.
SPKI Pinning
TcpProxyRule includes:
repeated string upstream_tls_spki_pins_sha256 = 20;Accepted encodings are hex, base64, or base64url of:
SHA-256(subjectPublicKeyInfo DER)Rules:
- Upstream TLS requires at least one pin.
- Multiple pins are allowed for certificate/key rotation.
- Pinning is explicit; trust-on-first-use is intentionally not the default.
- REALITY camouflage target probing may still use a probe-only no-verify path, but normal upstream TLS must not.
Removed Local Config Cache
The previous agent disk cache has been removed. The agent no longer creates, loads, migrates, or updates:
agent-state.nxcagent-state.previous.nxcagent-state.json
The cache environment variables are no longer part of the runtime contract:
NEXUS_CACHE_DIRNEXUS_AGENT_CACHE_KEYNEXUS_AGENT_CACHE_KEY_FILENEXUS_AGENT_CACHE_ALLOW_INSECURE_KEY_FILE
Runtime behavior is now:
- If the control plane disconnects after config has been applied, existing forwarders and L3 tunnels keep running from in-memory state.
- If the agent process restarts while the backend is unavailable, no local config is applied; the agent reconnects and waits for authoritative config replay.
- Duplicate config payloads may be skipped using in-process hashes/bytes, but this state is never persisted to disk.
Rationale: local cache encryption without a hardware-backed, operator-held, or external secret does not solve offline disk disclosure. Keeping no local config cache is simpler, easier to reason about, and makes backend availability/HA the explicit recovery boundary.
Rekey And Forward Secrecy
Secure control sessions use an authenticated open-session exchange and AEAD-protected frames. The agent proactively reconnects after the configured rekey interval so new session keys are derived.
Noise UDP behavior is intentionally closer to WireGuard's safety model:
- handshake packets do not carry arbitrary business payload.
- first business payload is queued locally.
- payload is sent only after transport keys are derived.
This preserves a fast user-visible path while avoiding the weaker property of putting application data in the initial handshake message.
QUIC REALITY currently relies on connection lifetime management. If the QUIC library exposes an explicit key-update interface in the future, that can be used; until then, reconnect is the rekey boundary.
REALITY Rotation
Current same-port QUIC REALITY credential constraints:
- QUIC REALITY routes sharing one UDP listen address must use the same REALITY private key.
- Different SNI/shortId combinations can share the QUIC REALITY listener within that key boundary.
- Mixing different REALITY private keys among QUIC REALITY routes on the same listener is rejected.
- This credential boundary does not prevent the same UDP socket from also carrying UDP RAW fallback or UDP Noise routes when packet dispatch is unambiguous.
Planned full rotation model:
- Store REALITY profiles with
active,next, andretiringstates. - Deploy dual-accept profile sets before moving clients.
- Move clients to the next public key and shortId set.
- Retire the old profile only after telemetry shows old usage has drained.
- Audit profile epoch, public-key fingerprint, node apply state, and remaining old-key connection count.
Remaining Work
- Replace more runtime
Vec<u8>/[u8; 32]private-key carriers with dedicated secret newtypes after protobuf/database boundaries. - Add backend Ed25519 signing for config updates.
- Add API and UI support for SPKI pin management.
- Build the full REALITY active/next/retiring credential lifecycle.
- Feed pin failures, config replay/apply events, rekey events, and REALITY rotation events into the observability matrix and TimeDB pipeline.