Enable SSH on a Cisco Switch¶
Getting from console-only access to working SSH on a Catalyst switch, including connecting from a modern client — which is its own obstacle on older IOS. Example model: a Catalyst 2960, but the sequence is the same across the fixed-config family.
This is the "turn it on" procedure; Security Hardening covers locking it down afterwards.
Do this from the console, and keep that session open until you have proved a fresh SSH login works.
transport input sshdisables Telnet the moment it's applied — if anything in the sequence is wrong, the console is the only way back in.
Prerequisites¶
SSH is the last step of commissioning, not the first. Before the sequence below will get you anything, the switch needs:
- Console access — a USB-to-serial adapter at 9600 baud. See Device Setup Fundamentals.
- A hostname other than
Switch, and a domain name — key generation refuses without both. - A management IP the client can actually reach, plus
ip default-gatewayif the client is on another subnet. This is the step most often missed on a factory-fresh switch: everything below can succeed and SSH still be unreachable because the switch has no address. See Device Setup Fundamentals — Management IP, which also covers the SVI autostate trap: the management SVI stays down until the VLAN exists and a live port carries it.
On a brand-new switch, work through Step-by-Step Switch Configuration first — it ends by handing over to this page.
Configuration Sequence¶
Cisco IOS — order matters (see below):
Switch> enable
Switch# conf t
Switch(config)# hostname SW01
SW01(config)# ip domain-name lab.local
SW01(config)# username admin secret <password>
SW01(config)# enable secret <different-password>
SW01(config)# crypto key generate rsa modulus 2048
SW01(config)# ip ssh version 2
SW01(config)# line vty 0 4
SW01(config-line)# transport input ssh
SW01(config-line)# login local
SW01(config-line)# end
SW01# copy running-config startup-config
Don't skip
enable secret, or SSH gets you halfway in. An SSH session lands in user EXEC (SW01>), andenablerequires a password that doesn't exist yet — so it fails, with no way to fix it over SSH. The console is unaffected (a switch with no enable secret lets the console straight into privileged mode), which is exactly why this is recoverable only from the console. Set it while you're already there.
What Each Step Does¶
hostnameandip domain-namecome first because the key generation needs them. RSA keys are named afterhostname.domain, socrypto key generate rsarefuses to run while the hostname is still the defaultSwitchor no domain name is set. This is the single most common reason the sequence fails partway.- The prompt changes when you set the hostname —
Switch(config)#becomesSW01(config)#. That is your confirmation it took. crypto key generate rsaexecutes immediately rather than just storing config — it prints progress for a few seconds. It's the only command here that does something. If keys already exist, IOS asks whether to replace them; answering yes invalidates the host key, so existing clients will warn about a changed fingerprint.username … secretcreates the accountlogin localwill authenticate against. Without it,login localaccepts no one and you are locked out of vty entirely.line vty 0 4drops you into line configuration mode (SW01(config-line)#) — which is why the next two commands are indented. They apply to those five vty lines, not globally. Some platforms have more (line vty 0 15); check withshow run | section line vtyand configure all of them, or the unconfigured ones remain Telnet-capable.endreturns to privileged EXEC from any depth —exitonly backs up one level.-
The save is not optional. Everything above lives in RAM until
copy running-config startup-config. Reboot without it and SSH access is gone. -
enable secretis separate from the login password. The account gets you a session; the enable secret gets you privileged mode. See Passwords & Privilege Levels for the password types and for theprivilege 15shortcut that skips theenablestep entirely.
Choose real passwords.
login localplus a weak secret is an internet-facing-grade mistake on any switch reachable beyond a lab.
Verify on the Switch¶
Cisco IOS — privileged EXEC:
! Should report "SSH Enabled - version 2.0"
show ip ssh
! Active SSH sessions, once you connect
show ssh
! Confirm the vty lines accept only SSH
show run | section line vty
! Confirm an enable secret exists (look for "enable secret 5 $1$...")
show run | include enable secret
Connecting From a Modern Client¶
Older IOS offers only SHA-1 host keys and legacy key exchanges, which current OpenSSH refuses by default. Expect the plain command to fail:
ssh admin@<switch-ip>
with no matching host key type found. Their offer: ssh-rsa, or no matching key exchange method found.
The algorithms are still compiled into OpenSSH — just not enabled — so re-enable them per-connection. The + prefix appends to the defaults rather than replacing them:
On the management machine (macOS/Linux):
ssh -o HostKeyAlgorithms=+ssh-rsa \
-o PubkeyAcceptedAlgorithms=+ssh-rsa \
-o KexAlgorithms=+diffie-hellman-group14-sha1 \
admin@<switch-ip>
If it then complains about ciphers, add -o Ciphers=+aes128-cbc.
Make It Permanent¶
Rather than retyping that, put it in ~/.ssh/config on the management machine — then plain ssh sw01 works:
Host sw01
HostName <switch-ip>
User admin
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
KexAlgorithms +diffie-hellman-group14-sha1
Ciphers +aes128-cbc
Scope these to the specific hosts that need them, as above — never globally under Host *. They are deliberately-disabled legacy algorithms, and weakening every SSH connection to accommodate one old switch is a poor trade.
Prove the scoping rather than assuming it — ssh -G prints the fully-resolved settings for a host without connecting:
! Should list ssh-rsa, diffie-hellman-group14-sha1, aes128-cbc
ssh -G sw01 | grep -iE 'hostkeyalgorithms|kexalgorithms|ciphers'
! Same check against any normal host — should list none of them
ssh -G github.com | grep -iE 'hostkeyalgorithms|kexalgorithms|ciphers'
The file must be mode 600 (chmod 600 ~/.ssh/config); OpenSSH ignores a config that others can write.
Restrict Access¶
Once SSH works, limit which addresses may reach it — the full treatment is in Security Hardening:
Cisco IOS — global configuration mode (conf t):
access-list 1 permit 10.0.99.0 0.0.0.255
line vty 0 4
access-class 1 in
If the switch uses AAA,
aaa new-modelsupersedeslogin local— authentication then follows the AAA method lists instead, and a list pointing at an unreachable server with no local fallback locks out every vty line.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
% Please define a hostname other than Switch |
Key generation attempted before hostname |
Set hostname, then retry |
% Please define a domain-name first |
No ip domain-name |
Set it, then retry |
| Connection refused | SSH never started (key generation failed), or vty lines don't permit SSH | show ip ssh; show run \| section line vty |
no matching host key type… ssh-rsa |
Modern OpenSSH rejects SHA-1 host keys | The -o options above |
no matching key exchange method |
Same, for the key exchange | -o KexAlgorithms=+diffie-hellman-group14-sha1 |
| Password rejected for a valid account | login local set but no username exists, or AAA is intercepting |
Create the user, or check show run \| include aaa |
Connects, but enable is refused |
No enable secret set — and it can't be fixed over SSH |
Set it from the console (see above); the console reaches privileged mode without one |
| Works now, gone after reboot | Config never saved | copy running-config startup-config |
| No response at all from the client | No IP route to the management subnet, or a VPN client on the management machine capturing that traffic | Check routing first; disconnect the VPN and retest |
Related¶
- Security Hardening — restricting SSH, AAA, and the rest of the hardening pass
- Device Setup Fundamentals — console access and out-of-band vs in-band management
- Cisco IOS CLI Reference
- Configuration Management & Recovery — saving, backups, and password recovery if you do lock yourself out