Skip to content

Bring up TACACS+ on a lab switch

  • “I want to confirm a real Catalyst actually authenticates against l2trace before I stand up AD and the database.”
  • “The mock NAS in the test suite passes. Does a physical switch agree?”
  • “I’m bringing up a lab switch and don’t want to provision LDAP and Postgres just to test the handshake.”

The test suite proves l2trace is self-consistent — it talks to its own mock NAS and they agree. Only a real switch proves l2trace is RFC-consistent: that a Catalyst’s MD5 pad, its ASCII login dialog, and the way it reads shell-authorization arguments all match l2trace’s wire format. l2trace tacacs-lab exists to close that gap with the least possible scaffolding — no Active Directory, no database, just a network path to the switch.

tacacs-lab runs the same TACACS+ server the production service runs, but wired to in-memory stand-ins instead of the real backends: a static credential map instead of AD, a static shell-profile resolver instead of group lookups, and a database-free accounting sink that logs each record instead of writing the bitemporal store. It holds cleartext test passwords in memory and is explicitly not a production path — production is l2trace tacacs with an identity source, covered in the device-administration how-to.

Enforcement stays off. The shell profile you attach to a --user is computed and logged, not applied: the switch keeps its own default privilege. The tool’s job is to prove the handshake and touch nothing — “the mock NAS proves we are self-consistent; only a Catalyst proves we are RFC-consistent,” and it proves that without changing a single privilege on the box.

Terminal window
l2trace tacacs-lab \
--switch-ip 192.0.2.10 \
--secret 'the-shared-secret-on-the-switch' \
--user test=Passw0rd=15 \
--dialect ios
FlagDefaultDescription
--switch-ip(required)The lab switch’s source IP — the NAS l2trace will accept
--secret(required)The shared secret configured on the switch
--user(required, repeatable)name=password[=priv_lvl], e.g. --user test=Passw0rd=15. At least one required
--listen-addr0.0.0.0Bind address
--port4949Bind port. Port 49 needs root; the default is high so you publish or redirect 49 → 4949
--dialectgenericShell dialect: ios | nxos | arista | generic

The --user parser reads the privilege only as a trailing all-digits field, so a password may itself contain =: --user u=a=b=7 is user u, password a=b, priv 7. A digits-only password with no priv is read as the password, not a level.

On start it logs the switch, bind address, dialect, and users, notes that enforcement is off (profiles are logged, not applied), and reminds you to point the switch at it with a group tacacs+ local method list and to log in with the local account first to confirm the fallback path. Every login and command then prints as an accounting line (tacacs-lab: ACCT ...) — no database needed.

On the lab switch (IOS shown), after the break-glass account is in place and tested:

tacacs server l2trace-lab
address ipv4 <l2trace-host>
key the-shared-secret-on-the-switch
port 49
aaa authentication login default group tacacs+ local
aaa authorization exec default group tacacs+ local

If l2trace listens on 4949, publish or redirect host 49 to it (the same host 49 → container 4949 split the production overlay uses, or the --port 49 bind if you run it as root). Then log in with your --user credential. It authenticates against the static map; the shell profile is computed and logged; accounting scrolls past in the terminal.

A successful lab login proves the three things the mock NAS structurally cannot:

  • the MD5 pad l2trace derives from the shared secret matches what the Catalyst expects, so the obfuscated body decodes;
  • the ASCII login dialog (the START / GETUSER / GETPASS exchange) lines up turn-for-turn with a real supplicant;
  • the switch reads the shell-authorization arguments l2trace sends in the dialect you selected (priv-lvl for IOS, shell:roles for NX-OS/EOS).

Once those agree on real hardware, you can move on to the full service with confidence that the wire format is right — the remaining work is identity and storage, not protocol. This exact runner was validated live against a Catalyst 9300L on IOS-XE 17.15.

tacacs-lab deliberately stops at the handshake. To get AD-backed logins, group-driven privilege and command sets, the bitemporal audit trail, and (eventually, lab-only) enforcement, follow Stand up TACACS+ device administration — the same switch config, pointed at l2trace tacacs with an identity source and database behind it.