Skip to content

Security Hardening

Security Checklist

Before putting a switch into production, verify:

  • All unused ports shutdown
  • Port security enabled on access ports
  • SSH only (no Telnet)
  • BPDU Guard on access ports
  • DHCP Snooping enabled
  • Dynamic ARP Inspection enabled
  • Storm control configured
  • ACLs for VLAN isolation
  • Logging to syslog server
  • Spanning tree priority set (intentional primary)
  • Backup power verified
  • Cabling labeled and tested

Each item is covered in the same order below.

All command blocks on this page are Cisco IOS, entered from global configuration mode (conf t) unless noted — see Cisco IOS CLI.

Disable Unused Ports

Reduce attack surface. Create a dedicated "blackhole" VLAN first — on many IOS versions, assigning a port to a VLAN that doesn't yet exist in the VLAN database will fail or behave inconsistently:

vlan 999
 name UNUSED

interface range Gi0/1 - 10
 shutdown
 switchport access vlan 999

Port Security

Prevent MAC table flooding attacks:

interface range Gi0/1 - 47
 switchport mode access
 switchport port-security
 switchport port-security maximum 2
 switchport port-security violation shutdown

Passwords & Privilege Levels

A switch has several independent passwords, and confusing them is the usual cause of "I'm logged in but can't do anything":

