> ## Content Index
> Fetch the complete content index at: https://www.pinglabz.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# VTY Access-Class Lockout on IOS XE: SSH and Management VRF
- URL: https://www.pinglabz.com/vty-access-class-ssh-lockout-management-vrf/
- Published: 2026-10-24T21:30:10.000Z
- Updated: 2026-10-24T21:30:10.000Z
- Description: Built in my home lab on IOS XE 17.18: a wrong VTY access-class locked out SSH, then a management VRF did it again with no ACL hits. Both recovered from the console.
- Author: Jaime
- Tags: Network Security, SSH, IOS XE, Troubleshooting, Labs

Locking down the VTY lines is a five-minute change that can cost you an afternoon. One wrong ACL entry and the router still answers ping but refuses every SSH session, including yours. Then, once the access list is fixed, moving management into its own VRF locks you out again, this time without a single ACL hit or log line.

I built this in my home lab to see both lockouts, with real output, and recover each from the console.

## The lab

Built in my home lab with Containerlab: two Cisco IOL nodes running IOS XE 17.18.2 and two Alpine Linux hosts.

- **EDGE1** is the router being hardened. Ethernet0/1 (172.31.40.1/24) faces the NOC segment, and Ethernet0/2 (10.120.0.1/30) is a transit link to CORE1.
- **CORE1** routes the staff LAN, 10.120.7.0/24.
- **noc** (172.31.40.10) is the admin workstation. It is the only host that should reach EDGE1's VTY lines.
- **staff** (10.120.7.20) is an ordinary user on the staff LAN. It should not reach EDGE1's VTY lines.

The console is the safety net. In Containerlab, that is the IOL serial console, and on real hardware it is the console port or a terminal server. Every recovery step below was done there.

![Lab diagram: EDGE1 on IOS XE 17.18.2, NOC host in management VRF MGMT, staff LAN via CORE1, access-class with vrfname MGMT](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/vty-access-class-ssh-lockout-management-vrf-diagram.png)

## Baseline: anyone can reach the VTY lines

The starting point is common: a local user, `login local`, and both Telnet and SSH allowed. IOS XE 17.18 flags Telnet right away, and SSH was not running yet because there was no host key:

```text
EDGE1#show ip ssh | include SSH
SSH Disabled - version 2.0
Please create an RSA key of atleast 3072 bits or an EC key of atleast 384 bits to enable SSH server functionality

```

The problem shows up from the wrong side of the network. A host on the staff LAN could log in to the router over Telnet:

```text
Connected to 10.120.0.1
Username: netops
EDGE1#show users
*  2 vty 0     netops     idle                 00:00:00

```

The log recorded logins from both the NOC host and the staff host:

```text
*Oct 24 20:47:00.588: %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success [user: netops] [Source: 172.31.40.10] [localport: 23] at 20:47:00 UTC Sat Oct 24 2026
*Oct 24 20:47:20.627: %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success [user: netops] [Source: 10.120.7.20] [localport: 23] at 20:47:20 UTC Sat Oct 24 2026

```

## Fault: a subnet mask in the VTY access-class ACL

This is the kind of change that gets pasted from a ticket. It has an access list for the admin subnet, an RSA key, an access-class, and SSH only:

```text
EDGE1(config)#ip access-list standard VTY-ADMINS
EDGE1(config-std-nacl)#permit 172.31.40.0 255.255.255.0
EDGE1(config-std-nacl)#deny any log
EDGE1(config)#crypto key generate rsa modulus 2048
EDGE1(config-line)#access-class VTY-ADMINS in
EDGE1(config-line)#transport input ssh

```

The key command already raised two warnings. On 17.18 the config-mode form is deprecated and hidden, and 2048 bits is below the recommended length:

```text
%This command is deprecated.  Use the command in exec mode insteadThe name for the keys will be: EDGE1.lab.pinglabz.test
 SECURITY WARNING - Module: SSH, Command: crypto key generate rsa ..., Reason: SSH host key uses insufficient key length, Description: SSH with insufficient key length, Remediation: Use SSH RSA host key with a minimum length of 3072 bits for enhanced security

```

SSH still came up (`%SSH-5-ENABLED: SSH 2.0 has been enabled`). From the NOC host, the router answered ping but refused every management session:

```text
64 bytes from 172.31.40.1: seq=0 ttl=255 time=1.379 ms
telnet: can't connect to remote host (172.31.40.1): Connection refused
ssh: connect to host 172.31.40.1 port 22: Connection refused

```

"Connection refused" from a router that is clearly up is the classic sign of an access-class rejecting you. A dead SSH server or a missing key looks the same from the client. Only the router side tells you which one it is.

## Diagnosis: SSH connection refused, ACL read back from console

From the console, `show ip ssh` ruled out the SSH server. It was enabled, with the weak 2048-bit key:

```text
EDGE1#show ip ssh
SSH Enabled - version 2.0
Authentication timeout: 120 secs; Authentication retries: 3
IOS Keys in SECSH format(ssh-rsa, base64 encoded): EDGE1.lab.pinglabz.test
Modulus Size : 2048 bits

```

The access list told the real story:

