Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Harden SSH: Key Auth, Fail2ban, and Port Hardening

By Kokil Thapa | Last reviewed: August 2026

If you manage Linux servers for web applications, leaving default SSH settings exposed is the fastest route to compromise. Bots scan public IPs relentlessly, and password-based authentication remains the primary attack vector for brute-force campaigns. To properly Harden SSH: Key Auth, Fail2ban, and Port Hardening are the three non-negotiable controls that stop automated attacks before they consume your server’s resources or breach your application data.

On production Laravel and WordPress systems I maintain across Nepal and internationally, this baseline configuration has prevented countless intrusion attempts. It is not theoretical security; it is operational hygiene. For teams managing multiple client sites, combining these controls with automated deployment workflows—like those discussed in my guide on CI/CD pipeline setup in Nepal—ensures security is baked into infrastructure provisioning rather than bolted on later.

How Do You Configure OpenSSH to Harden SSH: Key Auth, Fail2ban, and Port Hardening?

The foundation of server access security lies in the /etc/ssh/sshd_config file. Default Ubuntu 24.04 configurations prioritize convenience over security, often allowing password authentication and root login. In practice, I treat SSH configuration as code: version-controlled, reviewed, and deployed consistently via tools like Deployer or Ansible.

Defense-in-Depth: Harden SSH LayersLayer 1: NetworkNon-Standard PortUFW Firewall RulesBlocks 95% of bot scansLayer 2: ServiceKey-Only AuthenticationEd25519 Keys RequiredEliminates brute forceLayer 3: ApplicationFail2ban Active ResponseAuto-ban After 3 FailsStops persistent attackersResult: Secure, Low-Noise SSH Access for Production WorkloadsCompatible with Deployer, GitLab CI, and automated provisioning pipelinesZero successful brute-force compromises in 15+ years of production use
Three-layer defense model to Harden SSH: Key Auth, Fail2ban, and Port Hardening working together on Ubuntu 24.04

Disabling Password Authentication Safely

Before touching any configuration, generate and deploy an SSH key pair. Locking yourself out of a production server because you disabled passwords without a working key is a rite of passage nobody wants. On your local machine, generate an Ed25519 key (the current standard for performance and security):

ssh-keygen -t ed25519 -C "kokil@production-deploy" -f ~/.ssh/id_ed25519_prod
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub user@your-server-ip

Verify key-based login works in a new terminal session before proceeding. Then edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

# Apply these directives:
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
PubkeyAuthentication yes
AuthenticationMethods publickey
PermitRootLogin no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

The AuthenticationMethods publickey directive is critical. Without it, some PAM configurations may still fall back to password prompts even when PasswordAuthentication no is set. Validate syntax before restarting:

sudo sshd -t
sudo systemctl restart ssh

Selecting Modern Cryptographic Algorithms

Ubuntu 24.04 ships OpenSSH 9.x, which supports modern algorithms by default. However, explicit configuration prevents downgrade attacks and ensures compliance. Add these lines to sshd_config:

HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com
PubkeyAcceptedAlgorithms ssh-ed25519,sk-ssh-ed25519@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

This configuration rejects RSA keys entirely. If legacy systems require RSA, add rsa-sha2-512 to PubkeyAcceptedAlgorithms but plan migration. On legal-tech portals handling sensitive client documents, I enforce Ed25519 exclusively—there is no legitimate reason to accept weaker cryptography in 2026.

How Does Fail2ban Protect SSH When You Harden SSH: Key Auth, Fail2ban, and Port Hardening?

Even with key-only authentication, SSH logs fill with connection attempts from misconfigured clients, former employees, and bots probing for weaknesses. Fail2ban monitors these logs and dynamically updates firewall rules to block offending IPs. This reduces log noise, conserves server resources, and provides visibility into attack patterns.

Installing and Configuring Fail2ban on Ubuntu 24.04

Install Fail2ban and create a local override file (never edit jail.conf directly):

sudo apt update && sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local with production-appropriate values:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
backend = systemd
banaction = nftables-multiport

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 24h
findtime = 5m

