Secure SSH on Ubuntu: Port Configuration & Key Setup

18 min read 3,466 words
How to Secure SSH on Ubuntu: Ports, Keys, Fail2Ban & GeoIP featured image

Why SSH Security Matters for Ubuntu Servers

SSH, or Secure Shell, is the standard method for accessing Linux servers remotely. It provides a text-based terminal connection to your server, giving you full control over the machine within your user permissions. That level of control is exactly why SSH is the most frequently attacked service on any internet-facing Linux server.

Automated bots constantly scan the internet looking for servers with open SSH ports. They run through common usernames and passwords without any specific target in mind. The approach is purely volume-based: try enough combinations across enough servers, and eventually some will succeed. If your server uses password authentication on the default port, it will receive these automated login attempts within hours of going live. Most are harmless bots, but the door they are probing should be closed before something more serious comes along.

Securing SSH is not an optional hardening step. It is foundational server hygiene, and implementing it properly takes less than an hour. This guide covers the practical measures that make a real difference: port configuration, SSH key authentication, Fail2Ban, IP restrictions, and the supporting steps that keep your configuration effective over time.

Checking Your Current SSH Status on Ubuntu

Before making any changes, confirm SSH is installed and running on your Ubuntu server:

ssh -V

This returns the OpenSSH version number. If the command is not found, install the SSH server:

sudo apt update && sudo apt upgrade -y

sudo apt install openssh-server -y

Start the SSH service and ensure it starts automatically on boot:

sudo systemctl start ssh

sudo systemctl enable ssh

Check your current firewall status. UFW comes pre-configured on many Ubuntu installations and will block your SSH connection if you change the port without updating the firewall rules first. This is one of the most common ways people lock themselves out of remote servers:

sudo ufw status

If UFW is active, note the current allowed ports before making any changes. You need to ensure your new SSH port is permitted in the firewall before you restart the SSH service, or you will lose remote access to the server.

Step 1: Change the Default SSH Port

The default SSH configuration listens on port 22 and permits password authentication. These defaults are practical for initial server setup but are not appropriate for production environments. Moving SSH to a different port eliminates the majority of automated scans because bots target port 22 by default. This does not stop a determined attacker, but it significantly reduces the noise from opportunistic bots.

Edit the SSH daemon configuration file:

sudo nano /etc/ssh/sshd_config

Find the line Port 22 and change it to your chosen port. Select a number between 1024 and 65535 that is not already in use by another service. Port 2222 is a common choice:

# Port 22

Port 2222

Save the file and update your firewall to allow the new port before restarting the SSH service:

sudo ufw allow 2222/tcp

sudo ufw delete allow 22/tcp

sudo systemctl restart sshd

Do not close your existing terminal session yet. Open a new terminal window and test the connection to the new port:

ssh -p 2222 username@yourserverip

If the connection succeeds, your configuration is working correctly. You can safely close the original session. If it fails, your original session remains active so you can troubleshoot the configuration without losing access to the server.

If you do lock yourself out, most cloud providers offer console access that bypasses the SSH service entirely. AWS EC2 Instance Connect, DigitalOcean VNC console, and similar tools allow you to access the server directly through the provider's interface and fix the firewall rules from there.

Step 2: Set Up SSH Key Authentication

SSH keys use asymmetric cryptography to authenticate connections. You generate a pair of files: a private key that stays on your local computer and a public key that you install on the server. When you connect, the server challenges you to prove you hold the matching private key. This is fundamentally more secure than passwords because private keys cannot be guessed by automated tools, and compromising one would require either physical access to your computer or a cryptographic breakthrough.

Generating a Key Pair on Linux and macOS

Run the key generation command on your local machine:

ssh-keygen -t rsa -b 4096

When prompted for a file location, press Enter to accept the default location. When prompted for a passphrase, enter one. A passphrase adds an extra layer of protection: even if someone copies your private key file, they cannot use it without the passphrase. It is worth setting this even if it adds a small step to your login process.

Copy the public key to your server using the SSH key copy utility:

ssh-copy-id -p 2222 username@yourserverip

This command connects to the server, creates the ~/.ssh/ directory if it does not exist, and appends your public key to the ~/.ssh/authorized_keys file. Test the key-based login:

ssh -p 2222 username@yourserverip

If you are prompted for your key passphrase rather than the server password, key authentication is working correctly.

Generating a Key Pair on Windows Using PuTTY and Pageant