```text
EDGE1#show ip access-lists VTY-ADMINS
Standard IP access list VTY-ADMINS
    10 permit 0.0.0.0, wildcard bits 255.255.255.0
    20 deny   any log (2 matches)

```

IOS accepted `255.255.255.0` as the wildcard, so line 10 became "any address whose last octet is zero." The NOC host at 172.31.40.10 fell through to the deny, and the log named it:

```text
*Oct 24 20:47:44.157: %SEC-6-IPACCESSLOGS: list VTY-ADMINS denied 172.31.40.10 1 packet 

```

The parser didn't complain about the subnet mask, so the mistake only showed up in the `show` output. Always read the access list back after you write it. Standard and extended ACLs use wildcard masks, not subnet masks.

## Fix: correct wildcard, 3072-bit key, tighter SSH

Still on the console, I generated a 3072-bit key in exec mode with its own label, pointed SSH at it, tightened the timeout and retries, and replaced the broken entry:

```text
EDGE1#crypto key generate rsa general-keys modulus 3072 label EDGE1-SSH
% Generating crypto RSA keys in background ...
EDGE1(config)#ip ssh rsa keypair-name EDGE1-SSH
EDGE1(config)#ip ssh time-out 60
EDGE1(config)#ip ssh authentication-retries 2
EDGE1(config-std-nacl)#no 10
EDGE1(config-std-nacl)#10 permit 172.31.40.0 0.0.0.255

```

The exec-mode command generates the key in the background. Because I pasted the `ip ssh rsa keypair-name` line straight after it, IOS briefly warned that the key did not exist yet. Once generation finished, the result was correct:

```text
EDGE1#show ip ssh | include SSH|Modulus|timeout|SECSH
SSH Enabled - version 2.0
Authentication timeout: 60 secs; Authentication retries: 2
IOS Keys in SECSH format(ssh-rsa, base64 encoded): EDGE1-SSH
Modulus Size : 3072 bits

```

On a production box, wait a few seconds after generating a key before you configure anything that references it.

## Verify: admin in, staff out

SSH from the NOC host now worked, and Telnet stayed refused because the line allows SSH only:

```text
*  2 vty 0     netops     idle                 00:00:00 172.31.40.10
telnet: can't connect to remote host (172.31.40.1): Connection refused

```

The staff host was refused on both router addresses:

```text
ssh: connect to host 10.120.0.1 port 22: Connection refused
ssh: connect to host 172.31.40.1 port 22: Connection refused

```

The router side agreed. The permit counter moved, the deny counter kept climbing, and the SSH log lines showed the admin session from start to finish:

```text
    10 permit 172.31.40.0, wildcard bits 0.0.0.255 (2 matches)
    20 deny   any log (4 matches)
*Oct 24 20:48:25.797: %SSH-5-SSH2_SESSION: SSH2 Session request from 172.31.40.10 (tty = 0) using crypto cipher 'chacha20-poly1305@openssh.com', hmac 'hmac-sha2-256-etm@openssh.com' Succeeded
*Oct 24 20:48:26.140: %SSH-5-SSH2_USERAUTH: User 'netops' authentication for SSH2 Session from 172.31.40.10 (tty = 0) using crypto cipher 'chacha20-poly1305@openssh.com', hmac 'hmac-sha2-256-etm@openssh.com' Succeeded
*Oct 24 20:48:39.999: %SEC-6-IPACCESSLOGS: list VTY-ADMINS denied 10.120.7.20 1 packet 

```

Treat the counters as the source of truth. IOS rate-limits ACL log messages, so a `log` entry does not print one line per attempt. During the run I saw a deny line that was logged later than the attempt that caused it.

## Second lockout: management VRF with zero ACL hits

The access-class now limits who can log in, but the management subnet still shares the global routing table with user traffic. The next step is a management VRF, so I moved Ethernet0/1 into VRF `MGMT`:

```text
EDGE1(config)#vrf definition MGMT
EDGE1(config-vrf)#description out-of-band management
EDGE1(config-if)#vrf forwarding MGMT
% Interface Ethernet0/1 IPv4 disabled and address(es) removed due to enabling VRF MGMT
EDGE1(config-if)#ip address 172.31.40.1 255.255.255.0

```

Moving an interface into a VRF removes its IP address. If you are connected through that interface, the session drops right there, which is another reason to do this from the console. With the address back, the routes sat in the new table:

```text
EDGE1#show vrf
  MGMT                             <not set>             ipv4        Et0/1
C        172.31.40.0/24 is directly connected, Ethernet0/1
L        172.31.40.1/32 is directly connected, Ethernet0/1

```

The NOC host was locked out again:

```text
ssh: connect to host 172.31.40.1 port 22: Connection refused

```

This lockout is worse than the first because it leaves no trace. The ACL counters didn't move, and no deny line was logged:

```text
    10 permit 172.31.40.0, wildcard bits 0.0.0.255 (2 matches)
    20 deny   any log (4 matches)

```

