A server breach rarely looks like a Hollywood hacker typing furiously. It usually looks like a slow disk fill, a strange cron job, or an unexpected outbound connection. In support tickets I handle, the most common cause isn't a zero-day exploit. It's a misconfigured service left exposed to the public internet.
You don't need a security team to keep a VPS or dedicated server safe. You need a routine. This guide breaks down five specific areas where servers fail most often. We will look at the risks, the impact, and the exact commands to fix them.
1. The SSH Gateway: Your First Line of Defense
SSH is the remote control for your server. If you get in here, the attacker gets in. By default, many OS images allow password authentication and root login. This is a massive risk. Passwords get brute-forced. Root login means one guess and total control.
Disable Password Authentication
The first step is to switch to key-based authentication only. If you haven't set up SSH keys yet, do that first. Never skip this step. Once your key works, edit the SSH daemon config.
sudo nano /etc/ssh/sshd_config
Find these lines and change them:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Save the file. Before you restart the service, test your connection in a new terminal window. If you lock yourself out, you have a problem. You can always regain access via the hosting provider's console, but it's annoying. After confirming access, restart SSH:
sudo systemctl restart sshd
Limit User Access
Why allow any user to log in? Restrict SSH to specific users. Add this line to the same config file:
AllowUser yourusername
This ensures that even if a weak password is guessed for another user, they can't get a shell.
2. The Firewall: Less is More
A firewall isn't just a toggle. It's a policy. By default, many servers have no firewall rules. This means port 22, 80, 443, and potentially 8080 are wide open. Attackers scan for open ports constantly. If you run a web server, you need 80 and 443. If you use SSH, you need 22. Everything else should be closed.
Using UFW on Ubuntu/Debian
UFW (Uncomplicated Firewall) is easier to manage than iptables for most users. Install it if it's not there:
sudo apt install ufw
Set default policies. Deny all incoming traffic. Allow all outgoing traffic (you need this for updates and API calls).
sudo ufw default deny incoming
sudo ufw default allow outgoing
Now, open only what you need:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw allow 443/tcp
Enable the firewall:
sudo ufw enable
Check the status to verify:
sudo ufw status verbose
If you use cPanel, the firewall is managed by CSF (ConfigServer Firewall). Check /etc/csf/csf.conf to ensure SSH_PORT is correct and SSH_BLOCK is enabled. Don't mix manual iptables rules with CSF. It causes conflicts.
So what if the port is open but SSH still refuses?
Check the sshd logs. If you see "Connection closed by 1.2.3.4 port 22," it's likely a firewall block or an SSH config issue. If you see "Invalid user," it's a brute-force attempt. The firewall stops the noise. SSH config stops the success.
3. Patching: The Boring Part That Saves You
Unpatched servers are targets. Distro maintainers release security updates for a reason. If you run Ubuntu, use unattended-upgrades. It installs security patches automatically. This is critical. Relying on manual updates means you forget. Forgetting means vulnerabilities linger for months.
Enable Automatic Updates
On Ubuntu/Debian:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Select "Yes" to enable automatic updates. This will install security updates during off-peak hours. It won't reboot the server, but it will restart services if needed. Most of the time, this is transparent.
On CentOS/RHEL, use yum-cron.
sudo yum install yum-cron
sudo systemctl enable yum-cron
sudo systemctl start yum-cron
Check the logs to see what got updated:
sudo tail -f /var/log/yum.log
The Risk of Ignoring Patches
I once had a client whose server was compromised because they hadn't updated openssh for six months. A known CVE allowed remote code execution. The attacker didn't break a new hole. They walked through an old one. Patching is not optional. It's hygiene.
4. Web Server Hardening: Hide the Banner
Your web server tells attackers exactly what you're running. If you see Server: Apache/2.4.41 (Ubuntu) in the response headers, you've given them a map. They know the version. They can look up known exploits for that specific version.
Hide Server Tokens
For Nginx, edit /etc/nginx/nginx.conf:
http {
server_tokens off;
}
For Apache, edit /etc/apache2/apache2.conf or your virtual host file:
ServerTokens Prod
ServerSignature Off
Restart the service:
sudo systemctl restart nginx
# or
sudo systemctl restart apache2
Now, when you curl your site, the header will just say Server: nginx or nothing at all. This doesn't stop a determined attacker, but it stops the low-hanging fruit. It forces them to do more work.
Disable Unused Modules
If you're running Apache, you might have modules enabled that you don't use. mod_status, mod_info, and mod_autoindex are dangerous if exposed. Disable them:
sudo a2dismod status info autoindex
sudo systemctl restart apache2
Check your active modules with:
sudo apache2ctl -M
Remove anything you don't recognize. Less code running means less code to exploit.
5. Monitoring: See What's Happening
If you don't monitor your server, you don't know when it's compromised. You might think it's fine because the site is up. But the attacker might be using it to send spam, mine crypto, or attack other servers. You need logs.
Check for Suspicious Activity
Look for unusual processes. If you see a process using high CPU that you don't recognize, investigate. Check the parent process. Check the command line.
ps auxf
Look for processes running from /tmp or /var/tmp. Legitimate software rarely runs from there. If you see something like /tmp/.hidden/miner, you have a problem. Kill the process, but don't delete the file yet. You need evidence.
Monitor Failed Logins
Check your auth logs for brute-force attempts.
sudo grep "Failed password" /var/log/auth.log | tail -n 20
If you see hundreds of attempts from the same IP, block it. If you see attempts from many IPs, your SSH is exposed. Use fail2ban to automate this.
sudo apt install fail2ban
sudo systemctl enable fail2ban
Fail2ban will automatically ban IPs after a few failed attempts. This reduces noise and blocks attackers.
What to Check First
If you're starting from zero, do these five things in order:
- SSH: Disable password auth and root login. Use keys only.
- Firewall: Block all incoming traffic except SSH, HTTP, and HTTPS.
- Updates: Enable automatic security updates.
- Web Server: Hide version banners and disable unused modules.
- Monitoring: Check logs daily for failed logins and strange processes.
This routine takes an hour to set up. It saves you from 90% of common attacks. The rest require more advanced tooling, but this is your foundation.
FAQ
Q: Do I need a VPN for my server?
A: Not necessarily. If you restrict SSH to your IP or use a VPN, you reduce exposure. But a good firewall and key-based SSH are enough for most users. A VPN adds complexity. Only use it if you have a specific need for remote access from untrusted networks.
Q: What if I get locked out of SSH?
A: Use your hosting provider's console. Most VPS providers offer a VNC or IPMI console. Log in there, reset your password, or edit the SSH config. This is why you should never disable console access. It's your safety net.
Q: How often should I update my server?
A: Automatically. Set up unattended-upgrades or yum-cron and let it handle security patches. You don't need to manually check for updates every day. The system does it for you.
Q: Is it safe to open port 22 to the world?
A: Risky. If you must, use key-based auth only and enable fail2ban. Better yet, change the port to something non-standard (e.g., 2222) and block all other ports. This doesn't stop determined attackers, but it stops scanners.
