Exordos NAC: architecture#
Exordos NAC controls corporate devices' access to network resources. It binds a registered device to its current network address, checks access conditions and sends grants to a gateway. The gateway applies them to real traffic with nftables.
The central idea is to separate device identity, network connectivity and resource access. A certificate identifies the device, a VPN or office network provides connectivity, and NAC policy determines access. Grants expire and require regular refreshes.
This document describes the broker implementation in exordos_nac and its
interaction with the agent in
exordos_nac_agent.
Request and response formats are defined in the protocol,
installation procedures in the deployment guide, and test commands
and integration stands in Verification.
1. Purpose and scope#
NAC answers three related questions:
- Which registered device is contacting the broker?
- Which resources may it access under its current state and policy?
- At which network address should the gateway apply those grants?
The system serves managed devices running an agent. The supported agent platforms are Linux, Windows and macOS; hardware protection and the server's ability to verify it differ between platforms.
Network connectivity is outside NAC. The product does not issue VPN access, establish its own VPN tunnel or authenticate Wi-Fi through RADIUS. A device first obtains connectivity and an address from existing infrastructure. Even without NAC grants, it needs the minimum access required to reach the broker.
NAC controls traffic passing through its gateway. It does not control connections that bypass it, including direct communication within one L2 segment. Switches and access points must isolate those devices. Network requirements are in section 9.
Terms#
| Term | Meaning in this project |
|---|---|
| Device | A Device record with a persistent device_uuid, owner, tags and pinned public key. |
| Broker | The server that enrolls devices, verifies requests and produces access decisions. |
| Agent | The device program: uses the key, contacts the broker and collects OS state signals. On a gateway, the same agent runs as the enforcement point. |
| Enforcement point | A Linux gateway running the agent in enforce mode. Code and protocol call it an enforcer. |
| Session | A temporary binding of a device to an address, with check results and granted resource sets. It is neither a TLS connection nor a VPN session. |
| Resource set | A named list of addresses, protocols and ports — a resource_set. Policy decisions grant access to these sets. |
| Device state | The evaluation of signals measured by the agent; called posture in the protocol. |
| Lease | The period during which a grant survives without another proof. |
2. Components and connections#
The system has a decision plane and a traffic plane.
HTTPS terminates at nginx in front of the broker. The broker does not serve TLS itself; its HTTP API listens on loopback.
| Component | Responsibility | What it does not do |
|---|---|---|
| Device agent | Creates or opens the key, enrolls, renews its certificate and sends measurements and evidence of key possession. | Does not assign itself an owner, tags or grants. |
| nginx | Verifies TLS and the client certificate chain; passes the certificate and observed client address to the broker. Limits request frequency and body size. | Does not make NAC policy decisions or know the device's database status. |
| Broker | Maps certificates to devices, verifies evidence, evaluates posture, maintains sessions and serves state to gateways. | Does not forward user traffic or install rules on remote gateways. |
| Device CA | Signs certificates for registered devices' public keys. The current implementation loads the CA key from a file into the broker process. | Is not the HTTPS server certificate and does not sign agent releases. |
| Policy engine | Loads YAML, checks constraints, selects resource sets and compiles nftables rules. | Does not contact agents or store sessions. |
| PostgreSQL | Stores enrollment tokens, devices, certificates, sessions and audit events. | Does not store device private keys. |
| Gateway agent | Fetches broker state, reconciles it with nftables, tracks IP/MAC bindings and clears conntrack when access shrinks. | Does not independently calculate business policy. |
| Operator CLI | Manages tokens and devices, validates policy and shows sessions, audit and rollout status. | Does not install gateway rules; the gateway agent applies them. |
2.1. Trust boundaries#
Device data is not a server decision. The agent reports measurements and claims how its key is stored. The broker separately records what it verified. The owner, tags and gateway scope come from an operator-issued token, not the device request.
nginx is part of the trusted authentication boundary. It overwrites
X-Client-Verify, X-Client-Cert and X-Real-IP on every request. The broker
uses those values, so its HTTP port must not be directly accessible to clients:
a direct client can supply those headers itself.
Loopback is not an access-control boundary between local users. The current listener also accepts connections from unprivileged local processes, which can forge the trusted headers. Certificate issuer and CN checks on that path do not prove possession of the private key. The upstream has no nginx-only socket permissions or authenticated proxy transport; a host with untrusted local users is outside the protection provided by this deployment.
The gateway is part of the trusted enforcement boundary. The broker trusts its MAC observations within its assigned networks. A compromised gateway can break enforcement in those networks; its certificate does not prove that its nftables rules are correct.
The release system is a separate trust boundary. The broker distributes signed update offers, but the release signing key must remain outside it. See section 11.
3. Core invariants#
- The certificate determines identity. After enrollment,
device_uuidcomes from the CN of the verified client certificate. Renewal and session proof requests do not acceptdevice_uuidin their bodies. - Renewal preserves the key. A new CSR must carry the same public key as the device record. Replacing the key requires another enrollment.
- Policy uses verified information. For key protection, it reads
key_protection_verified, notkey_protection_claimed. Ordinary OS measurements remain agent-reported information. - Grants are temporary. Broker sessions and nftables elements expire. The gateway does not refresh grants from old state when the broker is down.
- An ambiguous address receives no grants. If several devices with live grants claim one IP, the broker reports a conflict and grants nothing for that address.
- A device has one current network binding. Proof from a new known address closes that device's sessions at other addresses.
- The broker is reachable before grants are issued. That path also remains after grants are issued, so the device can refresh its access.
- Blocking is checked in the database. A certificate that has not expired does not let a blocked device prove a session, renew a certificate or fetch gateway state.
- A refusal has an explanation. Decisions contain matched rules and check results. A policy loading failure is reported separately from a refusal made by a valid policy.
4. Identity and key protection#
4.1. Keys and certificates#
The implementation uses several independent keys.
| Key or certificate | Location | Purpose |
|---|---|---|
| Device private key | On the device: TPM 2.0, a supported hardware backend or a file. | Signs CSRs and participates in mTLS. The device proves possession during the TLS handshake. |
| AK — TPM attestation key | In the TPM; its public part is sent to the broker. | Signs TPM2_Certify and TPM2_Quote structures. It is separate because the device key must sign TLS data. |
| EK — TPM endorsement key | In the TPM; the broker stores its public key and fingerprint. | Used in continuity checks through MakeCredential / ActivateCredential. |
| Device CA key | On the broker server, in the current file storage. | Signs device client certificates. |
| HTTPS server certificate and key | In nginx. | Let the agent verify that it is contacting the correct broker. |
| Release signing key | In the release system, separate from the broker. | Authorizes installation of a particular agent package. |
The device key is ECDSA P-256. The broker checks the CSR signature and rejects other curves. The device certificate contains its UUID in CN and is valid for 60 days. For mTLS, the agent uses a signing interface; the hardware backend does not hand the private key to the agent.
The software backend stores a copyable key. Therefore, "the device key cannot be moved" applies to a suitable hardware backend, not every enrolled device.
4.2. Claimed and verified levels#
key_protection_claimed describes the agent's claim. It may claim software
or hardware_bound. Only the broker assigns attested and device_attested
after verifying evidence.
| Level | Meaning |
|---|---|
software |
The server has no evidence that the key is held in hardware. This is also the initial verified level for a new device. |
hardware_bound |
The agent claims hardware key storage. The current broker does not assign this as a verified level: without verified TPM evidence it remains software. |
attested |
The broker verified consistent TPM evidence: the AK signature, key properties, nonce, certified key name and its match to the CSR. |
device_attested |
A correct response to a challenge with the pinned EK additionally establishes continuity relative to enrollment. |
These are protocol state names, not an absolute security ranking. Policy lists allowed values explicitly; the order of table rows does not grant extra rights.
The TPM path on Linux and Windows can reach attested and device_attested.
The broker verifies evidence of kind tpm2. Secure Enclave keys have no
server-verifiable attestation path in this deployment.
On macOS, the agent can use a permanent Secure Enclave key, but the broker
retains key_protection_verified = software even when the agent claims
hardware_bound. An operator who checks the installation in person can record
that in an operator-controlled tag and a separate policy. This is an
organizational procedure, not server attestation. Signed installation, the
per-user LaunchAgent and multiple accounts are covered in macOS.
4.3. What attestation establishes and what it does not#
The broker pins the EK on first contact and then checks that it stays the same. This is trust on first use, or TOFU. TPM manufacturers' EK certificate chains are not verified.
A consistent proof therefore does not establish the existence of a real TPM.
An attacker controlling enrollment can create consistent software keys and
structures that pass the checks. Broker tests cover this case.
device_attested must not be interpreted as independent proof of genuine
hardware.
For a real TPM, pinned keys detect broken identity after a disk is moved to
another machine or the TPM is replaced or reset. They cannot distinguish a VM
from a clone containing a copy of its vTPM state: the keys are the same.
Re-enrollment of one public key is recorded as duplicate_public_key, but is
not blocked by that signal alone. A clone of already-enrolled state need not
enroll again, so the signal does not guarantee detection of every clone.
The single-use token and operator-controlled enrollment remain the basis of trust at first contact. A hardware key also does not prevent an attacker who already controls the running machine from using it: preventing extraction and preventing a signing operation are different properties.
4.4. Boot-state verification#
During a TPM check, the agent may supply a TPM2_Quote for PCR 0–7. The broker
checks the signature against the pinned AK, the expected nonce, PCR selection
and agreement between reported values and the signed digest.
The first valid digest becomes the baseline. The server then produces
verified_boot_state:
pinned— measurements match the saved baseline;changed— valid evidence contains different measurements;invalid— supplied evidence failed verification.
A match establishes continuity from an accepted baseline, not compliance with a universal list of safe boots. A BIOS or OS update may legitimately change PCRs. The broker does not automatically accept a new baseline; an operator procedure repins it and records an audit event.
Continuity and boot checks run at continuity_minutes, 60 minutes by default.
Between checks, the server uses the previous result. A value of 0 requests
evidence on every proof. The TPM check cadence thus defines a separate intended
detection window from the network lease.
The current implementation does not enforce independent freshness of the boot
result. A valid continuity response without a PCR quote can preserve an old
pinned value and refresh the shared continuity timestamp. Repeating this can
keep that value indefinitely. The configured cadence must therefore not be
read as a guaranteed bound on boot-state detection for a hostile agent.
5. Device lifecycle#
5.1. Enrollment#
- The operator issues a single-use token with an owner, tags and expiry;
tokens last 30 minutes by default. For a gateway, the operator adds
role:enforcerand servedenforcesprefixes. - The token reaches the device through a trusted channel. The CLI can issue a join ticket containing the broker URL and a certificate fingerprint for verifying its HTTPS identity.
- The agent creates or opens a key and signs a CSR. It calls
POST /v1/enroll/with the token, CSR and device information. It has no client certificate yet; the HTTPS server still must be verified. - The broker checks the CSR and atomically claims the token for single use.
The owner, tags and
enforcesare copied from the token. If TPM evidence was supplied, it is verified before creating the device record. - The broker creates a
Device, issues a certificate, records enrollment and, when necessary, returns a TPM challenge for a subsequent request. - The agent saves its state and starts proving its network session.
Enrollment itself does not grant corporate access. It creates an identity for access policy to evaluate.
The token is a secret authorizing enrollment with the operator's assigned attributes. It is not a public identifier and must not be logged.
5.2. Certificate renewal#
The agent calls POST /v1/renew/ over mTLS with a certificate that is still
valid. The broker checks that the device is active, the certificate is not
revoked and the new CSR's public key matches the saved key. A changed key is
rejected as key_rotation_not_allowed.
The response contains a new certificate and the next renewal time,
renew_after. Certificate renewal and access refresh are separate: a new
certificate does not replace a session proof.
5.3. Blocking and key loss#
device block sets a device to BLOCKED. The broker refuses subsequent
authenticated requests and omits its grants from gateway state. A working
gateway applies the change when it receives fresh state; an unreachable
gateway retains old grants only until their timers expire.
If the private key is lost or stops working, ordinary renewal is impossible:
the agent cannot perform mTLS with the old identity. Register the device with a
fresh token after resetting agent state. It receives a new device_uuid;
block or delete the old record separately. hw_fingerprint is diagnostic
information and is not sufficient grounds for issuing a replacement key.
Moving from a previously used hardware key to software requires an explicit decision: the agent remembers hardware backend use and must not silently create a copyable replacement. If the old key is entirely lost, lowering the server's verified level alone does not restore the ability to perform mTLS.
6. Policy model#
6.1. Decision inputs#
The live session-proof path passes these dimensions to the engine:
| Dimension | Source |
|---|---|
state |
proven for a decision after proof. Rules for admitted determine minimum access before proof. |
tier |
managed for every enrolled device. |
device_tags |
Tags from the device record, assigned by the operator. |
key_protection_verified |
The server's key-protection verification result. |
posture |
The server's evaluation of signals. |
The engine format includes owner_groups, but the broker does not fetch
groups from IAM or pass them to decisions. Policy loading rejects rules with
owner_groups. The device owner is retained for attribution; policy matches
operator-assigned device_tags.
The CLI uses IAM to resolve an owner's UUID from a username when configured. The agent needs no user IAM token for ordinary access proofs.
6.2. Rules and grant union#
Policy is a version: 2 YAML file containing:
resource_sets— network destinations: hostnames, IPv4 addresses or prefixes, transport protocols and ports or port ranges;posture— required signals and maximum report age;policies— conditions and granted resource sets;constraints— forbidden combinations of conditions and grants;tests— expected decision examples.
Matching rules add together. The engine unions the resource sets from all
matching rules, removes duplicates and sorts the result. Rule order gives no
priority and there is no first-match behavior. No matches means no additional
grants. Rules with an empty grant are rejected during policy loading; there
are no cancelling rules.
In the example policy,
for instance, an engineering device may receive base and eng from its
department rule, then grafana from a separate rule matching an extra tag.
The result is the union of all three sets. Each rule must specify sufficient
access conditions on its own.
Tags can be plain (hardened) or key:value (type:full, dept:engineering).
A device has one value per key. Different keys are ANDed; allowed values for
one key are ORed. A missing tag is not a match.
constraints are checked against rules at load time, not only against test
requests. A constraint can, for example, prohibit prod_ro in any rule that
allows software, or require the hardened tag for that grant.
Resource names resolve to IPv4 addresses when policy loads. nftables receives
addresses, not domain names. A DNS change does not update loaded sets by itself;
policy must be reloaded. policy check --no-resolve validates structure using a
placeholder address and does not establish DNS correctness.
6.3. Device posture evaluation#
The agent sends a posture_report with a measurement time and signals such as
its version. The broker compares them to policy requirements and produces:
| State | Condition |
|---|---|
compliant |
All configured checks passed. |
degraded |
A report exists, but at least one check failed. |
unknown |
The report is missing, stale or substantially dated in the future. |
Maximum report age defaults to 300 seconds and is configured through
posture.max_age_seconds. A missing signal fails its check. With a fresh
report and no requirements, the result is compliant: no requirements do not
establish protection.
Agents may not submit signals prefixed with verified_. The broker adds these,
including verified_boot_state, so an ordinary report field cannot overwrite
a server result.
Other signals are still measured by the agent, and a compromised agent can lie. Evaluating them on the server gives consistent requirements; it does not turn every OS measurement into hardware evidence.
Posture is evaluated for the entire session. Before making a signal mandatory,
check that every target platform supplies it, or posture: compliant will
exclude part of the fleet. Reduced access for software, degraded or
unknown must be explicit in policy; the server does not grant it automatically.
7. From device proof to access grant#
7.1. Session proof#
Agent nginx Broker Gateway
│ HTTPS/mTLS │ │ │
├────────────────────►│ certificate check │ │
│ ├─────────────────────►│ │
│ │ │ device check │
│ │ │ TPM and posture │
│ │ │ policy decision │
│ │ │ session write │
│◄────────────────────┴──────────────────────┤ │
│ decision, reasons, expiry │ │
│ │◄─────────────────────┤ state request
│ ├─────────────────────►│ grants and ruleset
│ │ │ apply nftables
├───────────────────── resource traffic ────────────────────────────►│
In POST /v1/session/prove/, the agent sends measurements and, when challenged
earlier, a TPM response and boot evidence. The broker identifies the device by
its certificate and the address by X-Real-IP, which nginx sets from the
accepted connection's address.
The broker then verifies evidence, evaluates posture and policy, and saves the
decision in a session for the device/address pair. After processing, the
session is proven, including when the grant list is empty. That means the
request was processed, not necessarily that access was granted.
The agent receives resource sets, matched rules, failed-check reasons and
session expiry. enforced, enforced_from, enforced_to and
broker_endpoints describe the enforcement scope known to the broker and help
the agent select the correct network path.
enforced: true means an active gateway with matching scope is registered.
It does not confirm that the gateway is reachable now, has applied the latest
decision or is actually on the packets' path.
The agent response and gateway installation are separate events. A decision
affects traffic after the gateway fetches and applies state. The broker does
not wait for installation acknowledgement before answering prove.
7.2. Three independent lifetimes#
| Mechanism | Value | Effect of expiry |
|---|---|---|
| Device certificate | 60 days | New mTLS requests cannot succeed without certificate renewal. |
| Broker session | 15 minutes after the latest decision | The session is subject to closure and stops appearing in full grant responses. |
| nftables grant element | 15 minutes after installation or refresh | The kernel removes the address from the allowed-source set. |
The gateway applies relative lease_seconds, not each record's absolute
lease_expires_at. Server expiry and kernel timers therefore are not
synchronized. Grant removal after proofs stop depends on session closure and
the gateway fetching fresh state.
During a broker outage, the gateway does not reinstall cached elements. Live elements expire on their own timers: current 15-minute grants expire no later than 15 minutes after the last successful element refresh on that gateway. This is more precise than promising 15 minutes after the device's last request.
Element expiry restricts traffic in the originating direction. Stopping tracked reply traffic also requires the conntrack cleanup in section 8.2. The agent performs it in the state-application loop. When a broker request fails, that loop does not change the kernel, so an element timer alone does not guarantee immediate termination of every previously opened flow.
A failed TPM continuity check while mTLS still works lowers the verified level; policy then decides which resources remain. Total key loss is a different case, covered in section 5.3.
8. Gateway enforcement#
8.1. Fetching state#
A gateway enrolls as an ordinary device with role:enforcer. It has no
separate gateway certificate or second CA.
Over mTLS, it calls GET /v1/dataplane/state/ and receives:
- the compiled
rulesetand itspolicy_version; - grants by address: resource sets, device UUID and MAC binding;
- the common grant lifetime,
lease_seconds; - address conflicts and its own
enforcesscope.
The broker excludes expired sessions, blocked devices, sets unknown to current
policy and addresses outside that gateway's scope. Empty enforces means
all addresses, not no authority. Set explicit scopes when using several
gateways. The same boundaries are checked for gateway binding reports.
There are two polling modes:
- A fast conditional request, every 3 seconds by default. The gateway passes
an opaque
ETaginIf-None-Match. When applied state has not changed, the broker returns304with a new cursor. - A periodic full request after one third of the lease, about every 5 minutes. It omits the cursor to refresh timers even when rights are unchanged.
A 304 response reinstalls nothing and refreshes no timers. Comparison covers
rules, grant membership, bindings and gateway scope; an ordinary session-expiry
refresh by prove need not rewrite every element.
Rule versions are content-derived. A new version requires loading rules; ordinary grant changes update elements. The agent applies changes in an nftables transaction. After an apply failure, the next request must fetch full state rather than treating "unchanged" as confirmation of uninstalled rules.
8.2. nftables structure#
The compiler creates an inet nac table:
rs_<name>contains destinations: IPv4 address, protocol and port;granted_<name>contains device addresses allowed to use the set, with a 15-minute timeout;bound_addrsanddevice_bindingscontain checked IP/MAC bindings;- the
forwardchain applies rules to traffic traversing the gateway.
Rules belong to resource sets, not individual devices. More devices increase set element counts without creating a chain for every machine.
Enforcement mode defaults to dropping forwarded traffic, in this order:
- Accept the reply direction of tracked connections:
ct direction reply ct state established,related accept. - Accept minimum destinations before device proof.
- Reject Ethernet frames with a foreign MAC for an already-bound IP.
- Accept traffic matching resource sets granted to the source.
- Reject the rest; IPv6 has a separate counter.
A general ct state established,related accept rule would leave a
device-initiated flow open after its rights shrink. To stop server traffic in
the reverse direction, the gateway agent also removes that source's conntrack
entries when it loses grants. Cleanup follows rule application and has its
own error diagnostics.
The compiler supports monitor mode, which logs traffic that would be rejected
without blocking it. It is suitable for evaluating policy, not for isolation.
8.3. Minimum access before proof#
Sets in rules with state: admitted provide access to any source on the
controlled path without an individual grant. Typically these contain the
broker, DNS and required enrollment services. quarantine is an ordinary set
name, not a special filtering mode.
The broker adds broker_endpoints addresses to these sets when loading policy.
They must therefore exist, or broker access must be provided separately.
Their rules precede individual checks: a device must be able to enroll and
restore access before it proves identity or after losing ordinary grants.
Successful prove does not remove the minimum path. Blocking a device also
leaves it as a network-policy property. NAC blocking ends individually granted
access; it does not disconnect the machine from VPN, Wi-Fi or minimum services.
8.4. IP reuse and MAC bindings#
On an Ethernet segment, the gateway observes neighbors and pins a MAC to an
active session address. It reports the binding to
POST /v1/dataplane/bindings/. The broker stores it so a restarted gateway
receives it back rather than learning addresses again.
If an IP starts replying from a different MAC, the gateway removes the old grant locally and reports the change. The broker closes sessions for that address and stops issuing their grants. The new device must prove itself.
This helps prevent inheriting another device's grant when reusing its old IP. It is not cryptographic Ethernet authentication: simultaneous IP and MAC spoofing needs switch protection, and behind an intermediate router the gateway sees the router's MAC. Ethernet bindings do not apply to an L3 tunnel; VPN infrastructure must prevent source-address spoofing there.
8.5. Dataplane constraints#
- Compiled grants use IPv4 only. In enforcement mode, IPv6 has no individual
access grants; tracked reply traffic remains allowed on the forwarding path,
while other IPv6 is rejected. There is no full dual-stack policy.
The broker does not exclude IPv6 session addresses when an enforcer
has empty
enforces. Such a grant reachesipv4_addrsets and can fail the entire refresh transaction, allowing existing segment grants to expire. IPv4-only compilation is therefore not itself a safe IPv6 input filter. - Filtering covers
forward. Access to services on the gateway throughinputneeds separate protection. - The reverse direction depends on conntrack. Several routing gateways with asymmetric paths are not a supported availability scenario.
- A resource set is network permission, not an application role. A name such
as
prod_rodoes not make service access read-only; the application must enforce that.
9. Network integration requirements#
A running broker and successful prove do not establish correct network
enforcement. These infrastructure conditions must be checked at integration.
| Requirement | Purpose | Effect of violation |
|---|---|---|
| Preserve source addresses between device, broker and gateway. | The observed IP binds identity to packets. | NAT merges devices behind an address or grants access to the wrong address. |
| Route controlled destinations through the NAC gateway. | Rules act only on the actual traffic path. | Resources remain accessible through a bypass independently of NAC decisions. |
| Provide a broker path before proof. | Devices need it to enroll and refresh access. | Circular failure: the device cannot get a grant because it cannot request one. |
| Prevent source-address spoofing. | Grants bind to packet addresses. | Another device's IP can claim its grants; MAC binding covers only some cases. |
| Isolate clients whose traffic bypasses the L3 gateway. | Inter-client traffic may never enter forward. |
Direct exchange on L2 or inside a VPN bypasses NAC. |
| Align the proof path and resource path. | A device has one current address binding. | Grants apply to an interface that corporate traffic does not use. |
| Place MAC observation on the served segment. | The gateway needs the device's MAC, not an intermediate router's. | Address-owner change detection loses its ability to distinguish devices. |
| Account for IPv4 and infrastructure access separately. | DHCP, local DNS and gateway services may bypass this chain. | Basic services are unavailable or outside NAC rules. |
For VPN, this means preserving client source addresses toward the broker and resources, validating client addresses in the VPN and preventing a bypass between clients. For office networks, it means controlled routing, source protection and isolation on switches or access points. NAC does not configure these properties automatically.
One address plan#
broker_endpoints lists all addresses of one broker reachable from served
networks. It is used in three places:
- The agent selects a suitable broker address.
- The compiler adds addresses to the minimum allowed path.
policy routesproduces destinations for externally configured VPN or DHCP routes.
The agent also uses enforced_from and enforced_to, compares corporate
resource routes with the selected broker route, and compares its local source
address with the address observed by the server. These checks help detect a
wrong binding but cannot prove the absence of a bypass gateway: two routes may
use the same source address.
A simultaneous office connection and VPN must not assume independent grants
for both networks under one device_uuid. Routes must provide one working
binding for controlled traffic.
10. Storage, failures and operations#
10.1. Data model and audit#
| Entity | Main data |
|---|---|
EnrollmentToken |
Enrollment secret, owner, tags, enforces, expiry, use and revocation. |
Device |
UUID, owner, tags, status, key fingerprint, platform, hardware fingerprint, claimed and verified protection, pinned EK/AK and boot state. |
DeviceCertificate |
Issued certificate, serial, validity and renewal dates, issuance reason and revocation marker. |
Session |
Device, address, MAC binding, state, posture result, grants, matched rules and time bounds. |
AuditEvent |
Event, device UUID, description, source address and key-protection levels. |
Sessions answer which device used an address at a given time. This is a record of observed bindings, not a full flow log or a history of every policy change within a session.
Audit events are stored in PostgreSQL. Apply access controls and retention to the database and its backups: they contain owner and network-address history, tokens and pending challenge state.
Expired sessions close during requests and through session reap; the shipped
systemd timer runs it every 5 minutes. A separate prune timer removes old
closed sessions, audit and completed tokens. The operator sets retention;
automatic deletion defaults to disabled.
10.2. Failure behavior#
| Event | Behavior |
|---|---|
| Broker or database unavailable | No new decisions. The gateway does not refresh grants from old responses; installed elements expire by timer. |
| Device agent stops proving | The server session expires; fresh gateway state removes its grants. |
| TPM continuity proof fails, mTLS still works | The verified level falls; policy reevaluates access. |
| Private key lost | mTLS fails. Re-enrollment is required, not ordinary renewal. |
| New policy file invalid or unreadable | Loaded policy is retained and an error logged. The cache does not survive process restart. |
| No policy ever loaded | The broker returns policy_unavailable, not a normal policy refusal. |
| nftables application fails | The gateway reports the failure and requests full state again; existing timers do not become permanent grants. |
| Device blocked | Authenticated operations are refused; grants disappear from fresh gateway state. |
| Several devices claim one address | The conflicting address receives no grants; the conflict is sent to the gateway and audited. |
Distinguish a saved database decision, a successful gateway response and actual kernel enforcement. Acceptance requires traffic tests: allowed destinations work, forbidden destinations are blocked, reducing rights interrupts existing flows and an unrefreshed grant expires.
10.3. Availability and server protection#
The current working scenario is one routing gateway per segment. Persisted MAC bindings can be restored after restart or passed to a standby gateway, but do not provide conntrack synchronization, active-node election or seamless connection transfer.
These properties describe a gateway with a loaded ruleset. Before its first successful state fetch after a cold boot, protection depends on the base firewall and forwarding configuration. Acceptance must include a gateway reboot with the broker unreachable; timer behavior alone does not verify that window.
Broker operation assumes one active configuration authority. Several processes sharing a database do not by themselves keep YAML policy, CA keys and configuration consistent or coordinate failover.
The broker is intended for a private network behind nginx. nginx requests
client certificates optionally to allow initial enrollment; for other
operations, the broker requires verification result SUCCESS. nginx rejects
a presented untrusted certificate before forwarding the request.
The shipped configuration limits bodies to 16 KiB and separately rate-limits ordinary requests and frequent gateway polling. Limits must fit agent cadence: applying enrollment limits to a gateway can stop access refresh for a segment.
The broker process can access the CA key. Compromise of this boundary can issue device identities, change policy and alter access state. An HSM or separate signing service is not part of this deployment. Placement in a private network does not remove that risk.
11. Agent updates#
Updates affect access: a broken agent stops proving sessions, and a minimum version in posture policy can exclude lagging devices. Package release, delivery and failure recovery must therefore be considered together.
The broker reads agent_release_file and may return agent_update in a proof
response. It selects packages by platform, architecture and installation
format, and devices by a stable cohort derived from their UUIDs. Increasing
share expands the cohort without reshuffling devices already included.
role:enforcer uses a separate enforcer_share, zero by default.
Offers contain an Ed25519 signature from the release key. It covers version, OS, architecture, format and package SHA-256. Download URL and rollout share are not signed: they select delivery location and participants, not authorized package content. The broker can withhold offers or downloads but cannot authorize a substituted package without the release key.
The agent verifies the offer, downloads the package, checks its digest and uses the native installer. Application defaults to enabled for verified offers and requires a trusted release key. On macOS, a separate privileged helper installs packages for the per-user agent.
Normal updates follow a successful proof, leaving lease time for installation and restart. The agent limits attempts per version and persists them before starting the installer. Subsequent measurements report the new version back to the broker.
If an agent cannot prove a session because of an unsupported contract version
or an undecodable response, it obtains the same signed offer through the stable
GET /v1/device/update/ endpoint. This endpoint requires the device certificate
and does not check the contract version (protocol §4d).
An older version offer does not by itself authorize rollback. It requires
signed rollback_from, bounding the newest version it may replace. Downgrade
also depends on the native installer; signatures do not override MSI or other
package-format restrictions.
The operator controls cohort expansion and pauses using release status and
device versions. Before raising a required policy version, provide delivery and
recovery paths; at_least alone is not a rollout.
Procedures are in Maintenance and agent documentation.
12. Code and document map#
| What to study | Source |
|---|---|
| Active HTTP routes | routes.py |
| Authentication, enrollment and decisions | controllers.py |
| Device and session storage | models.py |
| CSR signatures, certificate issuance and TPM evidence | ca.py, attestation.py |
| Policy format, loading and decisions | model.py, source.py, decide.py |
| Posture evaluation and nftables compilation | posture.py, compile.py |
| Conditional gateway requests | dataplane.py |
| TLS configuration and trusted headers | nginx configuration |
| Configuration values and CLI | config.py, cli.py |
| Update offers | release.py |
| Rule application and package installation | internal/enforce, internal/update |
| Contract and message examples | Protocol, golden messages |
| Installation and acceptance | Deploying, Checks, macOS |