Credential Controls Set with
Line password (console/vty) Access to that line, with no username line con 0password <pw> + login
Local user Username-based login, used by login local and SSH username admin secret <pw>
Enable secret Privileged EXEC (SW01#) — separate from whatever got you a session enable secret <pw>

Cisco IOS — global configuration mode (conf t):

! Local account for SSH/vty login
username admin secret <password>
! Privileged mode — a DIFFERENT password from the account above
enable secret <different-password>
! Obscure any remaining type-7 passwords in the config
service password-encryption

enable secret vs enable password

Always enable secret. The older enable password stores the value as type 7, a reversible Cisco-proprietary encoding that any online decoder reverses in seconds — it is obfuscation, not encryption. enable secret stores a hash instead. If both are configured, enable secret takes effect and the enable password line just sits in the config leaking a recoverable password — remove it with no enable password.

service password-encryption only applies type-7 encoding to other plaintext passwords in the config (line passwords, and so on). It raises the bar against someone reading a show run over your shoulder and nothing more. It is not a substitute for using secret forms.

Platform note: on Catalyst switches running IOS 15.2 and earlier — including the 2960 and 3560-CX families — enable secret produces a type 5 (MD5) hash, and that's the strongest available. The stronger type 8 (PBKDF2-SHA256) and type 9 (scrypt) hashes, and the enable algorithm-type command that selects them, were introduced in IOS 15.3(3)M and do not exist on these platforms. Check with enable algorithm-type ? before assuming.

Privilege Levels

IOS has privilege levels 0–15. A fresh login lands at level 1 (user EXEC, SW01>); enable raises you to level 15 (privileged EXEC, SW01#). Intermediate levels can be built for role-based access, assigning specific commands to a level.

The practical shortcut — give the account level 15 directly, and SSH drops you straight into privileged mode with no enable step:

username admin privilege 15 secret <password>

Convenient for a single-admin network, and it sidesteps the trap where SSH works but enable doesn't. The trade-off is that it removes the second gate: anyone who obtains that one password has full control immediately, and there's no separate enable secret standing in the way. Keep the two-step model where more than one person has access.

SSH Management Only

Secure remote access, restrict to management VLAN. For the full walkthrough of turning SSH on from a console-only switch — including connecting from a modern client, which older IOS makes awkward — see Enable SSH on a Cisco Switch.

hostname SW1
ip domain-name example.lan
username admin secret <strong-password>
crypto key generate rsa modulus 2048
ip ssh version 2
line vty 0 4
 transport input ssh
 login local

access-list 1 permit 10.0.99.0 0.0.0.255
line vty 0 4
 access-class 1 in

Verify you can SSH in (show ip ssh, test login) before closing your console session.

AAA (Authentication, Authorization, Accounting)

The login local model above is per-line authentication — fine for a few switches with local accounts. AAA is IOS's framework for doing this centrally: authentication (who are you), authorization (what may you do — privilege level, which commands), accounting (what did you do — session/command logs to a server). It's enabled with aaa new-model, which replaces the per-line model with method lists — e.g. "try the TACACS+ server, fall back to local accounts if unreachable."

When it's worth it: many devices and many admins — one central account database (typically TACACS+ for network gear), instant revocation, per-command audit trails. On a small network, AAA pointing at local accounts is mostly ceremony — with one exception: some features require it regardless, notably the SCP server (ip scp server enable silently fails every transfer without AAA authorization configured).

The minimal local-accounts form those features need:

Cisco IOS — global configuration mode (conf t):

aaa new-model
aaa authentication login default local
aaa authorization exec default local

Caution: aaa new-model takes effect immediately and changes how every vty line authenticates. Keep your console session open and verify a fresh SSH login works before disconnecting — a method list pointing at an unreachable server with no local fallback locks out everything but the console.

BPDU Guard

Prevent rogue switches on access ports (see Spanning Tree Protocol Fundamentals for what a BPDU actually is and why an access port sending one is a red flag):

interface range Gi0/1 - 47
 spanning-tree bpduguard enable
 spanning-tree portfast

Never on uplinks: Do not apply port security, PortFast, or BPDU Guard to uplink/trunk ports (Gi0/48 in these examples — the same port trusted for DHCP snooping and DAI below). Port security's MAC limit and BPDU Guard will errdisable an uplink, cutting the switch off from the network.

DHCP Snooping

Block rogue DHCP servers (see DHCP Fundamentals for the Discover/Offer/Request/Ack process this is protecting):

ip dhcp snooping
ip dhcp snooping vlan 10,20,30

interface Gi0/48
 ip dhcp snooping trust

Dynamic ARP Inspection

Prevent ARP spoofing attacks:

ip arp inspection vlan 10,20,30
ip arp inspection validate src-mac dst-mac ip

interface Gi0/48
 ip arp inspection trust

Dependency: DAI validates ARP packets against the DHCP snooping binding table, so DHCP Snooping must be enabled first (as shown above) for dynamic validation to work. Without it, DAI has no bindings to check against and will drop legitimate ARP traffic on untrusted ports.

Caution: Statically addressed devices (lighting consoles, Dante/sACN nodes) never send DHCP requests, so they have no snooping binding — DAI will drop all their ARP traffic and silently cut them off. Permit each one explicitly with an ARP ACL:

arp access-list STATIC_DEVICES
 permit ip host 10.0.10.50 mac host aaaa.bbbb.cccc

! Apply the ACL on the VLANs where the static devices live
ip arp inspection filter STATIC_DEVICES vlan 10

Storm Control

Prevent broadcast storms from misbehaving devices:

interface range Gi0/1 - 48
 storm-control broadcast level 10
 storm-control multicast level 10
 storm-control action trap

Caution: Storm control drops multicast above the rising threshold regardless of the action setting — on VLANs carrying multicast traffic (Dante, sACN) this can silently kill it. See Dante & sACN on the Network before applying storm-control multicast to those VLANs.

Platform note: the level value's accepted format varies by switch model — many Catalyst platforms expect a decimal percentage (level 10.00) and reject a bare integer, while others accept either. Some platforms use level pps <low> <high> instead of a percentage. Check storm-control ? on your specific hardware before applying.

Access Control Lists

Enforce VLAN isolation policies (ACLs can also filter by TCP/UDP port for more granular rules than IP-only):

ip access-list extended GUEST_POLICY
 deny ip 10.0.20.0 0.0.0.255 10.0.10.0 0.0.0.255
 permit ip any any

interface Vlan20
 ip access-group GUEST_POLICY in

Logging & Alerts

Monitor for attacks and violations:

logging 10.0.99.5
logging source-interface Vlan99
logging trap informational

snmp-server community <read-only-string> RO
snmp-server host 10.0.99.5 version 2c <read-only-string>
snmp-server enable traps

Replace <read-only-string> with a real, non-default SNMP community string — never leave it as public.

Spanning Tree Priority

Don't leave root bridge election to chance (lowest MAC address wins by default) — deliberately set an intentional, known primary so the topology is predictable. See Spanning Tree Protocol Fundamentals for how root election works:

! On the intended primary switch:
spanning-tree vlan 10 root primary
! On the designated backup switch:
spanning-tree vlan 10 root secondary

What a Fleet Audit Actually Finds

Tally from auditing four in-service Catalyst access switches (3560-CX ×2, 9200CX, 3850) against this page, 2026-08-24. The point: the per-port hardening was universally right, and every finding was a global setting that drifted per-switch because it was set by hand.

Consistently right on all four:

  • Rapid-PVST + extend system-id, BPDU guard with errdisable recovery
  • SSH v2 only on the VTYs, per-port storm-control/portfast/power inline never on user ports
  • Trunks with a dedicated unused native VLAN

Found drifting on at least one switch:

Finding Fix
No enable secret + unprotected line con 0 — walk-up full access enable secret + login local on con 0
ip http server / secure-server left on no ip http server / no ip http secure-server
no service password-encryption service password-encryption
VLANs only in vlan.dat (VTP server) — a config restore onto a wiped switch leaves trunks pointing at nothing vtp mode transparent, VLANs in the config
Smart Call Home phoning the vendor no service call-home (IOS-XE)
No NTP, no timezone, no logging buffered ntp server …, clock timezone, logging buffered 16384
One switch with no config backup at all back it up before touching anything else

The cure is a generated day-0 baseline applied at commission time rather than hand-set globals: one file carrying every line above plus a deliberate STP priority per role (24576 core / 61440 access — see Spanning Tree Priority), applied with configure replace so a redeploy is also a reset.

Physical & Operational Checks

Not everything on the checklist is a CLI command — verify these before go-live too:

  • Backup power — confirm the switch is on UPS/generator-backed power, and that the UPS itself has been load-tested recently, not just plugged in
  • Cabling labeled and tested — every run identified at both ends and verified for link speed/duplex, so a future fault doesn't turn into a physical cable hunt

Verify It Took

Applying the commands is not proof the features are running — several of the failure modes above are silent. Spot-check each one before go-live:

Cisco IOS — privileged EXEC:

show port-security
show ip dhcp snooping binding
show ip arp inspection vlan 10
show storm-control
show spanning-tree root
show ip ssh