Skip to content

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 ssh disables 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-gateway if 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>), and enable requires 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

  • hostname and ip domain-name come first because the key generation needs them. RSA keys are named after hostname.domain, so crypto key generate rsa refuses to run while the hostname is still the default Switch or no domain name is set. This is the single most common reason the sequence fails partway.
  • The prompt changes when you set the hostnameSwitch(config)# becomes SW01(config)#. That is your confirmation it took.
  • crypto key generate rsa executes 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 … secret creates the account login local will authenticate against. Without it, login local accepts no one and you are locked out of vty entirely.
  • line vty 0 4 drops 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 with show run | section line vty and configure all of them, or the unconfigured ones remain Telnet-capable.
  • end returns to privileged EXEC from any depthexit only 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 secret is 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 the privilege 15 shortcut that skips the enable step entirely.

Choose real passwords. login local plus 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-model supersedes login 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