Harden SSH on Linux Servers

Khimananda Oli 8 min read Virtualization
Harden SSH on Linux Servers

By Khimananda Oli | Last reviewed: August 2026

Default SSH configurations are a primary attack vector for any internet-facing infrastructure, exposing systems to relentless credential stuffing and brute-force bots. To properly harden SSH on Linux servers, you must move beyond default settings and implement a defense-in-depth strategy that combines cryptographic key authentication, network-layer filtering, and automated intrusion prevention. This guide provides the exact configuration steps I use to secure production environments against modern threats.

SSH Defense-in-Depth ArchitectureInternet AttackerBrute Force / ScanLayer 1: NetworkUFW / Non-Std PortLayer 2: DaemonKeys Only / No RootServerProtectedActive Response: Fail2BanMonitors Logs & Updates FirewallBlocks Repeat Offenders AutomaticallyAudit Trail: journald + structured logging for compliance evidence
Layered security model to harden SSH on Linux servers: network filtering, daemon hardening, and active response work together to stop attacks before they reach the shell.

How do you configure SSH key authentication and disable passwords?

Password authentication is the single biggest risk factor when you harden SSH on Linux servers. Human-chosen passwords are predictable, reused, and trivially cracked by modern GPU-accelerated tools. Key-based authentication replaces this weak link with cryptographic proof of identity. For new deployments in 2026, Ed25519 keys are the standard; they offer better security than RSA at a fraction of the size and are supported by all current OpenSSH versions.

Generate strong Ed25519 keys

Create your key pair locally. Never generate keys on the server itself. The comment field should identify the key's purpose and owner for audit trails.

ssh-keygen -t ed25519 -C "admin@khimananda-prod-2026" -f ~/.ssh/id_ed25519_prod

Deploy keys securely

Copy the public key to the server. If password auth is still enabled temporarily, use ssh-copy-id. For hardened systems where passwords are already disabled, append the key manually via console or out-of-band management.

# From your local machine
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub user@server-ip

# Verify permissions on the server (critical)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh

Harden sshd_config for key-only access

Edit /etc/ssh/sshd_config. These directives form the baseline for any secure configuration. As detailed in the Ubuntu security hardening guide, always test configuration changes before applying them to avoid locking yourself out.

# /etc/ssh/sshd_config - Authentication Hardening
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM yes

# Disable root login entirely
PermitRootLogin no

# Restrict to specific users (optional but recommended)
AllowUsers deployer admin

# Protocol 2 is default in modern OpenSSH, but explicit is safer
Protocol 2

Validate the configuration syntax before reloading. A typo here can make the server unreachable.

sudo sshd -t && sudo systemctl reload sshd

What network-level controls protect SSH from brute-force attacks?

Even with perfect authentication, exposing SSH to the entire internet invites noise. Network-level controls reduce your attack surface and filter out opportunistic scanners before they consume daemon resources. This is especially relevant for teams managing infrastructure in Nepal or other regions where VPS providers may not offer advanced cloud-native firewalls; host-level filtering becomes your primary perimeter.

Configure UFW to restrict SSH access

If you use Ubuntu, UFW provides a clean interface over nftables. Allow SSH only from trusted CIDR ranges. If you must allow broader access, combine this with fail2ban.

# Allow SSH from office IP range only
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp comment "Office SSH"

# Default deny incoming
sudo ufw default deny incoming
sudo ufw enable

Change the default port (security through obscurity)

Changing port 22 does not stop targeted attacks, but it dramatically reduces log noise from automated botnets. This makes genuine alerts easier to spot. Update both sshd_config and your firewall rules simultaneously.

# In /etc/ssh/sshd_config
Port 2222

# Update UFW accordingly
sudo ufw allow 2222/tcp comment "SSH Custom Port"

Implement TCP wrappers as a secondary filter

For legacy compatibility or additional layering, /etc/hosts.allow and /etc/hosts.deny provide application-level filtering independent of the kernel firewall.

# /etc/hosts.deny
sshd: ALL

