Skip to content

Restrict non-compliant devices with endpoint posture

You run an endpoint agent (everwas, formerly OpenRMM) that assesses each machine’s compliance (disk encryption, firewall, antivirus) and you want a device that fails a check you care about to be moved to a remediation VLAN at 802.1X time instead of joining the network normally.

This builds on 802.1X in monitor mode: the RADIUS decision path already knows how to assign a remediation VLAN, and the security model covers how these access-control decisions fail safe. Posture ingress supplies the missing input, which check results a machine is reporting, so that path can act.

The whole feature is off by default and gates nothing until you name the checks that should restrict access. Turning it on changes no behavior until you opt a check into gating, and enforcement is per-switch on lab gear first, exactly like the rest of the access-control plane.

  • 802.1X monitor mode working against a lab switch.
  • A remediation VLAN id on that switch (a network with enough reach to fix the device: patch server, agent endpoints, DNS).
  • The everwas agent publishing posture reports to your NATS bus. The agent owns identity (a device UUID) and the checks; l2trace owns which failures restrict access.

1. Decide which checks gate, starting with none

Section titled “1. Decide which checks gate, starting with none”

A check gates when its name or its category is named in POSTURE_GATING_CHECKS (comma-separated). Matching on the category means a policy written against encryption keeps covering the machine when the agent adds a fifth encryption check.

Start empty and observe. With no gating checks named, ingress records every report but restricts nothing:

.env
POSTURE_INGRESS_ENABLED=true
POSTURE_GATING_CHECKS= # empty: nothing gates yet (fail-open)

Once you have watched real reports land (step 4), name the checks or categories that should restrict access:

Terminal window
POSTURE_GATING_CHECKS=encryption,firewall

An absent or not-assessed gating check never gates. Only a present, fresh fail on a gating check remediates. A newly added gating check therefore has no effect until machines report it, which is correct: it must not remediate the fleet the instant you edit the policy.

Terminal window
RADIUS_REMEDIATION_VLAN=999 # the "fix me" network on your lab switch

If this is unset, a failing device is simply accepted with no VLAN change (fail open, no move), so a missing VLAN never cuts a device off by accident.

l2trace consumes reports from a dedicated NATS subject, deliberately off the per-device event bus (posture is keyed on the agent’s device UUID, not l2trace’s integer device id):

Terminal window
POSTURE_NATS_SUBJECT=l2trace.posture # the default

Configure the everwas publisher to send each report, as the agreed JSON envelope, to that subject on the same NATS the reconciler uses. l2trace derives an idempotency key from the report content, so an at-least-once bus redelivering the same report is a no-op.

4. Restart and confirm reports are landing

Section titled “4. Restart and confirm reports are landing”

Ingress runs as part of the reconciler service (single-host) when enabled:

Terminal window
docker compose up -d reconciler
docker compose logs -f reconciler | grep posture

You want lines like posture ingest running: subject=l2trace.posture gating=... and, per report, posture: device=<uuid> macs=N checks=M ingested. Nothing is enforced yet: monitor mode records the move it would make on the auth event (would remediate ...) and changes nothing on the wire.

A posture assessment older than this reads as “not assessed”, which never gates. Set it comfortably wider than the agent’s collection cadence so a laptop that sleeps over lunch does not flap:

Terminal window
RADIUS_POSTURE_FRESHNESS_SECONDS=7200 # 2h over a 30m cadence tolerates 3 misses

Erring long is the safe direction: a slightly old posture still informs, a too-tight window just produces needless not-assessed.

Only after you have watched the monitor-mode discrepancies match what you expect, turn a lab switch’s port to enforcing 802.1X. A device failing a gating check now receives the remediation VLAN; a device that passes, or whose posture is stale or absent, is unaffected. Never enforce on a production port until the lab behaviour is proven.

Every layer is arranged so a fault under-permits rather than cutting a device off or, worse, silently letting a failing one through unremediated:

  • Off by default. With POSTURE_INGRESS_ENABLED=false the store stays empty and the seam returns “no assessment”, so remediation never fires.
  • Empty gating set gates nothing. Gating is opt-in per deployment.
  • Absence and not-assessed never gate. Only a present, fresh fail does.
  • Unknown status degrades to not-assessed. If a wire regression ever emits a status l2trace does not recognise, it is read as not-assessed, never a gating fail, so a machine is never remediated for a check that never ran.
  • Stale posture reads as not-assessed (the freshness window).
  • A posture-store read error fails open: the RADIUS decision proceeds as if no assessment were held, rather than breaking the auth.