Network & delivery

8 min readUpdated

SSH port 22: what it is, why it is scanned, and how to harden it

Quick answer

SSH listens on TCP port 22 by default, a number IANA assigned in July 1995. Because every SSH server starts there, automated scanners try it constantly with common usernames and passwords. The defence is not a different port but key-only authentication, no direct root login, an allowlist or VPN where possible, and rate limits such as fail2ban. Moving SSH to another port cuts log noise from untargeted bots but adds no real security.

01What is port 22?

Port 22 is one of the well-known ports: the TCP port registered with IANA for the Secure Shell protocol (SSH), which provides encrypted remote login, command execution, file transfer with SFTP and scp, and tunnelling. RFC 4253, the SSH transport standard of January 2006, says a server "normally listens for connections on port 22". IANA also lists UDP and SCTP 22 for SSH, but in practice SSH runs over TCP only.

The number has a story. Tatu Ylönen wrote the first SSH in spring 1995 to replace telnet on port 23 and FTP on port 21, and asked IANA for the free number between them just before announcing ssh-1.0 in July 1995. The server most people run today, OpenSSH, first shipped with OpenBSD 2.6 on 1 December 1999.

PropertyValue
Default portTCP 22
ProtocolSSH-2, standardised in RFC 4251 to RFC 4254 (January 2006)
Usual serverOpenSSH sshd
Configuration/etc/ssh/sshd_config, plus /etc/ssh/sshd_config.d/*.conf on Debian and Ubuntu
First bytes sentA plain-text banner such as SSH-2.0-OpenSSH_9.2p1
Face the internet?Only if needed, ideally from known addresses or through a VPN

02Why is port 22 scanned so much?

Because it is predictable and valuable. Every internet-facing address is probed continuously, much of it by compromised machines in botnets, and an SSH server that accepts a guessed password hands over a shell. Bots work through lists of common usernames such as root, admin, test or ubuntu with leaked passwords, and some only read the banner to map software versions for the next vulnerability. When Qualys disclosed regreSSHion in July 2024, it counted more than 14 million potentially vulnerable OpenSSH servers exposed to the internet, which shows how large the pool is.

The attempts are easy to recognise in the log. The lines below were produced on a test server running OpenSSH 9.2p1 by failed logins and by a client that sent HTTP to port 22, the same patterns scanners leave around the clock. With password authentication switched on, failed guesses also appear as "Failed password for" lines.

sshd log lines from failed and malformed connections (OpenSSH 9.2p1 on Debian 12; client address replaced with a documentation address)
Invalid user admin from 198.51.100.23 port 52808
Connection closed by invalid user admin 198.51.100.23 port 52808 [preauth]
User root from 198.51.100.23 not allowed because none of user's groups are listed in AllowGroups
kex_exchange_identification: client sent invalid protocol identifier "GET / HTTP/1.1"
banner exchange: Connection from 198.51.100.23 port 52824: invalid format
kex_exchange_identification: Connection closed by remote host

03How to check what your SSH server allows

Read the effective configuration, not the file. sshd -T prints every setting after defaults and included files are applied, which catches overrides you did not know about. On OpenSSH 9.2 with no configuration at all it reports permitrootlogin without-password (keys only for root), passwordauthentication yes, maxauthtries 6, logingracetime 120 and maxstartups 10:30:100; distributions, installers and cloud images change some of these.

Read-only checks (the systemd unit is ssh on Debian and Ubuntu, sshd on RHEL-family systems)
# effective settings that matter for exposure
sudo sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|maxauthtries|maxstartups) '

# which addresses sshd listens on
sudo ss -Htlnp 'sport = :22'

# logins attempted for unknown users in the last 24 hours
journalctl -u ssh --since '-24h' -o cat | grep -c 'Invalid user'

# the addresses trying hardest
journalctl -u ssh --since '-24h' -o cat | grep -oP 'Invalid user \S+ from \K[0-9a-f.:]+' | sort | uniq -c | sort -rn | head

# installed version, for vulnerability checks
ssh -V

04A tested sshd_config hardening baseline

Put changes in a drop-in file instead of editing the main file. sshd uses the first value it reads for each keyword, and Debian and Ubuntu include /etc/ssh/sshd_config.d/*.conf at the top of sshd_config, so a file named 10-hardening.conf wins over later lines and over drop-ins that sort after it. This block was checked with sshd -t and sshd -T on OpenSSH 9.2p1: with PermitRootLogin yes and PasswordAuthentication yes further down the main file, both still came out as no. The table explains each setting; the file itself follows it.

SettingOpenSSH defaultBaselineWhy
PermitRootLoginprohibit-password (sshd -T prints without-password)noLog in as a named user and use sudo, so every action has an owner
PasswordAuthentication, KbdInteractiveAuthenticationyesnoRemoves password guessing altogether
MaxAuthTries63Fewer attempts per connection; failures are logged from half the limit
LoginGraceTime120 seconds30 secondsUnauthenticated connections are cut sooner, freeing slots
MaxStartups10:30:10010:30:60Above 10 unauthenticated connections, refuse 30% of new ones, rising to all at 60
PerSourceMaxStartupsnone3One address cannot hold more than 3 unauthenticated connections
AllowGroupsany usersshusersOnly members of one group may log in at all
/etc/ssh/sshd_config.d/10-hardening.conf (validated with sshd -t and sshd -T, OpenSSH 9.2p1)
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 30
MaxStartups 10:30:60
PerSourceMaxStartups 3
AllowGroups sshusers
X11Forwarding no
AllowAgentForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2

05Banning brute-force sources with fail2ban

Key-only authentication makes guessing futile, but the attempts still cost CPU, fill logs and occupy MaxStartups slots. fail2ban reads the log and bans an address after repeated failures. On Debian 12, which logs to the systemd journal and has no /var/log/auth.log unless rsyslog is installed, the jail needs backend = systemd and the python3-systemd package, or fail2ban stops with "Have not found any log file for sshd jail".

The mode matters too. Run against the full 10-line test log behind the excerpt above, the stock sshd filter in its default normal mode counted 3 of the 10 lines as failures; in aggressive mode it counted 9, including the banner-scan lines, and ignored only the clean disconnect.

/etc/fail2ban/jail.d/sshd.local (fail2ban 1.0.2; passes fail2ban-client -t)
[sshd]
enabled  = true
backend  = systemd
mode     = aggressive
port     = ssh
maxretry = 5
findtime = 10m
bantime  = 1h
bantime.increment = true
banaction = nftables-multiport

06Rate-limiting new SSH connections in the firewall

A firewall rate limit works before sshd sees anything and needs no log parsing. The ruleset below drops new connections to port 22 from any single address above 10 per minute, after a burst of 5, and leaves established sessions alone. In an isolated test, 30 rapid connection attempts from one address produced 6 accepted and 24 dropped. Retransmitted SYNs count against the limit, so keep it generous for real users behind shared NAT.

OpenSSH 9.8 (1 July 2024) added a built-in alternative, PerSourcePenalties, on by default: sshd records a penalty against addresses that fail authentication repeatedly or crash a session and refuses them until it expires. It is not available on the OpenSSH 9.2 tested here.

nftables per-address limit for new SSH connections (nftables 1.0.6; syntax-checked and load-tested in a network namespace)
table inet ssh_guard {
    set ssh4 { type ipv4_addr; flags dynamic; timeout 5m; size 65535; }
    set ssh6 { type ipv6_addr; flags dynamic; timeout 5m; size 65535; }
    chain input {
        type filter hook input priority filter; policy accept;
        tcp dport 22 ct state new add @ssh4 { ip saddr limit rate over 10/minute burst 5 packets } counter drop
        tcp dport 22 ct state new add @ssh6 { ip6 saddr limit rate over 10/minute burst 5 packets } counter drop
    }
}

07Should you change the SSH port?

Changing the port is obscurity, not security. It removes most untargeted bots, which only try 22, so logs become quieter and real attacks easier to spot. It does nothing against anyone who scans all 65,536 ports, and it protects no weak password. Use it, if at all, on top of key-only login, never instead of it. A port below 1024 that nothing else uses has one advantage: an unprivileged local user cannot bind it while sshd is down. The table at the end of this section compares a port change with the measures that do add security.

If you do change it, open the new port in the firewall before you restart, and keep the old session open until a login on the new port works:

  • Debian, RHEL and most distributions: set Port 2222 (or your choice) in a drop-in file, run sshd -t, then restart the ssh or sshd service
  • Port lines add up instead of overriding each other: with Port 2222 in a drop-in and Port 22 left uncommented in sshd_config, sshd -T shows both and sshd listens on both
  • Ubuntu 22.10 and later use socket activation; from 24.04 the port is read from sshd_config by a generator, so run systemctl daemon-reload and restart ssh.socket
  • RHEL with SELinux: allow the new port first with semanage port -a -t ssh_port_t -p tcp 2222, as Red Hat documents
  • Clients: add Port 2222 under the host in ~/.ssh/config, or pass ssh -p 2222
MeasureStopsDoes not stop
Key-only authenticationPassword guessing and credential stuffingStolen private keys, sshd vulnerabilities
Allowlist or VPN in front of SSHAlmost all exposure: strangers never reach sshdAttacks from inside the allowed network
fail2ban or a firewall rate limitNoisy brute force from single addressesSlow attacks spread across many addresses
Changing the portBots that only try port 22Anyone who scans more than one port
Keeping OpenSSH patchedKnown vulnerabilities such as regreSSHionWeak configuration

08What a firewall, VPN or proxy can do for SSH

The strongest protection is to make sshd unreachable from strangers: allow port 22 only from your office or management network, or reach it through a VPN or a bastion host. Then the guessing never starts. For a fleet, that one rule beats every log-parsing tool combined, and it fits the default-deny approach in firewall configuration.

A web reverse proxy or WAF does not help here. It carries HTTP and HTTPS, not SSH, so it neither filters brute force on port 22 nor hides it, and if the server's address is public, attackers can flood port 22 directly. Rate limits such as those in what is rate limiting slow guessing on one host; a volumetric flood against the port needs network-level filtering upstream of the server's link.

09SSH and port 22: a dated timeline

Vulnerabilities matter as much as configuration, because a pre-authentication flaw bypasses every login rule. Check the installed version with ssh -V and your distribution's package version, which carries back-ported fixes.

DateEvent
July 1995Tatu Ylönen announces ssh-1.0; IANA assigns port 22
1 December 1999OpenSSH 1.2.2 ships with OpenBSD 2.6
January 2006SSH-2 published as RFC 4251 to RFC 4254
1 July 2024CVE-2024-6387, regreSSHion, unauthenticated remote code execution in OpenSSH 8.5p1 to 9.7p1 on glibc Linux, fixed in OpenSSH 9.8
1 July 2024OpenSSH 9.8 adds PerSourcePenalties, on by default

Questions

Frequently asked questions

Port 22 is the default, registered with IANA and used unless the server is configured otherwise. An administrator can run sshd on any port with the Port setting, and a client then connects with ssh -p and the port number. Some hosting panels and appliances use a different default. The protocol is the same on every port; only the number changes.

SSH uses TCP port 22. IANA registered port 22 for SSH on TCP, UDP and SCTP, as it often does for a service, but SSH implementations use TCP only, because the protocol needs a reliable, ordered byte stream. If a scan shows UDP 22 open, it is not SSH, and it is worth finding out which program is listening there.

It can be, if password authentication is off, root login is disabled, OpenSSH is kept patched and brute-force sources are rate-limited or banned. It is safer still to open port 22 only to known addresses or through a VPN, so strangers never reach sshd. An open port 22 with password login enabled will see guessing attempts within minutes of going online.

Only marginally. A non-standard port avoids bots that only try port 22, so logs get quieter, but anyone who scans all ports finds SSH within minutes and the protocol announces itself in its banner. Real security comes from key-only authentication, no direct root login, patching and limiting who can connect. Treat a port change as noise reduction on top of those.

On systemd-based Linux, read the SSH service journal: journalctl -u ssh on Debian and Ubuntu, or -u sshd on RHEL-family systems. Lines starting with Invalid user name unknown accounts, and Failed password or Failed publickey lines show wrong credentials. Counting them per source address shows who is trying hardest. On systems with rsyslog the same lines are in /var/log/auth.log or /var/log/secure.

Sources

Sources and verification

sshd_config drop-in validated with sshd -t and sshd -T on OpenSSH 9.2p1 using a scratch configuration; log lines produced by a scratch sshd in an isolated network namespace; fail2ban 1.0.2 (Debian package, unpacked into a scratch directory) jail checked with fail2ban-client -t and the sshd filter run with fail2ban-regex against those lines; nftables rate limit syntax-checked and load-tested with nftables 1.0.6 in an isolated namespace; Debian 12, October 2026. No 24-hour exposure measurement is included.

  1. 01RFC 4253: The Secure Shell (SSH) Transport Layer Protocol · IETF
  2. 02How SSH got port number 22 (Tatu Ylönen) · SSH Communications Security
  3. 03OpenSSH project history · OpenSSH
  4. 04OpenSSH 9.8 release notes (PerSourcePenalties) · OpenSSH
  5. 05sshd_config(5), OpenSSH 9.2 in Debian bookworm · Debian
  6. 06regreSSHion: remote unauthenticated code execution vulnerability in OpenSSH server · Qualys
  7. 07fail2ban issue 3567: Have not found any log file for sshd jail (Debian 12) · fail2ban (GitHub)
  8. 08SSHd now uses socket-based activation (Ubuntu 22.10 and later) · Ubuntu Discourse
  9. 09Securing networks: using secure communications between two systems with OpenSSH (RHEL 9) · Red Hat

Last updated