How NetDefense Secures the Control Channel to Your Firewalls
When a forged command equals remote root
NetDefense exists to manage edge network devices — OPNsense and FreeBSD firewalls sitting at the boundary of customer networks. That job carries an uncomfortable property: the device, by design, will execute the instructions it receives from the control plane. A task that updates a firewall rule, restarts a service, or applies a configuration is, in effect, a privileged action on a machine that guards an entire network.
So the question a security-conscious reader should ask first is not “is the traffic encrypted?” It is: how does a device know that a command genuinely came from the control plane, and not from someone who managed to get into the path? If an attacker can forge a single command that a device accepts, that attacker has remote root on a customer firewall. Authenticity of the device control channel is therefore the crown-jewel property of the whole system. Everything else — confidentiality, availability, convenience — is secondary to it.
This article walks through how that authenticity is built: the trust boundaries, the threat model we hold ourselves to, the cryptographic primitives that enforce it, and the design rationale behind the choices — including the device “rebind” ceremony that lets a device’s identity key be rotated without ever weakening the guarantee.
The modules and the trust boundaries
NetDefense is decomposed into a small number of services, each with a deliberately narrow role. Understanding the boundaries between them is most of the security story, because the design assigns the power to sign — to mint authentic commands — to exactly one place, and gives everyone else only the power to verify.
- NDManager is the control plane: the REST API, the place where policy and tasks are authored, and the holder of the control plane’s own Ed25519 signing key. It is the only component that produces authentic commands.
- NDBroker is the real-time dispatcher. It maintains a persistent connection to each device and pushes signed tasks down to them. Critically, it is a pure verifier: it holds no device secret and no authority to originate a command. It checks signatures and forwards bytes.
- NDPathFinder is a relay that brokers short-lived interactive tunnel sessions between an operator’s tooling and a device.
- NDAgent is the on-device daemon. It runs on the firewall itself, and it is the one place in the entire system that generates and holds the device’s private key.
- NDCLI and NDWeb are the operator surfaces — the command line and the web console.
The trust chain runs operator → control plane → dispatch → device. An operator authenticates to the control plane through interactive login and authors intent there. The control plane turns that intent into signed tasks. The dispatcher carries them. The device verifies and acts. The important architectural decision is that the ability to forge is not distributed along this chain. Only the control plane can sign a command, and only a device can sign as itself. Every other hop is a forwarder and a verifier — never an authority.
The threat model
A security claim is only meaningful against a stated adversary. Here is the attacker we design against — assumed capable, not hypothetical.
We assume the attacker can:
- Observe traffic and logs. Passive visibility into network flows and operational telemetry.
- Sit in the middle of the network. Actively intercept, delay, drop, or attempt to modify traffic between any two components — a full network MITM, including the ability to break or terminate the transport layer.
- Leak an enrollment or organization token. Obtain a credential that is meant to scope or bootstrap enrollment.
- Write to a database. Insert, update, or delete rows in backing stores — including fabricating what looks like a legitimate task record.
- Compromise a non-signing service. Take over a component that forwards or processes traffic but does not itself hold a signing key.
- Replay captured messages. Capture a valid, previously-sent message and attempt to re-deliver it later.
Against that attacker, we set out to make three things infeasible for anyone who does not hold a private signing key — not merely difficult:
- Forging a command that a device will accept. No attacker — including one with database write access or control of a non-signing service — should be able to produce a task that a device executes.
- Impersonating a device. No attacker should be able to speak to the control plane as a device it does not control.
- Replaying old commands. No attacker should be able to capture a legitimate command and have a device act on it a second time, or out of order.
The qualifier in that sentence is deliberate and load-bearing. We do not claim the control plane’s signing key is unbreakable, because nothing can claim that. That key is the trust root, and we treat it as one — protected, rotatable, and pinned by the devices that rely on it. Likewise, a device’s own private key can in principle leak; the entire rebind ceremony described later exists precisely for that case. The honest framing is that the security of the channel reduces to the secrecy of those two private keys. The architecture is arranged so that nothing short of compromising one of those private keys yields forgery, impersonation, or replay — and so that when a key is suspected of compromise, it can be cleanly retired.
The core principle: asymmetric identity, no shared secrets
The foundational decision is that identity in NetDefense is asymmetric. The control plane has a signing key and the corresponding public key; each device has its own signing key and its own public key. Authenticity is established by signatures over content, verified against public keys — not by presenting a secret that the verifier also knows.
This is a deliberate rejection of the obvious alternative: shared secrets and bearer tokens. A bearer token — an API key, a shared HMAC secret, a session credential — has a fatal structural property for this use case: anyone who holds it can produce traffic that looks authentic. That means the secret must exist in at least two places (the issuer and the holder), and very often in more (logs, databases, intermediary services, backups). Every one of those locations becomes a forgery oracle. If a bearer secret leaks — from a database-write attacker, a compromised forwarding service, or a verbose log — the attacker can immediately mint “authentic” commands. There is no way to verify a bearer token without being able to create one.
Asymmetric signatures break that symmetry, which is the entire point. The verifier holds only public keys. A public key can sit in a database, be logged, be forwarded, and be read by a compromised non-signing service, and none of that grants the ability to forge, because verifying a signature does not require the power to produce one. The private signing keys live only inside their trust boundaries — the control plane’s signing key within the control plane, and each device’s key on that device — and are never shared with verifiers. There is no shared secret on the control channel that, once leaked, lets an attacker speak with authority. This is why the rest of the design holds up under an attacker with database access and a compromised intermediary — capabilities that would be game-over in a bearer-token system.
Device enrollment and identity
A device’s identity is born on the device. When NDAgent enrolls, it generates its own Ed25519 keypair locally, on the firewall itself. The private key never leaves the device — it is never written to any server, never transmitted over the wire, and never escrowed. It lives on the device under restrictive permissions. Only the public key is sent to the control plane, exactly once, at registration, and stored against the device’s record.
This has a direct consequence for the threat model: there is no central store of device private keys to steal. A database-write attacker who dumps every device record obtains a pile of public keys — useful for verification, useless for impersonation. The thing that would let you be a device exists only on that device.
Of course, “the device generated a key and sent us a public key” raises the bootstrap question: how does the control plane know the public key it just received belongs to the device it claims to be? NetDefense uses Trust On First Use (TOFU) combined with explicit operator approval. The first public key submitted for a given device identity wins — it is recorded as that device’s key. But recording is not trusting: a freshly enrolled device sits in a pending state and is not enabled until an operator explicitly approves it. TOFU establishes a candidate binding; the human operator ratifies it.
It is worth being plain about TOFU’s well-known residual, because a security reader will reach for it immediately. TOFU concentrates trust at the first-contact moment: an adversary positioned at the very first connection could, in principle, present a key of their own. NetDefense does not pretend that window does not exist — it backstops it. For device identity, the backstop is the explicit operator approval step: a silent first-contact attacker can get a key recorded, but it cannot get that key enabled, because a human has to look at the pending device and ratify it before it can drive anything. The model is “pin, then ratify,” not “trust whatever shows up first.” That ratification is exactly the gate an unattended MITM cannot pass.
Bootstrapping trust in the control plane
The device’s end of the relationship is established by enrollment; the control plane’s end needs the same treatment, because the device must know which signing key to trust before it can verify a single dispatch. The same principle applies in the other direction. Control-plane signing keys are pinned on first connect (TOFU), fetched over TLS, and thereafter the device only honors dispatches signed by a pinned key. As with device identity, this is a first-contact trust decision, and it carries the same residual — which is why a trust root must be rotatable rather than permanent.
Because keys age and a suspected compromise must be recoverable from, the design carries a primary and an emergency control-plane key concept. The emergency key exists for one job only: to authorize rotation of the primary. It is therefore held to a stricter posture than the primary and kept out of routine signing, so it widens the rotation path without meaningfully widening the day-to-day signing surface. That is the principle behind having a second pinned key at all — signing authority stays scarce in normal operation, while the fleet retains a pre-pinned, operator-driven path to introduce a new primary if the primary must be replaced. This is the explicit acknowledgment that the control-plane key is the trust root: it is treated as one, with a planned rotation story rather than a hope that it never needs changing.
The authenticated task channel, in both directions
With asymmetric identities established at both ends, the actual task traffic is authenticated end to end, in both directions, at the application layer.
Every message — task and response — is carried in a COSE_Sign1 envelope: the CBOR Object Signing and Encryption single-signer structure, using EdDSA over Ed25519 as the signature algorithm. COSE_Sign1 and CBOR were chosen because they are compact and well-specified for signed structured payloads, which keeps overhead low over long-lived, high-fan-out dispatch connections.
Dispatch (control plane → device). When NDManager dispatches a task, it signs the task with its own Ed25519 key. The device verifies that signature against the control-plane public keys it has pinned before executing anything. The dispatcher, NDBroker, independently re-verifies the signed dispatch against the authoritative task record before forwarding it — it does not blindly relay. And a subtle but essential detail: the routing and critical fields a device acts on are read from the signed payload, not from any surrounding unsigned envelope or transport metadata. An attacker cannot wrap a legitimately-signed body in a misleading outer wrapper and have the device trust the wrapper, because the device only honors what is inside the signature.
A sharp reader will catch a tension here and should: the threat model grants the attacker the ability to write database rows, including fabricating what looks like a legitimate task record. So what does the broker’s check against the “authoritative” record actually buy, and what makes that record authoritative? The honest answer is that the broker’s re-verification is defense in depth — a consistency gate, not the authenticity root. The broker holds no signing key, so it can neither manufacture a valid control-plane signature nor bless a forged one; its check catches inconsistency before bytes go on the wire. The authenticity that genuinely matters is the one the device enforces itself: it verifies the control-plane Ed25519 signature over the task content against its pinned key. A fabricated task row carries no valid control-plane signature, so it produces a dispatch the device rejects — even in the hypothetical where the broker were somehow fooled. The database-write attacker can invent rows all day; what they cannot invent is the signature, and the device trusts only the signature.
Response (device → broker). The relationship is symmetric. The device signs every response with its private key, and the broker verifies that signature against the device’s registered public key. This is what makes device impersonation infeasible without the device’s private key: a response is accepted as coming from a device only if it carries a signature that only that device’s private key could have produced.
What each signature defends. The dispatch signature defends authenticity of commands: the device acts only on instructions that carry a valid control-plane signature. The response signature defends authenticity of device identity: the control plane believes a status, result, or telemetry report only if it is signed by the device it claims to come from. Together they make the channel mutually authenticated at the application layer — independent of, and beneath, any transport.
Replay protection. Authenticity of a single message is not enough; a captured-and-replayed valid message still carries a valid signature. NetDefense defends against replay with monotonically increasing per-device sequence counters and strictly-increasing task identifiers, backed by signed timestamp freshness windows. A message that arrives with a sequence number at or below what has already been seen, or with a stale timestamp, is rejected even though its signature is perfectly valid. Because the counters and timestamps are inside the signed payload, an attacker cannot tamper with them to make a replay look fresh without invalidating the signature.
The deliberate no-nonce design. A reader steeped in challenge-response protocols will notice what is absent: there is intentionally no interactive nonce, no “server sends a random challenge, device signs it back” round trip. This is a considered tradeoff. An interactive challenge-response handshake adds a round trip and statefulness to every exchange, which is costly on a dispatch channel that fans out across a fleet and must work over intermittent connectivity. Monotonic counters plus freshness windows achieve the anti-replay goal — a message is accepted only once, in order, and only if it is recent — without the extra round trip, and without requiring the verifier to mint and track per-exchange challenges. The cost is real and worth stating: freshness is bounded by a time window and by counter ordering rather than by a fresh-per-request random value. The benefit is a simpler, statelessly-verifiable, replay-resistant channel that scales. We made that trade consciously.
Transport: TLS as the outer layer, not the source of authenticity
All of this rides over TLS. TLS provides the encrypted outer layer — confidentiality and integrity of bytes in transit between components. That is real and valuable, and it is the right tool for transport encryption.
But here is the honest and important point: the authenticity of commands does not depend on TLS. The signatures are the authority. If an attacker were to break, terminate, or otherwise defeat the transport layer entirely, they still could not forge a command a device would accept, because the device verifies an Ed25519 signature over the task content at the application layer — above and independent of the transport. TLS protects the envelope in motion; the COSE_Sign1 signature protects the truth of the contents regardless of what happens to the envelope. This is defense in depth in the precise sense: a transport compromise costs the attacker confidentiality of that traffic, but it does not hand them command forgery. The two layers fail independently, and the property we care about most lives in the layer that does not depend on the network being honest.
The interactive tunnel
Some operator workflows need an interactive session to a device rather than a one-shot task. NDPathFinder serves these by brokering ephemeral, single-use, short-lived relay sessions carried over TLS. The control plane mints such a session exclusively for an operator who is already authenticated (via interactive login) and who is targeting a device that is already registered and operator-approved. The relay then blind-forwards bytes end to end between the operator’s tooling and the device.
It is worth being explicit about where this channel’s assurance comes from, since the rest of the article has been about per-message, mutually-authenticated signing. The tunnel’s assurance comes from its mint-time preconditions — an authenticated operator, and an already-registered, operator-approved device as the target — and the relay itself is a blind byte-forwarder that adds no new trust path of its own beyond those preconditions. This is a deliberate scope: the tunnel is an authenticated-operator convenience layered on top of the device-identity and signed-task foundations, inheriting its preconditions from them, rather than a separate trust path. The substantive cryptographic guarantees of NetDefense live in the device-identity and signed-task-channel design described above.
Rebind: rotating a device’s identity without weakening it
A device’s identity key is long-lived by design — but “long-lived” cannot mean “permanent and irreplaceable.” There are entirely legitimate reasons a device needs a new identity key:
- Suspected or known private-key compromise. If a device’s key may have leaked, it must be retired.
- Hardware replacement or RMA. A new box for the same logical device.
- A wiped or reinstalled device that lost its key state and can no longer prove its old identity.
- Key or protocol migration. Moving the fleet forward cryptographically.
- Routine hygiene — planned decommission-and-reissue.
The operation that handles all of these is the rebind, or re-enrollment, ceremony. The interesting part is not that it exists, but the constraint it has to satisfy.
Why it must be operator-gated, and can never be silent or self-service. The entire value of asymmetric identity is that no shared secret can silently re-bind a device. Consider the alternative: if simply presenting an enrollment or organization token were enough to replace a device’s public key, then that token would become a bearer secret of exactly the kind we rejected — anyone who leaked it could swap in their own key and become the device. That would quietly reintroduce the forgery oracle we designed the system to eliminate. So rebinding a device’s identity must require an explicit, one-time, operator-issued authorization. A leaked token, on its own, must not be able to replace a key. Replacement of identity is a deliberate, human-authorized act — not an automatic consequence of holding a credential.
How the ceremony works. The flow is built so that authority to rebind is scarce, scoped, and verifiable:
- An operator issues a one-time enrollment token. It is short-lived — bounded by the operator, defaulting to roughly a 24-hour window — and tied to the organization.
- The server stores only a SHA-256 hash of the token, never the token itself. This means even a database-write or database-read attacker who later sees that record cannot recover a usable token; they see only a hash. The server simultaneously clears the device’s existing public key, resets the device’s replay counter, sets the device to a pending/unregistered state, and drops any live sessions for that device. From this moment the old identity is no longer trusted. The counter reset is safe precisely because the old key is cleared in the same instant: a replay of any message signed under the old identity now fails signature verification against the now-absent old key, so the fresh counter only ever becomes meaningful relative to the new keypair — there is no window in which an old captured message with a low sequence number can be replayed as fresh.
- The operator delivers the token to the device out of band. Authority travels by a channel the operator controls, not through the system being re-bootstrapped.
- On next start, the device generates a brand-new keypair — it does not reuse the old key — and re-enrolls, presenting the token.
- The server binds the new public key only if all of the following hold: the presented token’s hash matches the stored hash under a constant-time comparison, the token has not expired, and it belongs to the same organization. The token is single-use and is consumed on success, so it cannot be presented twice. The constant-time comparison matters here specifically because the token hash is a secret-equality check; that is the kind of comparison where naive byte-by-byte matching could leak information through timing, which constant-time comparison is designed to prevent.
- The operator re-approves the device. As with first enrollment, recording the new key is not the same as trusting it — a human ratifies the new identity before it can drive anything.
Why a fresh keypair instead of reusing the old one. The ceremony deliberately forces a new keypair rather than re-blessing the existing key. This buys two things. First, clean revocation: the old public key is removed from the device record, so any task signed for the old identity, and any attempt to act as the old identity, is cryptographically dead the moment the rebind starts. Second, a clean break with no inherited trust: a key that may have been compromised is fully retired rather than rehabilitated, so a previously-leaked private key confers no authority after the rebind. Combined with the simultaneous replay-counter reset, the new device identity starts from a clean, unambiguous state with nothing carried over from the old one.
The net effect is a rotation mechanism that requires fresh human authorization, carries no recoverable secret at rest, cryptographically retires the old key, and cannot be triggered by anyone merely holding a token.
What the toolset buys you
The security of the NetDefense control channel rests on a small, deliberately chosen set of primitives, each doing one job:
- Ed25519 / EdDSA for identity and signatures at both ends — fast, modern, and asymmetric, so verification never grants forgery. (Ed25519 verification is itself constant-time by construction.)
- COSE_Sign1 over CBOR as the signed envelope for every task and response — compact and well-specified for signed structured payloads.
- Trust On First Use (TOFU) with key pinning for bootstrapping both device and control-plane identities, ratified by explicit operator approval that backstops the first-contact window.
- Monotonic sequence counters, strictly-increasing task identifiers, and signed freshness windows for replay resistance.
- SHA-256 to store rebind authorization as a hash, never as a recoverable secret, with a constant-time comparison on that secret-equality check.
- TLS as the encrypted transport beneath — confidentiality in motion, deliberately not the source of command authenticity.
From these, the system achieves the properties it set out to: a device executes only commands carrying a valid control-plane signature; the control plane trusts only device responses carrying that device’s signature; captured messages cannot be replayed; and a device’s identity can be rotated only by an explicit, single-use, operator-issued authorization that leaves no reusable secret behind. An attacker who observes traffic, writes to the database, compromises a non-signing service, or breaks the transport still cannot forge a command, impersonate a device, or replay an old one — because in every one of those cases, what they have obtained is the power to verify, never the power to sign.
The one thing that genuinely matters — the private signing keys — exists in exactly the two places it must: the control plane, and the device itself. We treat those roots as roots: protected, rotatable, and recoverable from compromise. Everything else in the system is, by design, just a forwarder and a verifier.
Enjoyed this post? Explore more in the documentation.