Server breaches happen when administrators skip the fundamentals. A compromised server means stolen data, ransomware, resource hijacking for crypto mining, or your infrastructure used to attack others. This checklist walks through the security controls every Linux administrator should lock down before a server goes into production or after inheriting an existing system.
SSH Hardening
SSH is the primary entry point for remote administration and the most frequently attacked service on internet-facing servers.
Disable Root Login
Never allow direct root SSH access. Attackers target the root account first because it exists on every Linux system.
# Edit SSH daemon config
sudo nano /etc/ssh/sshd_config
# Set this directive
PermitRootLogin no
# Restart SSH
sudo systemctl restart sshd
Create a regular user account with sudo privileges for administrative tasks. This creates an audit trail showing who performed elevated actions.
Use Key-Based Authentication Only
Password authentication is vulnerable to brute force attacks. SSH keys are mathematically infeasible to crack.
# Generate a strong key pair on your local machine
ssh-keygen -t ed25519 -a 100
# Copy public key to server
ssh-copy-id [email protected]
# On server, disable password auth
sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
sudo systemctl restart sshd
Store private keys securely. Use a passphrase on the key itself as a second factor.
Change Default SSH Port (Optional)
Changing SSH from port 22 reduces automated scan noise but provides minimal real security. It's security through obscurity, not a substitute for strong authentication.
# /etc/ssh/sshd_config
Port 2222
# Update firewall rules before restarting
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo systemctl restart sshd
Restrict SSH Access by IP
If your administrative access comes from known IP ranges, whitelist them.
# /etc/ssh/sshd_config
AllowUsers [email protected]/24
# or
Match Address 203.0.113.0/24
PasswordAuthentication no
PermitRootLogin no
For dynamic IPs, consider VPN-only SSH access or port knocking.
Firewall Configuration
A firewall denies all traffic except explicitly permitted services. Default-deny is the foundation of perimeter security.
Enable and Configure Firewall
On RHEL/CentOS/Rocky/AlmaLinux:
sudo systemctl enable firewalld
sudo systemctl start firewalld
# Allow only necessary services
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
On Ubuntu/Debian (UFW):
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
sudo ufw status verbose
Close Unused Ports
Scan your own server to see what's exposed:
sudo ss -tulpn | grep LISTEN
If you see services you didn't expect, investigate and disable them. Common unnecessary services include cups (printing), rpcbind, or development servers left running.
Rate Limit SSH Connections
Prevent brute force attempts by limiting connection attempts:
# firewalld
sudo firewall-cmd --permanent --add-rich-rule='rule service name=ssh limit value=3/m accept'
sudo firewall-cmd --reload
# UFW
sudo ufw limit ssh
User Access and Privilege Management
Principle of Least Privilege
Grant users and services only the permissions they need. Avoid giving full sudo access when specific commands suffice.
# Create service-specific users with no shell
sudo useradd -r -s /usr/sbin/nologin appuser
# Grant specific sudo commands
# /etc/sudoers.d/developer
developer ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
Use visudo to edit sudoers files. It validates syntax before saving.
Audit User Accounts
Remove or lock accounts that are no longer needed:
# List all users
cat /etc/passwd
# Lock an account
sudo usermod -L olduser
# Remove an account and home directory
sudo userdel -r olduser
Enforce Password Policies
Even with key-based SSH, local accounts should have strong password requirements:
# Install password quality library
sudo yum install libpwquality # RHEL/CentOS
sudo apt install libpam-pwquality # Ubuntu/Debian
# /etc/security/pwquality.conf
minlen = 14
minclass = 3
maxrepeat = 2
# Set password expiration
sudo chage -M 90 username
File System Permissions and Integrity
Secure Critical Files
Restrict access to configuration files containing credentials:
# Database config files
sudo chmod 600 /etc/mysql/my.cnf
sudo chown mysql:mysql /etc/mysql/my.cnf
# Web application configs
sudo chmod 640 /var/www/html/wp-config.php
sudo chown www-data:www-data /var/www/html/wp-config.php
# Private keys
sudo chmod 600 /etc/ssl/private/*.key
Set Secure Umask
The umask controls default permissions for newly created files:
# /etc/profile and /etc/bashrc
umask 027
This creates files with 640 permissions and directories with 750, preventing world-readable files by default.
Enable File Integrity Monitoring
AIDE (Advanced Intrusion Detection Environment) detects unauthorized file changes:
sudo yum install aide # RHEL/CentOS
sudo apt install aide # Ubuntu/Debian
# Initialize database
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Check for changes
sudo aide --check
# Automate with cron
echo "0 5 * * * root /usr/sbin/aide --check | mail -s 'AIDE Report' [email protected]" | sudo tee -a /etc/crontab
Automated Updates and Patching
Unpatched vulnerabilities are the leading cause of server compromise. Automate security updates to close the window of exposure.
Enable Automatic Security Updates
RHEL/CentOS/Rocky/AlmaLinux:
sudo yum install yum-cron
sudo systemctl enable yum-cron
sudo systemctl start yum-cron
# /etc/yum/yum-cron.conf
apply_updates = yes
update_cmd = security
Ubuntu/Debian:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Mail "[email protected]";
Schedule a maintenance window for kernel updates and reboots. Use needs-restarting or checkrestart to identify services that need restarting after library updates.
SSL/TLS Configuration
Encrypt all traffic between clients and your server. Outdated TLS configurations expose data to interception.
Obtain and Install Certificates
Use Let's Encrypt for free, automated certificates:
sudo yum install certbot python3-certbot-nginx # RHEL/CentOS
sudo apt install certbot python3-certbot-nginx # Ubuntu/Debian
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renewal
sudo systemctl enable certbot-renew.timer
Harden TLS Settings
Disable weak protocols and ciphers in your web server or reverse proxy:
Nginx:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
Apache:
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
Test your configuration with SSL Labs or similar scanning tools.
Logging and Monitoring
You can't defend against what you can't see. Centralized logging and real-time alerts detect attacks in progress.
Configure Centralized Logging
Ship logs to a dedicated log server or SIEM. Attackers often delete local logs to cover their tracks.
# Basic rsyslog forwarding
echo "*.* @@logserver.example.com:514" | sudo tee -a /etc/rsyslog.conf
sudo systemctl restart rsyslog
Monitor Authentication Logs
Watch for failed login attempts and unusual access patterns:
# Recent failed SSH attempts
sudo grep "Failed password" /var/log/auth.log | tail -20
# Successful logins
sudo last -a
Set up alerts for repeated failures from the same IP or successful logins from unexpected locations.
Install Intrusion Detection
Fail2ban monitors logs and automatically bans IPs showing malicious behavior:
sudo yum install fail2ban # RHEL/CentOS
sudo apt install fail2ban # Ubuntu/Debian
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
Monitor System Resources
Crypto miners and DDoS bots consume CPU, memory, and bandwidth. Set up monitoring to catch anomalies:
# Install monitoring tools
sudo yum install glances htop iotop # RHEL/CentOS
sudo apt install glances htop iotop # Ubuntu/Debian
# Check current resource usage
glances
For production environments, deploy proper monitoring stacks like Prometheus, Grafana, or Netdata.
Application-Specific Hardening
Secure Database Access
Never expose database ports to the public internet:
# MySQL/MariaDB - bind to localhost only
# /etc/my.cnf or /etc/mysql/my.cnf
[mysqld]
bind-address = 127.0.0.1
sudo systemctl restart mariadb
Use strong passwords, remove test databases, and grant privileges narrowly:
DROP DATABASE test;
DELETE FROM mysql.user WHERE User='';
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
Harden Web Applications
Disable directory listing, server signatures, and unnecessary modules:
Nginx:
autoindex off;
server_tokens off;
Apache:
Options -Indexes
ServerTokens Prod
ServerSignature Off
Run web applications as dedicated users, not root or your personal account.
Keep Application Dependencies Updated
Outdated CMS platforms, plugins, and libraries are common attack vectors. Enable automatic updates where safe or establish a regular update schedule.
Conclusion
Server security is not a one-time checklist but an ongoing discipline. Implement these controls in order of impact: secure SSH first, enable the firewall, automate updates, then layer on monitoring and intrusion detection. Every control you skip is a door left unlocked. Review your server configurations against this checklist quarterly, audit logs weekly, and apply security patches promptly. The effort invested in hardening today prevents the far greater cost of incident response, data breach notification, and reputation damage tomorrow. Lock it down before someone else does it for you.
