This is the order we work through on projects. Each step stands alone, so you can start at the top and stop wherever you like.
1. Lock down SSH
Disable password login and accept keys only:
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowUsers deploy
MaxAuthTries 3After each change, test login from a separate terminal before closing your current session. That is the only way you don't lock yourself out.
2. Firewall with default-deny
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 443/tcp
ufw enableThe golden rule: any port whose purpose you can't explain in one sentence should be closed.
3. Automatic security updates
apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgradesAutomate security patches only, not every package. Fully automatic upgrades can break a service in the middle of the night.
4. Remove unnecessary services
systemctl list-units --type=service --state=runningInvestigate anything you don't recognise. A service that isn't running isn't vulnerable either.
5. A separate user per application
Nothing should run as root. Create a shell-less user for each service:
useradd --system --no-create-home --shell /usr/sbin/nologin appsvc6. Fail2ban for repeated attempts
[sshd]
enabled = true
maxretry = 4
bantime = 3600
findtime = 6007. File integrity
With AIDE, or the Arg Box agent, watch for changes in /etc, /bin and /usr/bin. A sudden change in those paths is almost always meaningful.
8. Synchronised time
timedatectl set-ntp trueWithout synchronised clocks, correlating logs across servers is effectively impossible. This simple step earns its keep during an investigation.
9. Ship logs off the host
# /etc/rsyslog.d/60-forward.conf
*.* @@log-collector.internal:6514If logs live only on that server, an attacker who owns the server can erase them.
10. Tested backups
A backup that has never been restored isn't a backup; it's an assumption. Rehearse a full restore at least quarterly, and record how long it took.
Final verification
Once every step is done, scan from outside:
nmap -sS -sV -p- your-server-ipAny port in that output you weren't expecting is exactly what you work on next.