Download and open PuTTYgen. Set the Type to RSA and the Bits to 4096. Click Generate and move your mouse over the blank area as instructed to create randomness. Save the private key to a secure location with a strong passphrase. Copy the public key text displayed in the window.

Connect to your server with your password one more time and add the public key to the authorized keys file:

mkdir -p ~/.ssh

nano ~/.ssh/authorized_keys

Paste the public key text on its own line, then save and close the file. Set correct permissions on the SSH directory and authorized keys file:

chmod 700 ~/.ssh

chmod 600 ~/.ssh/authorized_keys

Permission errors are a common reason key authentication fails. SSH enforces strict file permissions and will refuse to use authorized_keys files that are readable by group or world. This security measure prevents other users on the same system from reading your authorized keys file.

To use Pageant, which stores your key in memory and provides it automatically to PuTTY connections, run Pageant and add your private key file through the system tray icon. Configure PuTTY to use Pageant for authentication under Connection, SSH, Auth. Load your saved session and connect. Pageant will supply your key automatically without prompting for the passphrase each time, as long as Pageant is running.

Step 3: Disable Password Authentication

Once you have confirmed that key-based login works reliably, disable password authentication. This step removes the primary attack vector that automated bots exploit.

Edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

Find and update these lines:

PasswordAuthentication no

PermitRootLogin no

PubkeyAuthentication yes

Setting PermitRootLogin no prevents direct root login over SSH. Even if someone obtains your root password, they cannot use it to log in directly. Always log in as a regular user and use sudo for administrative tasks. This limits the potential damage if any single account is compromised.

Reload the SSH configuration to apply the changes:

sudo systemctl reload sshd

Keep your existing terminal session open while testing the new configuration. Open a new terminal and attempt to log in with key authentication. If it works, password authentication is properly disabled. If something goes wrong, your existing session remains active so you can investigate and correct the configuration.

A common error at this step is editing the wrong line in sshd_config. Look carefully at the actual content of the file. Lines beginning with # are comments and inactive. You need to ensure the active (uncommented) lines are set correctly. If PasswordAuthentication yes appears elsewhere in the file, it overrides any commented PasswordAuthentication no you may have added.

Step 4: Restrict SSH Access by IP Address

If your server has a fixed IP address or you access it from a known set of locations, restrict SSH access to those sources. This makes your SSH service invisible to connection attempts from any other IP address, eliminating the vast majority of automated attacks.

Find your current public IP address:

curl ifconfig.me

Allow SSH only from your IP address:

sudo ufw allow from YOUR.IP.ADDRESS.HERE to any port 2222 proto tcp

If you need SSH access from multiple locations or have a dynamic IP address that changes regularly, consider using a VPN such as WireGuard to establish a trusted network first, then restrict SSH to the VPN interface only. This is significantly more secure than allowing SSH from arbitrary IPs, but it requires additional configuration.

For small business servers where access typically comes from a fixed office location or known set of locations, IP restriction is the most effective additional layer of protection after key authentication. It requires minimal setup and eliminates an entire category of threat.

Step 5: Protect Against Brute Force with Fail2Ban

Fail2Ban monitors authentication log files and automatically blocks IP addresses that repeatedly fail to authenticate. It is lightweight and effective against sustained dictionary attacks targeting SSH. Even with password authentication disabled, Fail2Ban is worth running because it protects against other types of probes that attempt to identify your server's software version, open ports, or running services.

Install Fail2Ban:

sudo apt install fail2ban -y

Create a local configuration file to override the defaults, which can be overwritten during package updates:

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

sudo nano /etc/fail2ban/jail.local

Find the [sshd] section and update it with settings appropriate for your environment:

[sshd]

enabled = true

port = 2222

filter = sshd

logpath = /var/log/auth.log

maxretry = 3

bantime = 3600

findtime = 600

The settings work as follows: maxretry = 3 means an IP is banned after three failed login attempts within the time window. bantime = 3600 bans the IP for one hour. findtime = 600 sets the window to 10 minutes. Adjust these values based on your usage patterns. If you occasionally mistype your key passphrase, consider setting maxretry higher to avoid banning your own IP address accidentally.

Start and enable Fail2Ban:

sudo systemctl start fail2ban

sudo systemctl enable fail2ban

Check the status of the SSH jail to see how many IPs are currently banned:

sudo fail2ban-client status sshd

The output shows the number of banned IP addresses and lists them individually. For a detailed walkthrough of configuring Fail2Ban specifically for SSH and HTTP services, including custom filter rules and notification setup, refer to the Fail2Ban SSH and HTTP protection guide.