Note the port = 2222 value. This must match your custom SSH port exactly. Fail2ban uses nftables on Ubuntu 24.04 by default; if you still use UFW with iptables backend, change banaction to ufw. Enable and start the service:

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Fail2ban Detection CycleSSH Log EntryFailed auth attemptFail2ban FilterPattern matchingCounter Check≥3 fails in 5min?nftables BanBlock IP 24hBelow Threshold?Reset counter after findtime window expiresNoYesMonitor: sudo fail2ban-client status sshd | View bans: sudo fail2ban-client get sshd bannedUnban mistake: sudo fail2ban-client set sshd unbanip 192.0.2.1
Fail2ban detection and auto-ban workflow integral to Harden SSH: Key Auth, Fail2ban, and Port Hardening strategy

Tuning Fail2ban for Production Reality

Default Fail2ban settings are too aggressive for shared hosting environments where multiple developers connect from the same NAT gateway. On client projects with distributed teams, I adjust thresholds:

  • Development/staging servers: maxretry = 5, bantime = 30m — allows for typos and key rotation mistakes
  • Production application servers: maxretry = 3, bantime = 24h — strict enforcement
  • Bastion/jump hosts: maxretry = 2, bantime = 72h — highest security tier

Create ignore rules for trusted IP ranges (office networks, CI/CD runners, monitoring systems):

[sshd]
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16 203.0.113.50/32

Never add broad CIDR blocks unless you control the entire range. A common mistake is ignoring cloud provider metadata IPs or CDN ranges that attackers can abuse.

Why Change the Default Port When You Harden SSH: Key Auth, Fail2ban, and Port Hardening?

Moving SSH from port 22 does not stop determined attackers. Nmap scans all ports. However, it dramatically reduces log volume from automated botnets that only target port 22. On a typical production server, changing the port reduces SSH-related log entries by 90–95%. This makes genuine security events visible and reduces storage costs for log aggregation services.

Changing the SSH Port Without Locking Yourself Out

This operation requires careful sequencing. On one occasion, I changed the port on a client’s WooCommerce server without updating UFW first, causing a 45-minute outage while waiting for console access. Follow this exact order:

  1. Add the new port to UFW before changing sshd_config:
    sudo ufw allow 2222/tcp comment 'SSH alternate port'
    sudo ufw reload
  2. Edit sshd_config to listen on both ports temporarily:
    Port 22
    Port 2222
  3. Restart SSH and verify connectivity on the new port:
    sudo systemctl restart ssh
    ssh -p 2222 user@your-server-ip
  4. Only after confirmed access, remove port 22:
    # Edit sshd_config: remove "Port 22", keep only "Port 2222"
    sudo sshd -t && sudo systemctl restart ssh
    sudo ufw delete allow 22/tcp

Update Fail2ban’s port directive to match. Update deployment scripts, CI/CD pipelines, and documentation. On projects using Deployer 7, this means updating the deploy.php host configuration:

host('production')
    ->setHostname('your-server-ip')
    ->setPort(2222)
    ->setRemoteUser('deploy');

Firewall Hardening Beyond Port Changes

Port hardening alone is insufficient. Implement rate limiting at the firewall level as an additional layer:

sudo ufw limit 2222/tcp comment 'SSH rate limit'
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable

The limit rule allows connections but blocks IPs making more than 6 attempts in 30 seconds. This complements Fail2ban by catching volumetric attacks before they reach the SSH daemon. For servers behind Cloudflare or similar proxies, consider restricting SSH access to proxy IP ranges only, though this complicates direct emergency access.

Security Posture: Before vs After HardeningBEFORE (Default Ubuntu)✗ Password auth enabled✗ Root login permitted✗ Port 22 exposed✗ No intrusion prevention✗ Legacy crypto accepted✗ Unlimited auth attemptsRisk: HIGHAvg. compromise time: hours to daysLog noise: 1000+ entries/dayBrute force success rate: viableAFTER (Hardened)✓ Ed25519 keys only✓ Root login disabled✓ Non-standard port + UFW✓ Fail2ban auto-banning✓ Modern ciphers enforced✓ MaxAuthTries = 3Risk: LOWCompromise: cryptographically infeasibleLog noise: <50 entries/dayAutomated attacks: blocked at network
Quantifiable improvement when you Harden SSH: Key Auth, Fail2ban, and Port Hardening on production servers

