A new server starts getting probed within minutes of getting a public IP address. This linux server need to be hardening through these 12 steps. You can finish them in an hour. These commands assume Ubuntu or Debian, so adjust the package manager if you run something else.
Before you start
Keep your provider’s web console open, keep one SSH session open the whole time. If you break SSH or the firewall, the console is your only way back in. Treat that as step zero.
Step 1: Update everything
Because the image you booted may be weeks or months old, you need to run the update/upgrade command. Run this and reboot if the kernel was updated.
sudo apt update && sudo apt full-upgrade -y
sudo reboot
Step 2: Create a non-root user with sudo
Never use root as user. A normal user with sudo gives you a log of who ran what, and it limits the damage from a typo.
sudo adduser deploy
sudo usermod -aG sudo deploy
Pick any username you like. Just avoid obvious ones such as admin or ubuntu if you can, since bots try those first.
Step 3: Set up SSH key login
Use SSH key over passwords. Save the private file in a secure place (not in cloud too), it must be added in your laptop, and the public half goes on the server.
On your own computer, create a key and copy it over:
ssh-keygen -t ed25519 -C "you@laptop"
ssh-copy-id deploy@your.server.ip
Now open a new terminal and confirm you can log in as deploy without typing a password. Don’t continue until that works.
Step 4: Harden the SSH daemon
Once keys work, turn off root login and password login. This one change shuts down most all dangerous attacks.
Create an override file instead of editing the main config. On Ubuntu, cloud images often ship a file that re-enables passwords, so give your file a name first.
sudo tee /etc/ssh/sshd_config.d/00-hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
EOF
sudo sshd -t && sudo systemctl reload ssh
The sshd -t checks the syntax first, so you don’t take down SSH service. Test a new login in a second terminal before you close the old one.
Should you change the SSH port?
Switch SSH default port from 22, this will reduce the log noise. You can’t avoid port scan but still better if the noise bothers you. But don’t count it as protection.
Step 5: Turn on a firewall
A firewall blocks every incoming connection except the ones you allow. On Ubuntu, UFW (Uncomplicated Firewall) is the easiest option. Allow SSH before you enable it, or you’ll cut off your own session.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose
Only open 80 and 443 if you’re running a web server. The rule is simple: if you can’t name the service that needs a port, keep it closed.
One catch for Docker users: Docker edits firewall rules itself and can publish ports that UFW doesn’t show. Check what’s really exposed using step 9.
Step 6: Enable automatic security updates
Unattended upgrades install security patches on their own, so you don’t fall behind when life gets busy. This is the cheapest protection on the list.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades
The default setup only applies security updates, which is what you want on a server. Decide whether you want automatic reboots for kernel patches. If the box runs anything important, schedule reboots yourself instead.
Step 7: Install fail2ban
Fail2ban watches your logs and bans IP addresses that fail to log in too many times. With key-only SSH it’s a second layer, but it also cuts log spam and protects other services later.
sudo apt install fail2ban -y
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[sshd]
enabled = true
maxretry = 5
findtime = 10m
bantime = 1h
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Add your own home or office IP (or a VPN IP) to an ignoreip line if you tend to mistype things. Getting banned from your own server is annoying.
Step 8: Remove services you don’t need
Every exposed service can be attacked. List what’s listening on the network and question each entry.
sudo ss -tulpn
You’ll usually see SSH, a web server, and local-only services on 127.0.0.1. Local-only is fine. Anything listening on 0.0.0.0 that you don’t recognize should go.
sudo systemctl disable --now servicename
If you don’t need a package at all, remove it with sudo apt purge.
Step 9: Scan your server from the outside
Your firewall config tells you what you meant to do. A port scan tells you what’s actually open. Run Nmap from another machine, not from the server itself.
nmap -sV your.server.ip
You should see only the ports you chose. If something unexpected shows up, trace it with ss -tulpn on the server. Only scan machines you own or have permission to test.
Step 10: Add basic auditing with auditd
The Linux audit system records security-relevant events, such as someone changing a critical file. It’s much easier to set up before something goes wrong than after.
sudo apt install auditd -y
sudo auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
sudo auditctl -w /etc/sudoers -p wa -k sudoers
sudo ausearch -k sudoers
Rules added with auditctl disappear on reboot. To keep them, put the same watch lines in a file under /etc/audit/rules.d/ and load them with sudo augenrules --load.
Step 11: Run Lynis for a second opinion
Lynis is an open-source tool that scans your system and lists weak spots with suggestions. It’s a good way to catch things this checklist missed.
sudo apt install lynis -y
sudo lynis audit system
Read the warnings and suggestions at the end. Don’t chase a perfect score, since some suggestions don’t fit every server. Fix the ones you understand and look up the rest.
Step 12: Put SSH behind a VPN when you can
The safest SSH port is one the internet can’t reach. If you only manage the server from a few devices, run a VPN and allow SSH only from the VPN network. WireGuard is a good fit because it’s small and fast. Kaliex has a guide on setting up WireGuard as a simple, secure VPN.
Once the tunnel works, restrict SSH in UFW to your VPN subnet and delete the public SSH rule. Check the tunnel from a second device first, or you’ll need that provider console from step zero.
How often should you repeat this?
Hardening is not a one-time job. Re-run the Nmap scan and Lynis every few months, and after any big change such as installing new software. Also glance at sudo fail2ban-client status sshd and your update logs now and then.
If you manage more than one server, stop typing these commands by hand. Turn the checklist into a playbook so every machine gets the same setup. The Kaliex post on deploying Nginx on Ubuntu with Ansible is a good place to see that approach in action.
Start with steps 3 to 6 today if you’re short on time. Those four cover the attacks that hit new servers most often.
See ya!
Leave a Reply