Is Your Server Secure? A Quick Security Checklist
Server security checklist: SSH hardening, firewall, exposed ports, automatic updates, fail2ban — with commands for each step.
TL;DR
Server security is not a one-time task — it is an ongoing process. This checklist covers the most critical areas: locking down SSH, configuring a firewall, auditing open ports and running services, enabling automatic updates, setting up intrusion detection with fail2ban, and hunting for leaked secrets. Work through each section from top to bottom. Most steps take less than five minutes and dramatically reduce your attack surface.
Prerequisites
- A Linux server (Debian/Ubuntu-based — commands are adapted accordingly; CentOS/RHEL equivalents noted where relevant)
- Root or sudo access
- A working SSH connection (do not lock yourself out while hardening SSH — always keep a second session open)
- Basic familiarity with the terminal
1. SSH Security Hardening
SSH is the front door to your server. A misconfigured SSH daemon is the number one cause of unauthorized access.
1.1 Disable Root Login
Edit the SSH daemon configuration:
sudo nano /etc/ssh/sshd_config
Find and set the following directive:
PermitRootLogin no
1.2 Enforce Key-Only Authentication
Password-based logins are vulnerable to brute-force attacks. Switch to key-only authentication:
# On your LOCAL machine, generate a key pair if you don't have one
ssh-keygen -t ed25519 -C "your_email@example.com"
# Copy the public key to your server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip
Then disable password authentication on the server:
# In /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
1.3 Change the Default SSH Port
Changing the port does not provide true security, but it eliminates the vast majority of automated bot scans:
# In /etc/ssh/sshd_config
Port 2222
Apply all changes:
sudo sshd -t # Test configuration for syntax errors
sudo systemctl restart sshd
Warning: Before restarting, ensure your firewall allows the new port and that you have an active session as a fallback.
2. Firewall Configuration
2.1 UFW (Uncomplicated Firewall)
UFW is the recommended firewall frontend on Ubuntu/Debian:
# Allow SSH (use your custom port if changed)
sudo ufw allow 2222/tcp comment 'SSH'
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Deny everything else inbound by default
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
2.2 Basic iptables Examples
If you prefer working with iptables directly:
# Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH on custom port
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Drop everything else
sudo iptables -A INPUT -j DROP
# Persist rules across reboots
sudo apt install iptables-persistent
sudo netfilter-persistent save
3. Checking Exposed Ports
You should know exactly which ports are open on your server and which processes are listening.
# Show all listening TCP ports with process names
sudo ss -tlnp
# Same for UDP
sudo ss -ulnp
# Scan your own server from localhost
sudo nmap -sT -O localhost
# Scan from an external machine to see what the internet sees
nmap -sV your-server-ip
Review the output carefully. Every open port that is not explicitly required should be closed via the firewall or by stopping the service entirely.
4. Automatic Security Updates
Unpatched software is the easiest entry point for attackers. Automatic security updates are essential.
# Install unattended-upgrades
sudo apt update
sudo apt install unattended-upgrades apt-listchanges
# Enable it
sudo dpkg-reconfigure -plow unattended-upgrades
Verify the configuration:
# Check the config file
cat /etc/apt/apt.conf.d/50unattended-upgrades
Ensure the following lines are uncommented:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Test a dry run:
sudo unattended-upgrades --dry-run --debug
5. Checking for Leaked Secrets
Misconfigured deployments frequently leak database credentials, API keys, and environment files.
# Find all .env files on the system
sudo find / -name ".env" -type f 2>/dev/null
# Check if .env files are accessible from the web root
sudo find /var/www -name ".env" -type f
sudo find /var/www -name "*.bak" -o -name "*.sql" -o -name "*.log" 2>/dev/null
# Check for config files in publicly served directories
sudo find /var/www -name "wp-config.php" -exec ls -la {} \;
sudo find /var/www -name "config.php" -exec ls -la {} \;
# Search for hardcoded passwords in common config files
sudo grep -r "password" /var/www --include="*.php" --include="*.env" -l
sudo grep -r "DB_PASSWORD\|API_KEY\|SECRET" /var/www -l
Ensure your web server blocks access to sensitive files. For Nginx, add:
location ~ /\.env {
deny all;
return 404;
}
6. fail2ban Setup
fail2ban monitors log files and bans IPs that show malicious behaviour such as repeated failed login attempts.
# Install
sudo apt update
sudo apt install fail2ban
# Create a local config (never edit jail.conf directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
6.1 Configure SSH Jail
Edit /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
6.2 Configure Nginx Jail
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
Start and enable fail2ban:
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd
7. User Accounts & Sudo Audit
Regularly audit who has access to your server.
# Show currently logged-in users
who
# Show recent logins
last -20
# Show failed login attempts
sudo lastb -20
# List all users with a login shell
grep -v '/nologin\|/false' /etc/passwd
# Check who has sudo privileges
sudo grep -v '^#' /etc/sudoers | grep -v '^$'
ls -la /etc/sudoers.d/
# List members of the sudo group
getent group sudo
Remove any accounts that are no longer needed:
sudo userdel -r old_username
8. File Permissions
World-writable files are a common vector for privilege escalation.
# Find world-writable files (excluding /proc and /sys)
sudo find / -path /proc -prune -o -path /sys -prune -o -perm -002 -type f -print 2>/dev/null
# Find world-writable directories without the sticky bit
sudo find / -path /proc -prune -o -path /sys -prune -o -perm -002 -type d ! -perm -1000 -print 2>/dev/null
# Find files with SUID/SGID bits set
sudo find / -path /proc -prune -o -path /sys -prune -o \( -perm -4000 -o -perm -2000 \) -type f -print 2>/dev/null
# Fix web directory permissions
sudo chown -R www-data:www-data /var/www
sudo find /var/www -type d -exec chmod 750 {} \;
sudo find /var/www -type f -exec chmod 640 {} \;
9. Checking Running Services
# List all active services
sudo systemctl list-units --type=service --state=running
# List enabled services (start at boot)
sudo systemctl list-unit-files --type=service --state=enabled
# Check for services listening on network interfaces
sudo ss -tlnp | awk 'NR>1 {print $4, $6}'
Ask yourself: does this service need to be running? Does it need to be accessible from the network, or only locally?
10. Quick Wins
10.1 Update All Packages
sudo apt update && sudo apt upgrade -y
10.2 Disable Unused Services
# Example: disable and stop a service you don't need
sudo systemctl disable --now cups
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now rpcbind
10.3 Secure Shared Memory
Add this line to /etc/fstab:
tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0
10.4 Set Login Banners
echo "Authorized access only. All activity is monitored and logged." | sudo tee /etc/issue.net
# In /etc/ssh/sshd_config:
# Banner /etc/issue.net
10.5 Enable Process Accounting
sudo apt install acct
sudo systemctl enable --now acct
Troubleshooting
Locked out of SSH after changing the port?
Use your hosting provider's console/VNC access. Fix the port in /etc/ssh/sshd_config and ensure the firewall allows it:
sudo ufw allow 2222/tcp
sudo systemctl restart sshd
fail2ban banned your own IP?
# Unban an IP address
sudo fail2ban-client set sshd unbanip YOUR_IP_ADDRESS
# Whitelist your IP permanently in jail.local
# ignoreip = 127.0.0.1/8 YOUR_IP_ADDRESS
UFW blocking legitimate traffic?
# Check which rule is blocking
sudo ufw status numbered
# Delete a specific rule by number
sudo ufw delete RULE_NUMBER
# Or temporarily disable to diagnose
sudo ufw disable
Unattended-upgrades not running?
# Check the log
sudo cat /var/log/unattended-upgrades/unattended-upgrades.log
# Verify the service timer
sudo systemctl status apt-daily-upgrade.timer
Prevention & Ongoing Maintenance
- Schedule monthly audits: Review open ports, active users, installed packages, and fail2ban logs on a regular basis.
- Subscribe to security advisories: Follow the mailing lists for your OS distribution and all major software you run.
- Backup regularly: Security hardening is meaningless without reliable backups. Test your restore process.
- Use a centralized log manager: Ship logs to an external system so that if a server is compromised, the logs survive.
- Implement the principle of least privilege: Every user, service, and process should have only the minimum permissions it needs.
- Consider a rootkit scanner: Tools like
rkhunterorchkrootkitcan detect known rootkits. - Enable two-factor authentication: For SSH access, consider adding TOTP via
libpam-google-authenticator. - Document everything: Keep a record of every change you make. Future-you will be grateful.
Need Expert Help?
Want a professional security check with a clear report? €39.
Book Now — €39100% money-back guarantee