What Are Common Mistakes When Teams Harden SSH: Key Auth, Fail2ban, and Port Hardening?

In 15 years of managing production infrastructure, I’ve seen hardened configurations fail due to operational oversights rather than technical flaws. Understanding these pitfalls prevents costly outages and false confidence.

MistakeConsequencePrevention
Disabling passwords before testing keysComplete lockout requiring console recoveryAlways test key auth in separate session first
Editing jail.conf instead of jail.localConfig overwritten on package upgradeUse .local overrides exclusively
Changing port without updating firewallSSH unreachable until physical/console accessAdd new port to UFW before modifying sshd_config
Ignoring trusted IPs in Fail2banCI/CD pipelines or team members bannedMaintain explicit ignoreip list for automation
Using RSA keys with weak paddingVulnerable to signature forgery attacksMigrate to Ed25519; reject RSA in sshd_config
Not validating sshd_config syntaxDaemon fails to restart, no remote accessAlways run sshd -t before systemctl restart

Another frequent issue is failing to update backup and monitoring systems. If your backup solution connects via SSH, it will break when you disable password auth or change ports. Audit all automated systems that touch the server: rsync jobs, monitoring agents, deployment tools, and third-party integrations. On eCommerce platforms with payment webhook receivers or inventory sync services, this step is critical. For teams evaluating whether their current setup meets modern standards, reviewing server security best practices in Nepal provides additional context specific to regional infrastructure constraints.

Key Management Hygiene

Hardening SSH is not a one-time task. Key management requires ongoing discipline:

  • Rotate keys annually or immediately upon personnel changes
  • Use unique keys per environment — never share production keys with staging
  • Store private keys encrypted with strong passphrases; use ssh-agent for convenience
  • Audit authorized_keys regularly — remove stale entries from departed team members
  • Consider certificate-based auth for large teams using HashiCorp Vault or Teleport

For solo developers or small agencies managing dozens of client sites, SSH certificates add complexity that may not justify the overhead. In those cases, disciplined key management with per-client keys stored in a password manager provides adequate security. The goal is sustainable security, not perfect security that gets abandoned under operational pressure.

Final Steps to Maintain Your Harden SSH: Key Auth, Fail2ban, and Port Hardening Setup

Implementing these controls takes approximately 30 minutes per server. The real work is maintaining them through upgrades, team changes, and incident response. Schedule quarterly reviews of SSH configurations, Fail2ban ban statistics, and key inventories. Document your hardening process so new team members inherit secure defaults rather than reinventing them.

Monitor Fail2ban effectiveness with weekly log reviews. If ban rates drop to near-zero, verify that bots haven’t shifted to other attack vectors (HTTP brute force, API abuse). Security is iterative. The configuration described here reflects current best practices for Ubuntu 24.04 and OpenSSH 9.x in 2026; revisit it when major versions change.

For teams managing multiple production servers across Nepal or globally, consistent hardening is only possible with infrastructure-as-code. Manual configuration drifts. Whether you use Ansible, Terraform, or simple shell scripts versioned in Git, automate these settings from day one. If you need assistance auditing existing servers or implementing hardened SSH across your fleet, reach out to discuss your infrastructure security needs.

Frequently Asked Questions

Passwords are vulnerable to brute-force attacks and credential stuffing. Key-based authentication uses cryptographic pairs that are computationally infeasible to guess. In my experience managing Ubuntu servers for Nepal-based clients, disabling password auth eliminates the vast majority of automated login attempts logged in auth.log.

Use ed25519 keys for best security and performance. Run ssh-keygen -t ed25519 -C "your_email@example.com" on your local machine. Ed25519 keys are shorter than RSA-4096 but offer equivalent or better security with faster authentication. Avoid DSA keys entirely as they are deprecated and considered insecure in modern OpenSSH versions.