A plain `access-class ... in` applies only to connections that arrive in the global routing table. A connection that arrives in a VRF never reaches that ACL, so it is refused without touching it. Cisco covers the VRF-aware options in its [IOS XE 17 VRF Awareness Access Class Line guide](https://www.cisco.com/c/en/us/td/docs/routers/ios/config/17-x/application-services/b-application-services/m-bba-vrf-aware-access-class-line.html?ref=pinglabz.com).

## Fix: access-class in vrfname MGMT (or vrf-also)

I had expected an `ip ssh vrf` command for this, but IOS XE 17.18.2 on IOL has none. The `ip ssh ?` list has no VRF option. The VRF control is on the access-class:

```text
EDGE1(config-line)#access-class VTY-ADMINS in ?
  vrf-also  Same access list is applied for all VRFs
  vrfname   Access list is applied for given VRFs

```

`vrf-also` applies the same list to connections from every VRF. `vrfname` names the VRF, which is the tighter choice when management lives in exactly one VRF. I used `vrfname MGMT` and kept the global line, so connections in the global table are still filtered:

```text
EDGE1(config-line)#access-class VTY-ADMINS in vrfname MGMT
line vty 0 4
 access-class VTY-ADMINS in
 access-class VTY-ADMINS in vrfname MGMT

```

SSH from the NOC host worked again:

```text
*  2 vty 0     netops     idle                 00:00:00 172.31.40.10
*Oct 24 20:49:48.312: %SSH-5-SSH2_SESSION: SSH2 Session request from 172.31.40.10 (tty = 0) using crypto cipher 'chacha20-poly1305@openssh.com', hmac 'hmac-sha2-256-etm@openssh.com' Succeeded

```

From the staff LAN, the management address was now simply unreachable, because the global table has no route to it. The transit address stayed filtered by the global access-class:

```text
2 packets transmitted, 0 packets received, 100% packet loss
ssh: connect to host 172.31.40.1 port 22: Host is unreachable
ssh: connect to host 10.120.0.1 port 22: Connection refused

```

```text
EDGE1#show ip route 172.31.40.0
% Network not in table

```

## Final VTY and management VRF configuration

I also turned off the built-in HTTP and HTTPS servers and shortened the VTY idle timeout. These are the relevant lines from EDGE1's final running config (the local user is `username netops privilege 15 secret <your-secret>`):

```text
vrf definition MGMT
 description out-of-band management
 address-family ipv4
 exit-address-family
interface Ethernet0/1
 description NOC-MGMT
 vrf forwarding MGMT
 ip address 172.31.40.1 255.255.255.0
no ip http server
no ip http secure-server
ip ssh time-out 60
ip ssh authentication-retries 2
ip ssh rsa keypair-name EDGE1-SSH
ip access-list standard VTY-ADMINS
 10 permit 172.31.40.0 0.0.0.255
 20 deny any log
line vty 0 4
 access-class VTY-ADMINS in
 access-class VTY-ADMINS in vrfname MGMT
 exec-timeout 5 0
 login local
 transport input ssh

```

IOL only has `line vty 0 4`. On hardware with `line vty 5 15` (or more), apply the same access-classes and transport there too. An unprotected high VTY range is an easy gap to miss.

## A safe order for doing this on a live router

1. Make sure you have working out-of-band access first: a console, a terminal server, or a second management path.
2. Generate the key in exec mode (`crypto key generate rsa general-keys modulus 3072 label NAME`), wait for it to finish, then set `ip ssh rsa keypair-name`.
3. Build the ACL with wildcard masks and read it back with `show ip access-lists`.
4. Apply the access-class on every VTY range. If management is in a VRF, add `vrfname` or `vrf-also` (not both; Cisco documents them as mutually exclusive).
5. Test from an allowed host and a denied host, and confirm with ACL counters, not just log lines.
6. Remove Telnet with `transport input ssh` only after SSH has worked from the admin host.
7. Use `reload in 10` as a dead-man switch if you have no console. Cancel it with `reload cancel` once you are back in.

## Key takeaways

- "Connection refused" from a router that answers ping usually means the access-class rejected you. Check the ACL counters from the console.
- IOS accepts a subnet mask as a wildcard without complaining. `permit 172.31.40.0 255.255.255.0` became `permit 0.0.0.0, wildcard bits 255.255.255.0`.
- IOS XE 17.18 wants an RSA key of at least 3072 bits or an EC key of at least 384 bits. A 2048-bit key still enabled SSH on this IOL image, but with a security warning.
- A plain `access-class ... in` doesn't apply to VRF connections. Those were refused with no ACL hit and no log line.
- On IOS XE 17.18.2 (IOL) there was no `ip ssh vrf`. Use `access-class NAME in vrfname VRF` or `vrf-also`.

For the wider checklist, see [hardening the management plane on IOS XE](https://www.pinglabz.com/hardening-management-plane-ios-xe/). For the basic SSH setup, see [how to enable SSH on Cisco devices](https://www.pinglabz.com/enable-ssh-cisco/). For VRF fundamentals, see [VRF-Lite configuration on IOS XE](https://www.pinglabz.com/vrf-lite-configuration-cisco-ios-xe/). If you also want centralized device login, see [AAA with TACACS+ and RADIUS for device administration](https://www.pinglabz.com/aaa-tacacs-radius-device-administration/).