The median time between a CVE going public and an attacker weaponizing it dropped to 5 days in 2025, according to the Rapid7 2026 Global Threat Landscape Report. Your cloud provider hands you a fresh Ubuntu server. You think you have time to configure it properly. You don’t.
Every fresh VPS ships with root SSH enabled, password authentication on, no firewall rules, and zero intrusion detection. That’s not a vendor oversight. It’s a deliberate tradeoff between compatibility and security. The problem is that the tradeoff was made for general audiences, not for a server that’s about to receive real traffic.
This guide teaches you the mental model behind Linux server hardening, then walks through four concrete steps: SSH lockdown, UFW firewall setup, fail2ban installation, and automatic security updates. You’ll finish in under 30 minutes and eliminate the most common attack vectors.
Learn Linux from scratch with the EPT Lab Linux fundamentals course.
Key Takeaways - A fresh VPS has 120+ settings that fail CIS Level 2 secure configuration (CIS Benchmarks, Oct 2024) - The median time-to-exploit for top routinely exploited CVEs dropped to 5 days in 2025; organizations still take a median 32 days to patch (Rapid7 2026) - SSH key auth +
PermitRootLogin noeliminates the attack vector behind 427 million annual brute-force attempts - UFW deny-by-default + fail2ban + unattended-upgrades form a layered defense you can deploy in one session
A fresh Ubuntu 24.04 LTS server fails more than 120 default settings when measured against the CIS Level 2 Benchmark (CIS Benchmarks, Oct 2024). That number surprises most people. It shouldn’t. “Secure by default” is a marketing phrase, not a technical guarantee.
The mental model you need is this: attack surface equals everything that can receive input. A network port is an input. An SSH service accepting password logins is an input. An unpatched kernel module is an input. Every one of those inputs is a potential entry point, and a fresh VPS has dozens of them open by default.
[UNIQUE INSIGHT] Most hardening guides give you a checklist. What they skip is the threat model. Without understanding why each surface exists, you can’t prioritize which ones to close first. A VPS facing the public internet has three main attack vectors.
Open ports. Services bind to ports. Ports are visible to anyone who scans your IP. A port scanner will find your server within minutes of it going live. That’s not paranoia; it’s documented behavior in honeypot research.
Default or weak credentials. Root SSH with password authentication is the single most exploited configuration on cloud servers. Attackers don’t need zero-days. They use credential lists.
Unpatched software. Known vulnerabilities with public exploits are indexed by the attack community. If your kernel or SSH daemon hasn’t been updated, a script can match your version to a CVE and exploit it automatically.
Citation Capsule: The CIS Benchmark for Ubuntu 24.04 LTS identifies more than 120 default configuration settings that fail Level 2 secure configuration. These include open network services, default SSH settings, world-readable directories, and missing kernel hardening flags. (CIS Benchmarks, Oct 2024)
See the EPT Lab sysadmin fundamentals series for the Linux mental models this guide assumes.
The Rapid7 2026 Global Threat Landscape Report tracks two different metrics for how fast attackers move. The median time-to-exploit for the top routinely exploited CVEs dropped from 8.5 days in 2024 to 5.0 days in 2025 — the headline number for the most aggressively targeted vulnerabilities. Across all CVEs tracked, the average weaponization time is 19.5 days, while organizations take a median of 30.6 days to patch — an 11-day structural exposure window baked into every patch cycle (Rapid7 2026 Global Threat Landscape Report, Mar 2026). Either figure is bad; the 5-day number tells you how fast the worst-case scenarios move.
The mental model here is uncomfortable: the exploit clock starts at CVE publication, not at your awareness. You don’t have to know about a vulnerability for it to be used against you. The moment a CVE goes public, automated scanners start probing the internet for vulnerable versions.
It gets worse. The CISA Known Exploited Vulnerabilities (KEV) catalog documents cases where exploits were already active before the corresponding CVE was publicly disclosed. Even with perfect situational awareness, organizations taking 30+ days to patch are structurally exposed.
In 2025, the NIST National Vulnerability Database recorded 48,185 new CVEs — approximately 131 per day. The Linux kernel alone accumulated 2,879 of them (NIST NVD, 2025 CVE Statistics). Even with perfect awareness, manual patching at that velocity is impossible.
This is why automatic security updates aren’t a convenience. They’re the only realistic response to a 5-day exploitation window.
For monitoring what gets patched, see the EPT Lab guide to reading apt logs.
A 2024 USENIX NSDI study from the University of Utah — Where The Wild Things Are: Brute-Force SSH Attacks In The Wild And How To Stop Them — analyzed 427 million SSH brute-force attempts across 500+ servers over four years and concluded that attacks are “rapidly growing more aggressive” (Singh et al., USENIX NSDI 2024). SSH is not a niche attack surface. It’s the primary target for automated credential stuffing on every public-facing Linux server.
The mental model: SSH is your front door. Default Ubuntu configuration leaves it wide open: root login enabled, password authentication accepted, standard port 22. You’re going to close all three.
Don’t do anything else until you’ve created a user account with sudo access. Logging in as root gives an attacker who gains access complete, unrestricted control.
# Run as root on your fresh VPS
adduser deploy
usermod -aG sudo deploy
Verify the group assignment:
groups deploy
# Expected output: deploy : deploy sudo
Run this on your laptop or workstation, not the server:
ssh-keygen -t ed25519 -C "your-email@example.com"
# Follow the prompts; save to ~/.ssh/id_ed25519
# Set a passphrase — this matters
ED25519 keys are faster and more resistant to side-channel attacks than RSA 2048. Don’t skip the passphrase.
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@YOUR_SERVER_IP
# You'll be prompted for deploy's password (last time you'll need it)
Test that key auth works before you disable passwords:
ssh -i ~/.ssh/id_ed25519 deploy@YOUR_SERVER_IP
# You should land on the server without a password prompt
Edit the SSH config on the server:
sudo nano /etc/ssh/sshd_config
Find and change these lines (or add them if missing):
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Optional: change the default port
# Port 2222
Restart the daemon:
sudo systemctl restart sshd
# Keep your current session open while you test in a NEW terminal window
Open a second terminal and verify you can still connect with your key. If it works, close the old session. If it fails, you still have an open session to fix it.
[PERSONAL EXPERIENCE] The first time you disable password authentication and reconnect successfully, there’s a specific moment of relief followed immediately by the realization that you’ve also locked out anyone without the key. Keep a backup access method, whether that’s your cloud provider’s console or a second authorized key, before you close that original terminal.
Citation Capsule: A University of Utah study published at USENIX NSDI 2024 analyzed 427 million SSH brute-force attempts across more than 500 honeypot servers over four years. The researchers found attacks are “rapidly growing more aggressive,” with automated tools cycling through credential lists at increasing speed and scale. (Singh et al., USENIX NSDI 2024)
For key rotation, agent forwarding, and managing access across multiple servers, see the EPT Lab SSH key management guide.
In 2025, the Verizon Data Breach Investigations Report found that vulnerability exploitation was the initial access vector in 20% of all breaches — a 34% year-over-year increase — while human error and misconfiguration drove a further quarter of incidents (Verizon DBIR 2025, Apr 2025). IBM’s Cost of a Data Breach Report 2024 puts the global average cost at $4.88 million per incident (IBM Cost of a Data Breach Report 2024, Jul 2024). A missing firewall rule is misconfiguration. It’s also one of the fastest to fix.
Run ufw status on a fresh Ubuntu server and you’ll see
one word: inactive. There are no firewall rules. Every port
that a running service binds to is immediately reachable from the
internet.
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (use your custom port if you changed it)
sudo ufw allow 22/tcp
# Allow web traffic if this is a web server
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# You'll be asked to confirm — type y
# Verify the active rules
sudo ufw status verbose
Expected output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Allow-by-default means every port is open until you explicitly close it. Deny-by-default means every port is closed until you explicitly open it. These aren’t equivalent starting positions; they have completely different threat profiles.
With allow-by-default, you have to know which ports to close. With deny-by-default, you only have to know which ports your application needs. The second problem is dramatically smaller and easier to audit.
[UNIQUE INSIGHT] The common mistake is treating a firewall as a list of things to block. That mental model puts you in reactive mode: you block what you’ve seen before. Deny-by-default flips this. You’re now describing what legitimate traffic looks like. Anything that doesn’t match that description is dropped, including attack patterns you’ve never seen.
The same USENIX NSDI 2024 study found that fail2ban’s default configuration blocks approximately 66.1% of SSH brute-force attempts. That sounds good until you read the inverse: 34% of attempts still get through on default settings (Singh et al., USENIX NSDI 2024). fail2ban is a rate-limiter and an IP blocker. It’s not a replacement for SSH key authentication.
The mental model: fail2ban watches log files, detects repeated failed logins from a single IP, and writes a temporary block rule to iptables. It buys time. It slows attackers down. Against credential stuffing from distributed IP ranges, it’s limited. That’s why it works best as a layer on top of SSH keys, not instead of them.
sudo apt install fail2ban -y
# Never edit jail.conf directly — it gets overwritten on upgrades
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Find the [sshd] section and configure it:
[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 1h
findtime = 10m
This bans any IP that fails authentication 3 times within 10 minutes, for 1 hour.
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check that the SSH jail is active
sudo fail2ban-client status sshd
Expected output:
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
`- Actions
|- Currently banned: 0
|- Total banned: 0
Why run fail2ban at all if 34% of attempts still get through? Because you’re not choosing between fail2ban and SSH keys. You’re running both. SSH key authentication means a brute-force attempt against password authentication fails immediately at the protocol level. fail2ban then bans the IP, reducing log noise and protecting any other services on the server.
The USENIX study also found that more sophisticated blocking strategies, like dictionary-based IP blocking, can stop 99.5% of attempts. fail2ban’s 66% is a floor, not a ceiling. You can tune it.
Citation Capsule: A University of Utah analysis of 427 million SSH brute-force attempts found that fail2ban’s default configuration blocks approximately 66.1% of attempts, while dictionary-based IP blocking blocks 99.5%. The gap exists because fail2ban operates reactively per-IP, while dictionary blocking targets known attack infrastructure proactively. (Singh et al., USENIX NSDI 2024)
The Verizon DBIR 2025 found that only 54% of vulnerable devices are fully remediated within a year, with a median patch time of 32 days (Verizon Data Breach Investigations Report 2025). Against a 5-day median exploitation window, 32 days isn’t a patch cadence. It’s an exposure guarantee.
The unattended-upgrades package solves the problem for
security patches specifically. It won’t update major versions or break
your application dependencies. It watches the security pocket of the
Ubuntu repository and applies patches automatically.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Select "Yes" at the prompt
Verify the configuration was written:
cat /etc/apt/apt.conf.d/20auto-upgrades
Expected output:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
The 1 values mean: check for updates daily, apply
security upgrades daily. You can also confirm which repositories are
covered:
cat /etc/apt/apt.conf.d/50unattended-upgrades | grep -A2 "Allowed-Origins"
# Should show Ubuntu security pocket enabled
With unattended-upgrades active, your effective patch
window for security updates drops from 32 days to under 24 hours. That’s
not a complete solution. But it eliminates the gap between CVE
publication and your first line of defense.
Configure email alerts for upgrade events with the EPT Lab unattended-upgrades monitoring guide.
If you want to go beyond these four steps, the EPT Lab Linux Security course covers the full CIS Benchmark implementation, intrusion detection with OSSEC and AIDE, and centralized log monitoring.
Verizon’s DBIR 2025 reported that supply chain breaches doubled from 15% to 30% of incidents in a single year, and ransomware appeared in 44% of all breaches, with 88% of those targeting small and medium businesses (Verizon DBIR 2025). Layer-one hardening doesn’t stop supply chain attacks. It does eliminate the low-effort intrusions that dominate the attack landscape.
What you’ve built after these four steps:
The remaining attack surface is smaller and harder to exploit. But it still exists.
What comes after this guide? Intrusion detection with OSSEC or AIDE lets you monitor file integrity changes, which is how you catch attackers who’ve already gotten in. Centralized log shipping to a SIEM means your logs survive if the server is compromised. A full CIS Benchmark Level 2 audit will surface the remaining 100+ configuration gaps that this guide doesn’t cover.
The next step is a full CIS Benchmark Level 2 audit for Ubuntu 24.04 — covered in the EPT Lab Linux Security course.
The four steps in this guide, from creating a non-root user to
enabling automatic updates, take under 30 minutes on a fresh server. A
full CIS Benchmark Level 2 audit takes 2 to 3 hours and requires working
through 200+ individual controls. Start with the four steps here, then
run the CIS audit tool (ubuntu_cis_hardening.sh) when you
have a maintenance window.
Yes. “Brand new” describes the server’s age, not its security posture. The CISA Known Exploited Vulnerabilities catalog documents exploits active before their CVE was published, and Rapid7’s 2026 Threat Landscape Report found attackers weaponize vulnerabilities in 19.5 days on average (Rapid7 2026). A fresh VPS running an unpatched kernel is vulnerable to CVEs published before you provisioned it. Age provides no protection.
On its own, fail2ban blocks approximately 66% of SSH brute-force attempts on default configuration (Singh et al., USENIX NSDI 2024). The remaining 34% get through, whether from distributed IP ranges or slow-and-low attacks that stay under the rate threshold. SSH key authentication eliminates password-based brute force entirely. Run both, not one or the other.
UFW is a frontend for iptables. When you run
ufw allow 22/tcp, UFW translates that into iptables rules
and writes them to the kernel’s packet filtering system. You’re always
using iptables underneath. UFW’s value is readability and the
deny-by-default posture it enforces from the first
ufw enable command. Without it, Ubuntu ships with no active
iptables rules, leaving all 120+ CIS-flagged default open ports
reachable (CIS Benchmarks,
Oct 2024).
PermitRootLogin no eliminates the attack vector
responsible for 427 million annual brute-force attempts, making
password-based credential stuffing impossible.