Edit /etc/ssh/sshd_config and set Port to a non-standard value between 1024 and 65535. Always keep Port 22 active temporarily while testing the new port to avoid lockout. Update UFW rules with ufw allow NEW_PORT/tcp before restarting sshd. Only remove Port 22 after confirming connectivity on the new port.

Fail2ban monitors /var/log/auth.log for repeated failed authentication patterns and dynamically adds iptables or nftables rules to block offending IPs. On production Laravel servers I maintain, it typically bans IPs after three failures within ten minutes. This stops automated scanners without requiring manual firewall management or constant log review.

Yes, and you should. Key authentication prevents unauthorized access even if attackers guess usernames, while Fail2ban reduces log noise and server load from persistent scanners. They address different threat vectors. I configure both on every Ubuntu 22/24 server I deploy, including shared EC2 instances running multiple client sites.

You lose access to all servers configured with that key unless you have backup authentication methods. Always maintain console access through your hosting provider or cloud platform. Store encrypted backups of private keys securely. For critical infrastructure, configure multiple authorized keys per user or enable temporary password auth as an emergency recovery mechanism.

Never close your existing SSH session when testing changes. Open a second terminal window to verify new configurations work before disconnecting. Use sshd -t to validate config syntax before restarting the service. Keep root password authentication enabled temporarily during initial setup, then disable only after confirming key-based access functions correctly on the new port.

It reduces automated scan noise significantly but does not prevent targeted attacks. Determined attackers will find open ports quickly using nmap. The real benefit is cleaner logs and reduced resource consumption from bot traffic. Treat port changes as operational hygiene rather than a primary security control. Always combine with key auth and Fail2ban.

Set maxretry to 3, bantime to 3600 seconds, and findtime to 600 seconds in jail.local. Enable the sshd jail explicitly. Use backend = systemd for Ubuntu 22/24 instead of polling log files directly. These defaults balance security with legitimate user tolerance. Adjust bantime upward for persistent offenders based on your auth.log patterns.

Use your cloud provider's web console or VNC access to log in directly. Fix configuration errors in /etc/ssh/sshd_config and restart sshd. If Fail2ban blocked you, run fail2ban-client set sshd unbanip YOUR_IP from the console. Always test changes in a staging environment first. Maintain documented recovery procedures before applying hardening to production servers.

Yes, when users connect from predictable locations. Add AllowUsers or Match Address directives in sshd_config to limit access to known CIDR ranges. This provides defense-in-depth even if keys are compromised. For remote teams with dynamic IPs, combine with VPN access instead. On legal-tech portals I build, I often restrict admin SSH to office networks plus VPN endpoints.

Basic hardening including key setup, Fail2ban, and port change costs Rs 8,000 to 15,000 (USD 60 to 110) for a single server. Complex multi-server environments with custom firewall rules range Rs 25,000 to 40,000 (USD 190 to 300). Pricing depends on existing configuration state and documentation requirements. Ongoing monitoring adds monthly maintenance costs.

Forgetting to update firewall rules before changing ports causes immediate lockouts. Incorrect file permissions on authorized_keys (must be 600) or .ssh directory (700) silently fail key authentication. Disabling password auth before verifying key access works. Not reloading sshd after config changes. Always validate with sshd -t and test in parallel sessions before committing changes.

Each developer generates their own key pair and provides only the public key. Add individual entries to authorized_keys with comments identifying each user. Remove keys immediately when team members leave. Consider using SSH certificates via a CA for larger teams to simplify revocation. Never share private keys between users. Audit authorized_keys quarterly.

When servers do not need direct public SSH exposure. These tools eliminate open inbound ports entirely by routing traffic through encrypted overlays. They simplify access management for distributed teams and reduce attack surface. However, they add dependency on external services. For standalone servers with stable teams, traditional hardening remains simpler and more self-contained. Evaluate based on operational complexity tolerance.

Share this article

Quick Contact Options
Choose how you want to connect me: