Lab brief & addressing
Topology Open full size ↗
The Mission
R1 is a production router and right now anyone who can reach it can log in over cleartext Telnet with no real authentication. By the end, R1 must accept SSH only, from the trusted subnet only — and you must prove with real sessions that ADMIN gets in over SSH, Telnet is refused, and ROGUE is turned away at the VTY door while it can still ping R1. That last part is the whole lesson: locking the management door must not break the network the router carries.
You'll be able to
- Replace Telnet with SSHv2 and store every credential as a hash, not plaintext.
- Force authentication on the VTY lines with
login local— and explain whyno loginis a back door. - Restrict who can even reach the VTYs with an
access-classACL, so a wrong source IP is dropped before the password prompt.
Before you start
- Finished E05 (you can read
show runsections and gather evidence). - Three IOSv routers: R1 is the device you harden; ADMIN and ROGUE are IOSv test stations that stand in for admin workstations — unlike a VPCS host, an IOS device can actually originate an
sshand atelnetsession, so every proof in this lab is a real login you run, not a screenshot. Onel2switch(SW1) forms the trusted management LAN. - ADMIN sits on the trusted subnet through SW1; ROGUE hangs off R1's second interface on a separate, untrusted subnet. R1 routes between the two, so before you harden anything every station can already ping R1. Hardening must change who can log in without changing who can ping.
Addressing plan
| Device / Interface | Address (/mask) | Role |
|---|---|---|
| R1 Gi0/0 (to SW1) | 10.80.99.1 / 24 | device being hardened |
| R1 Gi0/1 (to ROGUE) | 10.80.66.1 / 24 | gateway for the untrusted subnet |
| ADMIN Gi0/0 | 10.80.99.10 / 24, gw .1 | allowed to reach VTY |
| ROGUE Gi0/0 | 10.80.66.66 / 24, gw .1 | must be denied VTY access |
The one idea behind this lab
The management plane is the path you use to log in to a device — the VTY lines, the credentials, the protocol that carries them. It is not how the router forwards packets; it is how you drive the router. That makes it a prime target: crack the front door and you own the box.
Hardening it is defense in depth on one door, in four independent layers: (1) carry the session over an encrypted transport (SSH, not Telnet); (2) store credentials as hashes so a captured config leaks nothing usable; (3) require authentication on every VTY line (login local); and (4) restrict who may even open a VTY at all (access-class). Each layer survives the failure of the others — that is the whole point.
See & Do Turn Telnet into SSH-only on R1
Type these on R1 exactly. After each block, read the output panel before moving on — the goal is to connect a command to the protection it adds.
Lay the groundwork SSH needs (name, domain, key)
R1> enable R1# configure terminal R1(config)# hostname R1 R1(config)# ip domain name lab.local ! hostname + domain = the key's name R1(config)# crypto key generate rsa modulus 2048
The name for the keys will be: R1.lab.local % Generating 2048 bit RSA keys, keys will be non-exportable... [OK] (elapsed time was 2 seconds) *Jun 29 14:02:11.733: %SSH-5-ENABLED: SSH 1.99 has been enabled
hostname.domain. No hostname or no domain name and crypto key generate is refused. The %SSH-5-ENABLED log line is your proof the key exists and the SSH server woke up.Create hashed credentials and pin SSH to version 2
R1(config)# enable secret S3cret-Enable! ! privileged-mode password, stored hashed R1(config)# username admin privilege 15 secret S3cret! ! local account, also hashed R1(config)# ip ssh version 2 ! refuse the weaker SSHv1 R1(config)# service password-encryption ! masks any remaining type-7 passwords too
secret (not password) means the credential is stored as a one-way hash — a stolen config shows $1$... or $9$..., never the word you typed. privilege 15 drops admin straight into enable mode on login. ip ssh version 2 closes the downgrade path to SSHv1.Lock the VTY lines to SSH and require a real login
R1(config)# line vty 0 4 R1(config-line)# transport input ssh ! drop Telnet entirely R1(config-line)# login local ! authenticate against the username database R1(config-line)# end
transport input ssh alone refuses Telnet; login local alone closes the "press enter to get in" hole. You need both.Read the evidence (this is the skill, not the typing)
R1# show ip ssh
SSH Enabled - version 2.0 Authentication methods:publickey,keyboard-interactive,password Authentication timeout: 120 secs; Authentication retries: 3
Now jump to the ADMIN station (10.80.99.10) and open a real SSH session — an IOS device is its own SSH client:
ADMIN# ssh -l admin 10.80.99.1
Password: ! S3cret! R1# ! privilege 15 -> straight into enable mode
From the same station, plain Telnet is refused — R1 no longer listens on port 23 at all:
ADMIN# telnet 10.80.99.1
Trying 10.80.99.1 ...
% Connection refused by remote host ! transport input ssh -> nothing answers on tcp/23
show ip ssh says SSH Enabled - version 2.0 (encrypted transport, no v1), and the live test shows SSH in / Telnet refused. Because you ran these from an actual device, the refusal is the router's own TCP reset, not a claim — transport input ssh means tcp/23 is simply not open.Now You (guided) Restrict who can reach the VTYs
SSH-only stops the wrong protocol. It does nothing about the wrong source — right now anyone with SSH and a stolen password can try, from anywhere. Close that with an access-class ACL that only lets the management subnet even open a VTY. You write the lines; I'll give you the target.
- Build a standard named ACL
MGMT-INthat permits the management subnet10.80.99.0/24(and nothing else — the implicit deny does the rest). - Apply it to the VTY lines with
access-class MGMT-IN inunderline vty 0 4— note it'saccess-class, notip access-group, because VTYs are lines, not interfaces.
Target to hit — three things at once: SSH from ADMIN (10.80.99.10) still works; an SSH attempt from ROGUE (10.80.66.66) is dropped before the password prompt; and ROGUE can still ping 10.80.99.1. That last one is the proof you did management-plane filtering, not a blunt interface ACL — the router still routes the rogue's data, it just refuses to let it log in.
Stuck? Reveal the MGMT-IN config
R1(config)# ip access-list standard MGMT-IN R1(config-std-nacl)# permit 10.80.99.0 0.0.0.255 R1(config-std-nacl)# exit R1(config)# line vty 0 4 R1(config-line)# access-class MGMT-IN in ! filter inbound VTY sessions by source IP R1(config-line)# end
What "correct" looks like
ADMIN# ssh -l admin 10.80.99.1 ! source 10.80.99.10 - allowed Password: R1# ROGUE# ssh -l admin 10.80.99.1 ! source 10.80.66.66 - denied at the VTY % Connection refused by remote host ROGUE# ping 10.80.99.1 ! ...but data-plane reachability is untouched Sending 5, 100-byte ICMP Echos to 10.80.99.1, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/8 ms
Read the pair of ROGUE results together. The SSH is refused before any password prompt — brute-forcing the password is off the table for any source outside 10.80.99.0/24. Yet the ping still returns !!!!!: the router happily forwards the rogue's packets, it just won't let that source open a VTY. Filtering the who-can-log-in without touching who-can-be-routed is exactly what access-class on a line does — and exactly what an ip access-group on the interface would not do (that would drop the ping too).
Solo Challenge No commands this time
Do this from memory
Add three more hardening controls to R1's management plane, and for each one state the command and the attack it mitigates:
- An idle session timeout on the VTY lines, so an abandoned SSH session can't sit logged in.
- Brute-force protection that locks out login attempts after repeated failures within a window.
- Logging of login activity so failed and successful logins leave a record.
You pass when all three are true:
- You can name the VTY-line command for an idle timeout and say what it limits (hint: it lives under
line vtyand takes minutes and seconds). - You can name the global command that throttles repeated failed logins and the two
logincommands that make those events show up in the log. - You can say, in one sentence each, the threat each control blunts — a forgotten open session, a password-guessing flood, and a silent intrusion with no audit trail.
Check your reasoning (open only after you've done it)
Idle timeout — under line vty 0 4: exec-timeout 5 0 closes any session idle for 5 minutes. Mitigates an admin who walks away leaving an authenticated SSH session open for someone else to hijack.
Brute-force protection — globally: login block-for 120 attempts 5 within 60 means "after 5 failed logins in 60 seconds, refuse all login attempts for 120 seconds." Mitigates an automated password-guessing flood by making it pause and crawl.
Logging — globally: login on-failure log and login on-success log (paired with a logging host 10.80.99.10 to ship them off-box). Mitigates a silent intrusion — without a record, you never learn the front door was rattled or opened.
Break & Fix Re-open the front door, on purpose
Now create the exact misconfiguration you'll find on neglected gear, so you know its fingerprint on sight. On R1, undo the transport and authentication controls on the VTY lines:
R1(config)# line vty 0 4 R1(config-line)# transport input all ! Telnet is back on the menu R1(config-line)# no login ! and no password is required at all
From ADMIN, Telnet now connects straight to a prompt — no username, no password:
ADMIN# telnet 10.80.99.1
Trying 10.80.99.1 ... Open
R1> ! straight in, cleartext, unauthenticated
R1# show run | section line vty
line vty 0 4
no login
transport input all
transport input all permits Telnet (every keystroke and password in plaintext on the wire), and no login removes authentication entirely. Either one alone is dangerous; together they mean anyone with a route to R1 owns it. This is the single most common real-world management-plane failure.Now fix it — restore all three controls and confirm Telnet dies and the rogue is denied again:
R1(config)# line vty 0 4 R1(config-line)# transport input ssh R1(config-line)# login local R1(config-line)# access-class MGMT-IN in R1(config-line)# end
Done check
Close this lab when, with the hardened config restored, all of these are true:
| Run / test | Proof you want |
|---|---|
show ip ssh on R1 | SSH Enabled - version 2.0 |
ssh -l admin 10.80.99.1 from ADMIN | Logs in to R1# |
telnet 10.80.99.1 from ADMIN | % Connection refused by remote host |
show run | section line vty on R1 | Shows transport input ssh, login local, access-class MGMT-IN in |
ssh -l admin 10.80.99.1 from ROGUE | Denied at the VTY — no password prompt |
ping 10.80.99.1 from ROGUE | !!!!! — data plane still works; only login is filtered |