Why the First 30 Minutes on a New Server Matter

A freshly installed Ubuntu server on a VPS or cloud instance is functional out of the box, but it is not secure by default. The default settings are well-documented, meaning attackers know exactly what to look for. Open SSH ports running on the standard port, root login enabled, and no firewall rules in place are all common attack vectors that automated bots scan for constantly.

Hardening a new server does not require advanced knowledge, but it does require knowing which steps matter most and in what order to apply them. Skipping steps or applying them in the wrong sequence can leave gaps. This guide walks through the essential hardening tasks that apply to any Ubuntu server exposed to the internet, whether it is a VPS, cloud instance, or physical server.

Creating a Non-Root User for Daily Administration

Logging in as root gives you unrestricted access to everything on the system, including the ability to accidentally delete critical files or change settings that break the server. Best practice is to create a regular user with sudo privileges and use that account for day-to-day administration.

adduser deploy
usermod -aG sudo deploy

The deploy user can now run administrative commands by prefixing them with sudo. Before relying on sudo for critical tasks, test that it works correctly:

su - deploy
sudo ls /root

If ls displays the contents of /root without errors, sudo is configured properly for that user. You should also set up key-based authentication for this account to avoid using passwords for SSH connections.

Setting Up SSH Key-Based Authentication

Key-based authentication is more secure than password authentication because it cannot be brute-forced in the same way. Create the required directory structure and set appropriate permissions:

mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Copy your public key to the server using ssh-copy-id or by manually appending the key content to ~/.ssh/authorized_keys. Once key-based authentication is confirmed to work, you can disable password authentication entirely in the SSH daemon configuration.

Hardening the SSH Daemon Configuration

The SSH configuration file at /etc/ssh/sshd_config controls how users can connect to the server. Open it with a text editor:

sudo nano /etc/ssh/sshd_config

Apply these settings to strengthen the default configuration:

Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

Changing the SSH port from 22 to something less common like 2222 does not provide true security, but it significantly reduces automated bot traffic since most bots scan only port 22 by default. Think of it as noise reduction rather than a security measure on its own.

The MaxAuthTries 3 setting disconnects after three failed authentication attempts, which helps protect against brute force attempts. The ClientAliveInterval and ClientAliveCountMax settings work together to close idle connections after 10 minutes, preventing resource exhaustion from forgotten sessions left open.

After saving the configuration, always test it before restarting the service:

sudo sshd -t

If no errors are reported, reload the SSH configuration:

sudo systemctl reload sshd

Keep your existing SSH session open and test the new configuration in a separate terminal before logging out. If the new settings break SSH access somehow, your existing session lets you fix the problem without losing connectivity to the server.

For a deeper walkthrough of SSH hardening techniques, including additional configuration options and key setup, see the guide on securing SSH on Ubuntu.

Configuring the Firewall with UFW

Ubuntu includes UFW (Uncomplicated Firewall), which provides a simplified interface to manage iptables rules. Before enabling the firewall, make sure you allow SSH on the new port to avoid locking yourself out:

sudo ufw allow 2222/tcp
sudo ufw allow 'Apache Full'
sudo ufw enable

If you are running other services such as mail servers, databases on non-standard ports, or custom applications, allow their respective ports before enabling the firewall. Run sudo ufw status numbered to see all rules in order. UFW's default policy already sets default-deny for incoming connections, which is the recommended setting for servers.

Installing and Configuring Fail2Ban

Fail2Ban monitors log files and automatically bans IP addresses that repeatedly fail authentication. It is particularly effective against brute force attacks on SSH and web services. Install it first:

sudo apt install fail2ban -y

The default configuration works, but you should copy the configuration file to a local override file so your settings are not overwritten when the package receives updates:

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

Update the SSH jail to use your custom port and adjust the ban settings to suit your environment:

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

The bantime value sets the ban duration in seconds, so 3600 equals one hour. For more detailed Fail2Ban configuration including HTTP protection and custom filter settings, the guide on Fail2Ban SSH and HTTP protection covers additional options.

Start and enable the service:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Check the current status:

sudo fail2ban-client status

Enabling Automatic Security Updates

Security patches are released regularly, and applying them manually is easy to forget, especially if you manage multiple servers. Ubuntu can install security updates automatically without requiring your attention each time.

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure unattended-upgrades

Choose Yes when prompted to enable automatic updates. The configuration is stored in /etc/apt/apt.conf.d/50unattended-upgrades. Verify the setup is working:

sudo unattended-upgrades --dry-run

For servers where you want to receive notifications when updates are applied, configure email alerts in the unattended-upgrades configuration file. This is particularly useful for production environments where knowing what changed matters for auditing purposes.

Disabling Unnecessary Services

A default Ubuntu server installation includes services you may not need. Each running service represents a potential attack surface. Check what is currently running:

systemctl list-units --type=service --state=running

Review the list and disable services you do not use. Common services to check on a web server include:

  • CUPS: The print server. Unless you are printing directly from the server, it should not be running.
  • Bluetooth: Not needed on a headless server.
  • Avahi: Service discovery daemon. Useful on local networks but unnecessary on public servers.
  • atd: The at scheduling daemon. Disable if you do not use it.

Stop and disable a service with:

sudo systemctl stop cups
sudo systemctl disable cups

Configuring Swap Space

Swap is disk space used as virtual memory when physical RAM is full. Without swap configured, a server under memory pressure can trigger the Linux Out of Memory Killer, which arbitrarily terminates processes to free up memory. This can kill critical services without warning. Check if swap is already configured:

sudo swapon --show

If no swap is shown, create a swap file:

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Make the swap file permanent by adding it to /etc/fstab:

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Set the swappiness value to control how aggressively the kernel uses swap. A value of 10 means the kernel swaps only when RAM usage reaches approximately 90%:

echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

Hardening System Kernel Parameters

The Linux kernel controls network behaviour and memory management at a low level. Edit /etc/sysctl.conf to add network hardening settings:

sudo nano /etc/sysctl.conf

Add these settings to improve the server's resistance to common network-based attacks:

# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Do not accept IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0

# Log suspicious packets
net.ipv4.conf.all.log_martians = 1

Apply the changes without rebooting:

sudo sysctl -p

These settings prevent IP spoofing, disable source routing, and log suspicious packets. They represent standard hardening practices for any publicly accessible server. For a more comprehensive look at Ubuntu server hardening, the guide on Ubuntu 22.04 security hardening covers additional steps.

Verifying the Hardened Configuration

After completing the hardening steps, verify the server is in a defensible state before considering it ready for production use:

sudo ufw status
sudo systemctl status fail2ban
sudo ss -tulpn | grep LISTEN

The ufw status command shows all active firewall rules. The fail2ban status confirms the service is running and monitoring for suspicious activity. The ss command lists all listening ports and the services associated with them.

Review the output carefully. Confirm only the ports you expect to be open are actually listening. Any unexpected listening service should be investigated. Either disable it if it is unnecessary, or secure it properly if it is required for your setup.

Next Steps After Hardening

Hardening the server is only part of maintaining a secure environment. Once the initial setup is complete, ongoing maintenance becomes the priority. This includes monitoring logs regularly, testing backups, and reviewing access controls when team members join or leave.

If you are running a web server alongside these hardening steps, Apache security configuration also deserves attention. The guide on securing Apache HTTPd settings covers additional steps for protecting web services specifically.

Backups should be tested regularly. A backup that has never been verified may fail when you need it most. Schedule time to test restore procedures on a regular basis.