Authorize with Nautobot policies
When you need this
Section titled “When you need this”Standalone, l2trace decides 802.1X access from its own MAC-authority view plus the operator approvals in its local store (see Stand up 802.1X in monitor mode). Integrated mode hands the authorization policy to Nautobot: which identity is allowed, onto which VLAN, with which complete RADIUS response (Filter-Id, downloadable ACL, Session-Timeout, SGT, verbatim AV-pairs). Reach for it when:
- policy is authored and reviewed by a network team in Nautobot, not per-device in l2trace, and you want one source of truth for intent;
- a device’s authorization depends on identity (a user credential, or a device MAC), a category (printer vs workstation vs medical-device), a time-of-day window, or a required auth method, not just its MAC;
- you want the full RADIUS profile, not only a VLAN.
It stays optional and off by default. With the engine off, l2trace does 802.1X exactly as before.
How the decision works (read this first)
Section titled “How the decision works (read this first)”On every authentication, with the engine on, l2trace:
- Evaluates Nautobot’s ordered policy locally. A periodic sync pulls the
active policy set into l2trace; the auth decision reads that local cache, never
a live Nautobot call, so a switch never waits on Nautobot. Policies are
first-match by ascending
weight; a policy matches when every condition it sets matches the request (a condition it leaves blank is “don’t care”). - Renders the matched profile. A permit becomes an Access-Accept carrying the
profile’s full attribute set. A policy’s downgrade gate (
required_auth_method) or an explicit reject-decision profile becomes an Access-Reject under enforcement. - Falls back when no policy matches. l2trace uses its existing local decision (MAC Authentication Bypass + operator approvals). No match is never a denial.
- Applies monitor-first LAST. In monitor mode the reply is always Access-Accept with no attributes; l2trace only records the profile it would have assigned. A policy can never change the network in monitor mode.
That ordering is deliberate: the network-changing decision is computed, then the monitor-first gate nulls it, so no policy path can route around the safety check.
Prerequisites
Section titled “Prerequisites”- Standalone 802.1X working first (monitor-mode how-to). Prove the pipeline before adding Nautobot.
- The l2trace SSoT app deployed on your Nautobot, exposing the authorization
models (
AuthorizationProfile,AuthorizationPolicy,TimeSchedule,DeviceCategory) under/api/plugins/ssot-l2trace/. l2trace reads these; it never writes them.
1. Point l2trace at Nautobot
Section titled “1. Point l2trace at Nautobot”l2trace reuses the same Nautobot connection as the rest of its Nautobot integration:
| Env var | Meaning |
|---|---|
NAUTOBOT_URL | Base URL of your Nautobot (e.g. https://nautobot.example.net) |
NAUTOBOT_TOKEN | An API token with read access to the SSoT plugin endpoints |
NAUTOBOT_VERIFY_TLS | true in production |
The token is read-only intent-consumption; l2trace has no write scope on these models.
2. Turn on the policy engine
Section titled “2. Turn on the policy engine”| Env var | Default | Meaning |
|---|---|---|
RADIUS_POLICY_ENGINE_ENABLED | false | Master switch for integrated mode |
RADIUS_POLICY_REFRESH_SECONDS | 300 | How often the local policy cache is refreshed from Nautobot |
Set RADIUS_POLICY_ENGINE_ENABLED=true and restart the RADIUS service. On start
it does one initial load, then refreshes on the interval. Until the first load
succeeds the engine is inert and l2trace uses its local decision, so a slow or
unreachable Nautobot at boot never blocks authentication.
3. What the engine matches on
Section titled “3. What the engine matches on”Each request is turned into a runtime context and matched against the policy conditions:
| Context fact | Source |
|---|---|
username | the EAP/MAB identity |
mac | the supplicant MAC (Calling-Station-Id) |
auth_method | mab on the MAB path, peap on the PEAP path |
machine_auth | best-effort from the inner identity (an AD computer account) |
device_category | the endpoint’s Nautobot role, folded to a canonical slug |
| time-of-day | evaluated against a policy’s TimeSchedule in that schedule’s timezone |
required_auth_method is a downgrade gate: on a matched policy, if the
presented method is not the required one, l2trace denies (under enforcement)
regardless of any permit, which stops a device that must do 802.1X from slipping
in via MAB.
Regex conditions
Section titled “Regex conditions”Five of those conditions have an opt-in regex twin, for when an exact value won’t do — an OUI prefix, a naming convention, a family of identity groups:
| Equality condition | Regex twin |
|---|---|
username | username_regex |
mac_address | mac_regex |
identity_group | identity_group_regex |
device_category | device_category_regex |
auth_method | auth_method_regex |
Each is left unset by default (unset = “don’t care”), so adding the fields is fully back-compatible — a policy that sets none behaves exactly as before. A few things about how they match:
- Full-match, not search.
^b8:57:d6:.*matches a MAC in that OUI; a bareb8:57:d6matches nothing, because the whole value must match. Anchor with.*deliberately. - Case-sensitive by default. The equality path folds case; the regex path
does not. Write
(?i)at the front of the pattern when you want case-insensitivity. mac_regexsees the canonical MAC. Both sides are normalized to lowercase colon form first, so you write the pattern againstb8:57:d6:...regardless of how the switch put the MAC on the wire.- It’s an AND, like every other condition. A
_regextwin is an independent condition, not an alternative to its equality sibling — set both and both must pass. Express OR by authoring two policies.
The patterns are compiled with RE2 (google-re2), the same engine the
TACACS+ command sets use, for the same reason: RE2 matches in guaranteed
linear time, so an operator’s pattern can never catastrophically backtrack
and stall the 802.1X auth loop. A pattern RE2 won’t accept — it rejects
backreferences and lookaround — is treated as un-evaluatable, i.e. a
non-match, and logged. That fails in the safe direction: a broken pattern
under-permits (the policy simply doesn’t match, and evaluation falls through
to the next policy or the local store), it never silently permits.
4. Keep monitor mode on while you validate
Section titled “4. Keep monitor mode on while you validate”The engine respects the exact same safety model as the rest of 802.1X. Leave
RADIUS_MONITOR_MODE=true while you watch what the policies would do:
curl -s "https://l2trace.example.net/api/auth-events" | jq '.[] | {mac, result, monitor_mode, discrepancy, reason}'In monitor mode every reason reads like monitor mode; would <apply profile X> (no change). Nothing on the wire changes. This is your before-you-enforce
worklist: the devices whose Nautobot policy disagrees with where they are today.
5. Arm enforcement
Section titled “5. Arm enforcement”Enforcement is unchanged and still takes all three independent switches:
RADIUS_MONITOR_MODE=false and RADIUS_ENFORCEMENT_PERMITTED=true and the
individual switch’s device_collector.extras.radius_enforce=true. Only then does
a matched permit assign its VLAN/profile and a gate-deny become a real
Access-Reject. Prove it on lab switches first.
The fail-safes (why turning this on is low-risk)
Section titled “The fail-safes (why turning this on is low-risk)”- Nautobot outage: the policy cache serves the last good set indefinitely; a failed refresh keeps the prior beliefs and never reads as “deny everyone.”
- No matching policy: falls back to the local MAB/approval decision, never a denial.
- Any error in the policy path (a DB blip, an unresolvable endpoint): fails open to Access-Accept with no change, and never drops the reply.
- Engine off: byte-for-byte the standalone behavior.
The deeper “what happens when Nautobot is down” reasoning, and the posture-required exception, live in 802.1X with Nautobot, and what happens when Nautobot is down.
See also
Section titled “See also”- Stand up 802.1X in monitor mode — the standalone base this builds on.
- 802.1X with Nautobot resilience — the cache-and-decide-locally model and outage behavior.
- The engine + readers:
src/l2trace/radius/policy.py,src/l2trace/collectors/authz_reader.py,src/l2trace/radius/policy_cache.py.