Step 6: Block SSH Traffic by Country Using GeoIP Blocking

If your business operates domestically and you do not need remote access from abroad, you can block all SSH connections from foreign IP ranges. This is optional but dramatically reduces the volume of traffic reaching your SSH service and eliminates categories of attack traffic that originate from specific countries.

Install the required tools:

sudo apt install xtables-addons-common libtext-csv-xs perl libnet-cidr-lite-perl -y

Create the GeoIP directory and build the database from country IP range data:

mkdir -p /usr/share/xt_geoip

cd /usr/share/xt_geoip

sudo /usr/lib/xt_geoip/xte_geoip_build -D . /usr/share/xt_geoip/*.csv 2>/dev/null || \

 (wget https://download.db-ip.com/free/dbip-country-lite.csv.gz && \

 gunzip dbip-country-lite.csv.gz && \

 sudo /usr/lib/xt_geoip/xte_geoip_build -D . .)

The xte_geoip_build command processes country IP ranges into a binary format that iptables can use for matching. The exact invocation depends on which CSV package you use. If the automated build fails, download country IP data directly and build the database manually.

Block traffic from specific countries by IP range. For example, to block China and Russia, which are the source of a disproportionate amount of SSH brute-force traffic:

sudo iptables -A INPUT -p tcp --dport 2222 -m geoip --src-cc CN,RU -j DROP

The --src-cc flag accepts a comma-separated list of two-letter country codes. Add or remove countries based on where you have legitimate business access requirements.

Make the rules persistent across reboots:

sudo apt install iptables-persistent

sudo netfilter-persistent save

Review your active iptables rules to verify the configuration:

sudo iptables -L -n --line-numbers

This shows all active rules in order. Verify your GeoIP blocking rules are present and positioned correctly, before any ACCEPT rules for the same port.

Step 7: Monitor SSH Access Logs

Regular log review catches unusual access patterns before they become serious incidents. The main SSH authentication log on Ubuntu is at /var/log/auth.log.

View recent failed login attempts:

sudo grep "Failed password" /var/log/auth.log | tail -20

View successful logins to verify expected access:

sudo grep "Accepted" /var/log/auth.log | tail -20

Monitor logs in real time during troubleshooting:

sudo tail -f /var/log/auth.log

The journalctl command also provides a filtered view:

sudo journalctl -u ssh | tail -50

Automated monitoring is more reliable than manual log review for production servers. Configure Fail2Ban to send alerts when it bans IP addresses, or use a log monitoring tool that triggers notifications when specific patterns appear, such as failed root logins, logins from unusual IP ranges, or authentication failures from known hostile networks.

Step 8: Additional SSH Hardening Options

Several other settings in /etc/ssh/sshd_config improve security with minimal disruption to normal use. Edit the file and consider adding these options:

LoginGraceTime 60

This sets a 60-second limit for completing authentication. The default is 120 seconds, which is longer than necessary for key or password authentication and can be exploited to hold connections open indefinitely against the SSH daemon.

PermitEmptyPasswords no

Refuse authentication attempts with empty passwords. This is already the default in most OpenSSH installations, but setting it explicitly prevents issues if the default changes in a future update.

MaxStartups 10:30:100

Limits concurrent unauthenticated connections. The format is start:rate:full: 10 unauthenticated connections are allowed, after which new connections are randomly dropped with a 30 percent probability, scaling up to 100 at which point all new connections are refused. This mitigates connection exhaustion attacks against the SSH service.

ClientAliveInterval 300

ClientAliveCountMax 2

These settings terminate idle connections after 5 minutes of inactivity. Useful if you want to ensure sessions do not remain open indefinitely when you forget to disconnect.

After changing these settings, reload SSH and test thoroughly:

sudo systemctl reload sshd

Ubuntu SSH Hardening Checklist for Production Use

Before deploying a server, apply this checklist of SSH hardening measures. These form the baseline security configuration for any Ubuntu server that will be accessible from the internet:

  • Non-default SSH port: Port 22 is automatically scanned by bots. Any other port receives significantly fewer probes and automated attempts.
  • SSH key authentication only: Disable password authentication entirely. Keys are the only acceptable authentication method for production servers.
  • Root login disabled: No direct root access over SSH. Always log in as a regular user with sudo privileges.
  • IP restriction: Allow SSH only from known IP addresses or VPN endpoints where access patterns permit.
  • Fail2Ban active: Automated blocking of repeated authentication failures to protect against brute force attempts.
  • GeoIP blocking where applicable: Block SSH access from countries where you have no legitimate business requirements.
  • Failed authentication monitoring: Regular review of authentication failures in auth.log, or automated alerting configured.
  • Idle session timeout: Configure ClientAliveInterval to terminate forgotten sessions and reduce exposure windows.

Apply these measures in a test environment before deploying to production. Document the changes so the configuration can be reviewed and audited independently. A complete Ubuntu server hardening checklist covers additional system-level security measures beyond SSH that complement these configuration changes.

Building an Effective SSH Security Setup

SSH security works best as a layered approach. Each control on its own provides limited protection. Together, they make your server significantly harder to compromise and significantly less interesting to automated attackers.

The minimum effective configuration for any internet-facing server is: a non-default port, SSH key authentication only, password authentication disabled, and Fail2Ban monitoring for repeated authentication failures. IP restriction and GeoIP blocking add further protection where your access patterns allow them.

Test each change before moving to the next. Keep one terminal session open while testing each modification so you can recover if something breaks. Document your configuration so the hardening is reproducible and auditable. Schedule a regular review of your authentication logs and your Fail2Ban status so you catch anomalies before they become problems.

If you need help reviewing your current SSH configuration or implementing these hardening measures, prepare details of your current setup, the Ubuntu version you are running, and the access patterns your server needs before getting in touch.

Frequently Asked Questions

Is changing the SSH port enough to secure my server?
No. Changing the port reduces automated scans but does not protect against targeted attacks or compromised credentials. You need key-based authentication, password authentication disabled, and ideally IP restrictions or Fail2Ban monitoring as well. Port change is one layer among several. Think of it as removing your server from the list that opportunistic bots check first. Anyone who specifically targets your server will still attempt access regardless of which port SSH is listening on.
I lost my private key and cannot log in. What do I do?
If you have console access through your hosting provider, such as AWS EC2 Instance Connect or DigitalOcean VNC console, you can access the server directly and edit the SSH configuration to temporarily re-enable password authentication. Alternatively, add a new public key to the authorized_keys file through the console.
Can I use the same SSH key across multiple servers?
Yes. Your private key authenticates you to any server where your public key is installed in authorized_keys. However, if your private key is compromised, every server using that key is potentially affected. For larger environments, consider using an SSH certificate authority to manage trust centrally and set expiry dates on certificates. For small environments with a handful of servers, the practical risk of key reuse is low if the key is well-protected with a strong passphrase and stored securely on your local machine.
Does Fail2Ban interfere with legitimate users who mistype their password?
Fail2Ban only bans IP addresses after the configured number of failed attempts within the defined time window. A legitimate user who types their key passphrase incorrectly once is not affected. It only triggers on repeated failures. With key-based authentication, failed passphrase attempts are rare in normal use, so Fail2Ban typically only activates against automated brute-force attempts. You can also set higher maxretry values if you find yourself frequently locked out due to typos.
Should I use key-based authentication for automated scripts that log into the server?
Yes. For automated scripts that run on a schedule, such as cron jobs or deployment scripts, use a dedicated SSH key pair that is restricted to the specific commands the script needs to run. Use the command=... restriction in authorized_keys to limit what the key can execute.
How often should I review my SSH configuration?
Review your SSH configuration quarterly at minimum, or after any server incident, staff change, or network architecture modification. Check that Fail2Ban is running and has not accumulated an unusually high number of banned IPs, which may indicate increased attack activity. Verify that authorized_keys files contain only keys you recognise. Remove any keys for staff who have left the organisation. Regular reviews ensure your hardening measures remain effective as your server environment evolves over time.
What should I do if I see successful logins from unfamiliar IP addresses?
Immediately investigate. Check whether the IP belongs to a location or service you recognise, such as your office, a VPN provider, or a trusted colleague's connection. If the login is genuinely unfamiliar, assume the account is compromised until proven otherwise. Revoke the public key from authorized_keys, change your private key passphrase, and review the server for any unexpected changes such as new user accounts, modified cron jobs, or unfamiliar processes running on the system.
How does SSH hardening fit into overall server security?
SSH hardening is one component of a broader server security posture. Alongside securing SSH, you should configure automatic security updates, a properly configured firewall, system-level access controls, and regular backup procedures. For a comprehensive approach to securing Ubuntu servers, the Ubuntu server hardening guide covers the additional measures that work alongside your SSH configuration to protect the overall system.