SSH Key Authentication and Brute-Force Prevention
SSH Key Authentication and Brute-Force Prevention
Section titled “SSH Key Authentication and Brute-Force Prevention”Replacing SSH password authentication with public key auth removes the most common attack vector against RouterOS management. Combining key auth with firewall-based brute-force detection and optional port knocking provides layered protection for internet-exposed routers.
Prerequisites
Section titled “Prerequisites”- RouterOS 7.x (RSA and Ed25519 key support confirmed)
- SSH service enabled:
/ip service printshowssshis active - A second terminal session open during configuration — do not lock yourself out
Step 1 — Generate an SSH Key Pair
Section titled “Step 1 — Generate an SSH Key Pair”Generate the key pair on your client machine, not on the router. Ed25519 is preferred; use RSA 4096 if your client requires compatibility with older software.
Linux / macOS:
# Ed25519 (preferred)ssh-keygen -t ed25519 -f ~/.ssh/id_mikrotik
# RSA 4096 (compatibility fallback)ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_mikrotik_rsaWindows (PowerShell with OpenSSH):
# Ed25519 (preferred)ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_mikrotik"
# RSA 4096ssh-keygen -t rsa -b 4096 -f "$env:USERPROFILE\.ssh\id_mikrotik_rsa"This produces two files:
id_mikrotik— private key (keep secret, never copy to router)id_mikrotik.pub— public key (upload to router)
Supported formats for import: OpenSSH, PEM, PKCS#8.
Step 2 — Upload the Public Key to the Router
Section titled “Step 2 — Upload the Public Key to the Router”The public key file must be present in router storage before it can be imported.
Option A — SCP (from client):
Option B — Winbox:
Drag and drop id_mikrotik.pub into the Files panel.
Option C — FTP:
ftp 192.168.88.1put ~/.ssh/id_mikrotik.pubStep 3 — Import the Public Key
Section titled “Step 3 — Import the Public Key”Once the file is on the router, associate it with the target user account:
/user ssh-keys import user=admin public-key-file=id_mikrotik.pubVerify the key was imported:
/user ssh-keys printExpected output shows the key owner and associated user:
Columns: USER, KEY-OWNER USER KEY-OWNER admin user@hostnameStep 4 — Test Key Authentication
Section titled “Step 4 — Test Key Authentication”Before disabling password login, open a second session and confirm key auth works:
Explicitly force password auth to confirm it is still active (before disabling):
Only proceed to the next step after the key login succeeds in the test session.
Step 5 — Disable SSH Password Authentication
Section titled “Step 5 — Disable SSH Password Authentication”With key auth confirmed, disable password-based SSH login at the service level:
/ip ssh set always-allow-password-login=noVerify the setting:
/ip ssh printLook for always-allow-password-login: no.
Step 6 — Firewall Rules to Block Brute-Force Attempts
Section titled “Step 6 — Firewall Rules to Block Brute-Force Attempts”Even with password auth disabled, connection noise from brute-force scanners wastes CPU. The staged address-list method escalates repeat offenders into a blacklist that auto-expires after 1 day.
Rule ordering is critical — add rules in this order so blacklist drops fire before escalation rules:
/ip firewall filter
# Optional: whitelist trusted admin IPs (bypasses all SSH rate limiting)add chain=input action=accept protocol=tcp dst-port=22 \ src-address-list=ssh-trusted \ comment="Allow trusted SSH admins"
# Drop IPs already on the blacklistadd chain=input action=drop protocol=tcp dst-port=22 \ src-address-list=ssh-blacklist \ comment="Drop blacklisted SSH sources"
# Escalation: stage 3 -> blacklist (1 day)add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage3 \ address-list=ssh-blacklist address-list-timeout=1d \ comment="SSH stage 3 -> blacklist"
# Escalation: stage 2 -> stage 3 (1 hour)add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage2 \ address-list=ssh-stage3 address-list-timeout=1h \ comment="SSH stage 2 -> stage 3"
# Escalation: stage 1 -> stage 2 (15 minutes)add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage1 \ address-list=ssh-stage2 address-list-timeout=15m \ comment="SSH stage 1 -> stage 2"
# First new connection attempt -> stage 1 (5 minutes)add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new \ address-list=ssh-stage1 address-list-timeout=5m \ comment="SSH new connection -> stage 1"How it works: after 4 new connection attempts (regardless of whether auth
succeeds or fails), the source IP lands in ssh-blacklist and is dropped for
24 hours. Each stage list auto-expires, so an attacker slowing their rate below
the timeout threshold resets to stage 1.
This technique can be abused for DoS: an attacker can forge your admin IP as
source to get it blacklisted. Always add your management IPs to ssh-trusted
first using the accept rule above.
Add trusted admin IPs:
/ip firewall address-listadd list=ssh-trusted address=192.168.1.0/24 comment="Internal management network"add list=ssh-trusted address=203.0.113.10 comment="Admin home IP"Monitor the blacklist:
/ip firewall address-list print where list=ssh-blacklistManually unblock an IP:
/ip firewall address-list remove [find list=ssh-blacklist address=203.0.113.50]Step 7 — Port Knocking (Optional)
Section titled “Step 7 — Port Knocking (Optional)”Port knocking hides SSH entirely from scanners. The router only accepts SSH from source IPs that have sent TCP packets to a specific sequence of ports within defined time windows. Hosts that never knock cannot reach port 22.
/ip firewall filter
# Allow SSH only from the "secured" listadd chain=input action=accept protocol=tcp dst-port=22 \ src-address-list=ssh-secured \ comment="SSH: allow after successful knock"
# Drop SSH from everyone elseadd chain=input action=drop protocol=tcp dst-port=22 \ comment="SSH: drop without knock"
# Knock sequence rules (place BEFORE the drop above, or in a pass-through position)# Knock 1: TCP/1001 -> enter knock-stage1 (30s window)add chain=input action=add-src-to-address-list protocol=tcp dst-port=1001 \ address-list=knock-stage1 address-list-timeout=30s \ comment="Knock 1"
# Knock 2: TCP/2002 while in knock-stage1 -> enter knock-stage2 (30s window)add chain=input action=add-src-to-address-list protocol=tcp dst-port=2002 \ src-address-list=knock-stage1 \ address-list=knock-stage2 address-list-timeout=30s \ comment="Knock 2"
# Knock 3: TCP/3003 while in knock-stage2 -> enter secured (15 min)add chain=input action=add-src-to-address-list protocol=tcp dst-port=3003 \ src-address-list=knock-stage2 \ address-list=ssh-secured address-list-timeout=15m \ comment="Knock 3 -> secured"Send the knock sequence from the client:
# Linux/macOS — use nc (netcat) or nmapnc -z 203.0.113.1 1001nc -z 203.0.113.1 2002nc -z 203.0.113.1 3003Windows (PowerShell):
function Knock($ip, $port) { $t = New-Object System.Net.Sockets.TcpClient try { $t.Connect($ip, $port) } catch {} $t.Close()}Knock "203.0.113.1" 1001Knock "203.0.113.1" 2002Knock "203.0.113.1" 3003Complete Hardened Configuration Summary
Section titled “Complete Hardened Configuration Summary”For a fully hardened SSH setup combining all steps:
# 1. Import key and disable password auth/user ssh-keys import user=admin public-key-file=id_mikrotik.pub/ip ssh set always-allow-password-login=no
# 2. Allow only trusted management IPs (adjust subnets/IPs)/ip firewall address-listadd list=ssh-trusted address=192.168.1.0/24 comment="Management LAN"
# 3. Staged brute-force blacklist (input chain)/ip firewall filteradd chain=input action=accept protocol=tcp dst-port=22 \ src-address-list=ssh-trusted comment="Trusted SSH admins"add chain=input action=drop protocol=tcp dst-port=22 \ src-address-list=ssh-blacklist comment="Drop blacklisted"add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage3 \ address-list=ssh-blacklist address-list-timeout=1d comment="Stage 3 -> blacklist"add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage2 \ address-list=ssh-stage3 address-list-timeout=1h comment="Stage 2 -> stage 3"add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new src-address-list=ssh-stage1 \ address-list=ssh-stage2 address-list-timeout=15m comment="Stage 1 -> stage 2"add chain=input action=add-src-to-address-list protocol=tcp dst-port=22 \ connection-state=new address-list=ssh-stage1 \ address-list-timeout=5m comment="New SSH -> stage 1"Troubleshooting
Section titled “Troubleshooting”Key login fails with “Permission denied (publickey)”
Section titled “Key login fails with “Permission denied (publickey)””- Verify the key was imported for the correct user:
/user ssh-keys print
- Check the client is using the right private key:
Terminal window - Confirm the public key file was uploaded intact — re-upload and re-import if in doubt.
Locked out after disabling password auth
Section titled “Locked out after disabling password auth”Recover via serial console or Winbox (Winbox uses its own auth path):
/ip ssh set always-allow-password-login=yesThen re-import a working key and retry.
Legitimate IP blocked by brute-force rules
Section titled “Legitimate IP blocked by brute-force rules”Remove from blacklist manually:
/ip firewall address-list print where list=ssh-blacklist/ip firewall address-list remove [find list=ssh-blacklist address=<ip>]Then add the IP to ssh-trusted to prevent future blocking.
Port knock sequence not working
Section titled “Port knock sequence not working”- Check the address-list entries are being populated:
/ip firewall address-list print where list~"knock"
- Verify the knock rules fire before the SSH drop rule in the input chain:
/ip firewall filter print chain=input
- Confirm all knocks arrive within the timeout window — use a scripted knock rather than manual commands.
See Also
Section titled “See Also”- IP Services Hardening — restrict allowed-from addresses and disable unused services
- User Management: Password Policy, Service Hardening, and 2FA — TOTP, password policy, dedicated API accounts
- Bruteforce Prevention — firewall-based protection for Winbox, Telnet, and other services