# /etc/hosts.allow
sshd: 203.0.113.0/24 198.51.100.50
Fail2Ban Active Response WorkflowAuth Log/var/log/auth.logFailed SSH AttemptsFail2Ban FilterRegex Pattern MatchCount > maxretryJail ActionExecute Ban CommandSet bantime durationFirewallUFW/nftablesIP BlockedConfiguration Example: jail.local[sshd] enabled=true | port=2222 | maxretry=3 | bantime=1h | findtime=10mUnban: sudo fail2ban-client set sshd unbanip 192.0.2.1
Automated intrusion prevention: fail2ban monitors authentication logs and dynamically updates firewall rules to block brute-force sources, a critical component when you harden SSH on Linux servers.

How does fail2ban automate intrusion prevention for SSH?

Network filters handle known-good sources, but you cannot whitelist the entire internet. Fail2ban bridges this gap by watching authentication logs and temporarily banning IPs that exceed failure thresholds. This turns passive logging into active defense. When combined with the fail2ban configuration guide, you get a production-ready setup that survives reboots and integrates with your existing firewall.

Install and configure fail2ban

Never edit jail.conf directly; package updates will overwrite it. Create jail.local instead.

sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Define SSH jail parameters

Tune these values based on your traffic patterns. Aggressive settings catch more attackers but risk false positives for legitimate users with flaky connections.

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = ufw

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h

Verify and manage bans

Monitor fail2ban status regularly. False positives happen; ensure you have an unban procedure documented for your team.

# Check jail status
sudo fail2ban-client status sshd

# Manually unban a legitimate user
sudo fail2ban-client set sshd unbanip 203.0.113.50

# Test regex patterns without banning
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

Which SSH hardening methods actually improve security?

Not every hardening recommendation delivers real value. Some are outdated, others are cosmetic. Understanding the difference prevents wasted effort and false confidence. The table below separates genuine security improvements from common misconceptions based on current threat models and OpenSSH 9.x behavior.

Hardening MeasureSecurity ImpactOperational CostRecommendation
Disable Password AuthCritical — eliminates credential stuffingLow — requires key distributionMandatory for all production
Ed25519 KeysHigh — stronger crypto, smaller keysNone — drop-in replacementDefault for new deployments
Non-Standard PortLow — stops dumb bots onlyMedium — breaks tooling assumptionsOptional — reduces log noise
Fail2BanHigh — automated active defenseLow — set-and-forget after tuningRecommended for public-facing
Disable Root LoginCritical — enforces accountabilityLow — use sudo insteadMandatory everywhere
SSH Certificates (CA)Very High — scalable key managementHigh — requires CA infrastructureFleets >20 servers or compliance
Two-Factor (TOTP)High — defense against key theftMedium — adds login frictionPrivileged access only

For teams managing database servers alongside application infrastructure, apply the same principles consistently. The PostgreSQL administration essentials guide covers securing database connections with similar rigor, ensuring your entire stack follows the same authentication standards.

SSH Authentication Methods: Security vs ComplexityOperational Complexity →Security Strength →PasswordRSA-4096LegacyEd25519RecommendedEd25519+ TOTPSSH CACertificatesNEVER USESWEET SPOTENTERPRISE SCALE
Trade-off matrix for SSH authentication: Ed25519 keys provide the optimal balance of security and simplicity for most teams hardening SSH on Linux servers, while certificates justify their complexity only at scale.

How do you maintain SSH security posture over time?

Hardening is not a one-time event. Configuration drift, package updates, and new vulnerabilities erode security over time. Sustainable hardening requires automation and observability. Treat your SSH configuration as code: version it, review it, and deploy it through your configuration management system rather than manual edits.

  • Automate deployment: Use Ansible, Terraform, or cloud-init to push sshd_config and fail2ban rules. Manual edits on individual servers inevitably diverge. The initial Ubuntu server setup guide demonstrates integrating SSH hardening into provisioning workflows.
  • Monitor authentication events: Forward /var/log/auth.log to your centralized logging stack. Alert on unusual patterns: successful logins from new geolocations, spikes in failures from internal IPs, or key additions outside change windows.
  • Audit authorized_keys regularly: Stale keys from departed employees or decommissioned services are a persistent risk. Implement quarterly reviews or automate key lifecycle management with SSH certificates.
  • Test your backups: Before any hardening change, verify console access works. Cloud provider consoles, IPMI, or out-of-band management are your recovery path when SSH misconfiguration locks you out.
  • Stay current: Subscribe to OpenSSH release announcements. New features like FIDO2 resident keys and improved algorithms arrive regularly; outdated versions miss critical protections.

