Security hardening is one of those topics where it's easy to either do nothing or spiral into increasingly elaborate measures. The goal isn't to make your server impenetrable — it's to make it significantly harder to compromise than the typical internet-connected server, so that automated attacks move on to easier targets.
I've reviewed the security configurations of a fair number of servers over the years. The most common finding isn't sophisticated vulnerabilities — it's the basics: password auth still enabled, no firewall, kernel not updated in months, unnecessary services running. This guide covers the baseline measures that address those common weaknesses in a systematic way.
Most of these take 5 minutes each. The whole checklist can be done in an afternoon.
This guide is a comprehensive checklist covering SSH, filesystem, network, services, and monitoring — with the reasoning behind each step.
I apply this checklist to every Tencent Cloud Lighthouse instance I set up. Lighthouse adds two specific features that complement OS-level hardening: the console-level firewall operates at the network infrastructure layer, completely independently of UFW — it blocks traffic before it reaches your server's OS. This means even if a kernel vulnerability bypassed the OS firewall, the infrastructure-level rules still hold. The OrcaTerm browser terminal also provides an independent management path that doesn't use SSH — useful as a recovery mechanism if SSH gets locked down or misconfigured.
- Key Takeaways
Most cloud server compromises fall into a few categories:
This guide addresses the server-level issues (1, 2, 3, 5). Application-level security (4) is covered in each application's deployment guide.
sudo nano /etc/ssh/sshd_config
# Disable root login — use sudo from a regular user account
PermitRootLogin no
# Disable password authentication — require SSH keys
PasswordAuthentication no
ChallengeResponseAuthentication no
# Explicitly enable public key authentication
PubkeyAuthentication yes
# Limit login attempts
MaxAuthTries 3
# Reduce connection timeout
LoginGraceTime 30
# Disconnect idle sessions after 15 minutes
ClientAliveInterval 300
ClientAliveCountMax 3
# Disable X11 forwarding (unless you need it)
X11Forwarding no
# Disable SFTP if you don't use it (enable if needed)
# Subsystem sftp /usr/lib/openssh/sftp-server
# Only allow specific users to SSH
# AllowUsers ubuntu youradmin
sudo systemctl restart sshd
# Create a non-root user with sudo access
sudo adduser admin
sudo usermod -aG sudo admin
# Test the new user before locking root
ssh admin@YOUR_SERVER_IP
sudo whoami # Should return: root
# On local machine: generate key if needed
ssh-keygen -t ed25519 -C "server-access"
# Copy to server
ssh-copy-id admin@YOUR_SERVER_IP
# Verify key login works before disabling password auth
sudo passwd -l root
# Prevents root login even if someone gains shell access
sudo apt install -y libpam-pwquality
sudo nano /etc/pam.d/common-password
# Find the pwquality line and add:
# minlen=12 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1
# Default deny incoming
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow only what you need
sudo ufw allow ssh # Port 22
sudo ufw allow http # Port 80 (if running web server)
sudo ufw allow https # Port 443
# Enable
sudo ufw enable
# Verify
sudo ufw status verbose
Also configure the Lighthouse console firewall — this operates at the network layer, independent of the OS. If UFW is misconfigured, Lighthouse's rules still protect you.
sudo apt install -y fail2ban
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IP
[sshd]
enabled = true
maxretry = 3
bantime = 86400
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Unpatched software is the #1 cause of server compromise. Apply security updates promptly:
# Manual update
sudo apt update && sudo apt upgrade -y
# Enable automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Verify it's configured:
cat /etc/apt/apt.conf.d/20auto-upgrades
# Should contain:
# APT::Periodic::Unattended-Upgrade "1";
Each running service is a potential attack surface. Disable anything you don't need:
# List all running services
systemctl list-units --type=service --state=running
# Check what's listening on network ports
sudo ss -tlnp
# Disable a service
sudo systemctl disable --now SERVICE_NAME
# Common services to evaluate:
# postfix (mail) — disable if not sending server email
# avahi-daemon (mDNS) — usually not needed on servers
# cups (printing) — never needed on servers
# In /etc/profile or /etc/bash.bashrc:
umask 027 # Files created as 640, directories as 750
sudo nano /etc/fstab
Add:
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0
sudo mount -a
noexec prevents executing binaries from tmp directories, which many exploits rely on.
sudo find / -xdev -type f -perm -0002 -ls 2>/dev/null
# Should return nothing for a well-configured system
sudo find / -xdev \( -perm -4000 -o -perm -2000 \) -type f -ls 2>/dev/null
# Review the list and remove unnecessary SUID bits
sudo nano /etc/sysctl.d/99-security.conf
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Disable IPv6 if not needed
# net.ipv6.conf.all.disable_ipv6 = 1
# Restrict ptrace (reduces debugger-based attacks)
kernel.yama.ptrace_scope = 1
# Prevent core dumps from setuid programs
fs.suid_dumpable = 0
# Randomize memory layout
kernel.randomize_va_space = 2
sudo sysctl -p /etc/sysctl.d/99-security.conf
# Watch auth log for suspicious activity
sudo tail -f /var/log/auth.log | grep -v "pam_unix"
# Failed SSH attempts
sudo grep "Failed password" /var/log/auth.log | tail -20
# Successful logins
sudo grep "Accepted" /var/log/auth.log | tail -20
sudo apt install -y aide
# Initialize the database
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Check for changes (run periodically)
sudo aide --check
(crontab -l; echo "0 6 * * * /usr/bin/aide --check >> /var/log/aide.log 2>&1") | crontab -
Many guides recommend changing SSH from port 22 to a random high port (e.g., 2222) to "improve security." This is a marginal improvement at best.
What it actually does: Reduces the volume of automated scanning in logs (bots scan port 22 heavily). This makes logs cleaner but doesn't meaningfully improve security.
What it doesn't do: Port scanners find SSH on any port within minutes. Any determined attacker will find it.
Real SSH security comes from:
If you change the port anyway (for log cleanliness), that's fine — just remember to update UFW rules and client ~/.ssh/config. The security benefit is minimal.
| Step | Command/Action | Done? |
|---|---|---|
| Non-root user with sudo | adduser admin && usermod -aG sudo admin |
☐ |
| SSH key authentication | ssh-copy-id admin@SERVER |
☐ |
| Disable password auth | PasswordAuthentication no in sshd_config |
☐ |
| Disable root login | PermitRootLogin no in sshd_config |
☐ |
| UFW firewall enabled | ufw enable with minimal allow rules |
☐ |
| Fail2ban installed | apt install fail2ban + jail.local config |
☐ |
| Auto security updates | unattended-upgrades configured |
☐ |
| All packages updated | apt update && apt upgrade |
☐ |
| Unnecessary services disabled | Review systemctl list-units |
☐ |
| sysctl hardening | /etc/sysctl.d/99-security.conf |
☐ |
| Whitelist your IP in Fail2ban | ignoreip = YOUR_IP in jail.local |
☐ |
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
Does server security conflict with Lighthouse's console-level firewall?
No — they operate at different layers. Lighthouse's console firewall blocks at the network infrastructure level. server security operates at the OS level. Both provide independent protection layers.
How do I check if server security is working correctly?
Check the service status with sudo systemctl status server. Review logs for recent activity. Most security tools have a test or dry-run mode to verify configuration.
What should I do if server security blocks a legitimate user or IP?
Most security tools have a whitelist mechanism. Add trusted IPs to the whitelist/ignore list. For Fail2ban, use fail2ban-client set JAIL unbanip IP_ADDRESS.
How often should I review security logs?
For personal servers, a weekly review of auth logs and firewall logs is reasonable. For production or sensitive servers, set up log monitoring with alerts for suspicious patterns (unusually high fail rates, unusual hours).
Build on a hardened foundation:
👉 Tencent Cloud Lighthouse — Cloud server with console-level firewall protection
👉 View current pricing and promotions
👉 Explore all active deals and offers