Securing Remote Access for Production Workloads

To effectively harden SSH on Linux servers, combine Ed25519 key authentication, strict daemon configuration, network-layer filtering, and automated intrusion prevention into a cohesive defense strategy. Each layer compensates for the others' limitations: keys prevent credential theft, firewalls reduce exposure, fail2ban handles dynamic threats, and monitoring catches what slips through. Document your configuration, automate its deployment, and treat SSH security as an ongoing operational discipline rather than a checklist item. If your team needs help implementing these controls across a fleet or preparing for a compliance audit, reach out to discuss your infrastructure security requirements.

Frequently Asked Questions

Disable root login immediately by setting PermitRootLogin to no in sshd_config. This prevents direct administrative access and forces users to authenticate as standard users before escalating privileges via sudo.

Set PasswordAuthentication to no in /etc/ssh/sshd_config after deploying SSH keys for all users. Restart the sshd service using systemctl restart sshd to apply changes and prevent brute-force password attacks.

Enforce Protocol 2 exclusively as Protocol 1 is obsolete and cryptographically broken. Modern OpenSSH versions default to v2, but explicitly verifying this in configuration ensures legacy clients cannot negotiate insecure connections.

Yes, moving from port 22 reduces automated botnet noise significantly. While not true security, it lowers log volume and resource waste from scripted scans targeting the default port on hardened Linux servers.

Use curve25519-sha256 and sntrup761x25519-sha512 for post-quantum readiness. Avoid older diffie-hellman-group14-sha1 variants. Configure KexAlgorithms in sshd_config to restrict negotiations to these modern, audited cryptographic primitives only.

Fail2Ban monitors auth logs for repeated failed login attempts and dynamically updates firewall rules to ban offending IPs. Configure maxretry and bantime in jail.local specifically for the sshd service to mitigate credential stuffing.

Prefer Ed25519 keys for faster signing and smaller key sizes with equivalent security. Generate them using ssh-keygen -t ed25519. Reserve RSA-4096 only for legacy systems that lack Ed25519 support in their SSH client.

It creates an explicit whitelist of accounts permitted to connect via SSH. Denying access by default and listing only authorized usernames prevents compromised service accounts or dormant users from becoming attack vectors.

Rotate host keys annually or after any suspected compromise. Regenerate using ssh-keygen -A, distribute new fingerprints to known hosts files, and revoke old keys to maintain trust chain integrity across infrastructure.

Yes, combine publickey and keyboard-interactive methods using AuthenticationMethods publickey,keyboard-interactive. Integrate Google Authenticator PAM module to require TOTP codes after successful key validation for defense-in-depth on critical servers.

Set 600 permissions on authorized_keys and 700 on the .ssh directory. OpenSSH refuses key authentication if group or world write bits are set, preventing unauthorized key injection through shared directory vulnerabilities.

Use ss -tnp | grep :22 to list current connections with process IDs. Combine with w or who commands to map sessions to usernames and source IPs for real-time access monitoring and incident response.

Certificates simplify key management at scale by using a trusted CA to sign short-lived user keys. This eliminates manual authorized_keys distribution and enables centralized revocation without touching individual server configurations.

Disable CBC mode ciphers like aes128-cbc and arcfour variants due to padding oracle vulnerabilities. Restrict Ciphers to chacha20-poly1305 and aes256-gcm to ensure authenticated encryption with associated data protection.

Run sshd -t to check syntax errors before restarting the daemon. Always maintain an active session while testing changes to avoid lockout, and verify connectivity from a separate terminal window.