Every minute, automated botnets, credential stuffers, and vulnerability scanners probe thousands of publicly accessible Linux servers across the internet. When an unhardened server is deployed with default credentials or unpatched packages, automated malware scripts can compromise it within hours.
Securing a production Linux server does not require purchasing expensive enterprise security suites. The vast majority of intrusions exploit basic administrative oversights: weak passwords, exposed management ports, unpatched software, and missing firewall rules.
By implementing a disciplined, layered defense strategy (defense-in-depth), you make unauthorized access exponentially more difficult. Here are 15 practical, production-tested steps to secure your Linux servers.
1. Implement SSH Key-Only Authentication
Passwords are vulnerable to brute-force attacks, credential stuffing, and phishing. SSH public key authentication uses cryptographic key pairs that are computationally infeasible to guess.
- Generate a modern Ed25519 key pair on your local client:
ssh-keygen -t ed25519 -C "admin@company.com" - Copy the public key to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip - Test your key login in a new terminal window before modifying the SSH configuration.
2. Disable Direct Root Login and Password Authentication
Once your standard user account can log in via SSH keys and escalate privileges with sudo, disable direct root logins and disable password prompts entirely.
Edit /etc/ssh/sshd_config.d/01-security.conf (or /etc/ssh/sshd_config):
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
Validate configuration syntax and reload the SSH service:
sshd -t && systemctl reload sshd
Warning: Never close your current active SSH session until you verify in a separate terminal window that you can successfully log in with your SSH key.
3. Enable Automated Security Updates
Zero-day vulnerabilities and common vulnerabilities and exposures (CVEs) are discovered weekly. Keeping base operating system packages patched is your primary defense against automated exploits.
On Ubuntu / Debian:
Install and configure unattended-upgrades:
apt-get update && apt-get install -y unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
Ensure /etc/apt/apt.conf.d/50unattended-upgrades enables security repository updates.
On AlmaLinux / Rocky Linux / RHEL:
Install and enable dnf-automatic:
dnf install -y dnf-automatic
systemctl enable --now dnf-automatic.timer
Configure apply_updates = yes in /etc/dnf/automatic.conf.
4. Configure a Strict Host-Level Firewall
A server should only listen to network traffic on ports strictly required for its business purpose (such as port 80/443 for web traffic and your SSH port).
Using UFW (Ubuntu/Debian):
# Set secure defaults (block all incoming, allow outgoing)
ufw default deny incoming
ufw default allow outgoing
# Allow SSH and Web traffic
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
# Enable firewall
ufw enable
Using firewalld (RHEL/AlmaLinux):
firewall-cmd --permanent --zone=public --add-service=ssh
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload
5. Deploy Brute-Force Intrusion Prevention (Fail2ban)
Even with password authentication disabled, botnets repeatedly hammering port 22 waste CPU cycles, pollute system authentication logs, and exhaust connection states.
Install and enable Fail2ban:
# On Ubuntu/Debian:
apt-get install -y fail2ban
# On RHEL/AlmaLinux:
dnf install -y epel-release && dnf install -y fail2ban
Create /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = ssh
Start and verify Fail2ban status:
systemctl enable --now fail2ban
fail2ban-client status sshd
6. Enforce Least Privilege and Review Sudo Access
Never run daily tasks or deploy application code as root. Create dedicated user accounts for engineers and restrict privileges using /etc/sudoers.d/.
- Audit who currently possesses root/sudo privileges:
grep -Po '^sudo:.*:\K.*' /etc/group grep -Po '^wheel:.*:\K.*' /etc/group - Remove unauthorized users from administrative groups:
deluser username sudo
7. Disable Unnecessary System Services
Every active daemon listening on your server expands your attack surface. If an unused legacy service is running, disable it.
List all enabled systemd services:
systemctl list-unit-files --type=service --state=enabled
If you see unused services (such as Bluetooth, RPC binders, CUPS printing daemons, or unneeded NFS servers):
systemctl stop rpcbind
systemctl disable rpcbind
8. Audit Listening Ports and Network Sockets
Regularly verify which programs are bound to public network interfaces.
Run ss to inspect all active TCP and UDP listening ports:
ss -tulpn
What to Look For
- If MySQL (
3306), Redis (6379), or PostgreSQL (5432) shows0.0.0.0:portor*:port, the database is exposed to the public internet. - Bind internal databases strictly to
127.0.0.1(localhost) or an internal private VPC subnet in their configuration files.
9. Harden Kernel Parameters via sysctl
The Linux kernel has built-in network security protections that can be enabled by configuring /etc/sysctl.d/99-security.conf:
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
# Disable ICMP redirect acceptance (prevents route hijacking)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Ignore ICMP echo broadcasts (Smurf attacks)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Enable reverse path filtering (spoofing protection)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable IP packet forwarding (unless this server is a VPN/router)
net.ipv4.ip_forward = 0
Apply the changes immediately:
sysctl --system
10. Enable Linux Audit Framework (auditd)
The Linux Audit Daemon (auditd) records kernel-level system calls, logging critical security events such as modifications to system files or unauthorized privilege escalation attempts.
Install and enable auditd:
apt-get install -y auditd audispd-plugins # Debian/Ubuntu
systemctl enable --now auditd
Add monitoring rules for /etc/passwd and /etc/shadow in /etc/audit/rules.d/audit.rules:
-w /etc/passwd -p wa -k identity_changes
-w /etc/shadow -p wa -k identity_changes
Review audit events using ausearch -k identity_changes.
11. Run Periodic Rootkit and Malware Scans
Deploy scanning utilities to catch file integrity modifications or hidden binaries running in /tmp.
Install rkhunter (Rootkit Hunter):
apt-get install -y rkhunter # Ubuntu/Debian
rkhunter --update
rkhunter --propupd
Run a system scan:
rkhunter --check --skip-keypress
Review the report in /var/log/rkhunter.log. Investigate any warnings related to hidden files or altered system binaries.
12. Run Web Applications Under Isolated Unprivileged Users
Never run PHP-FPM, Node.js, Python, or web servers as root. If an attacker executes a Remote Code Execution (RCE) vulnerability through your web application, running as root grants them total control of the server immediately.
- Run applications as dedicated unprivileged system users (e.g.,
www-data,nginx, or a customappuser). - Use systemd service isolation directives in your service unit files:
[Service] User=appuser Group=appuser ProtectSystem=strict ProtectHome=true NoNewPrivileges=true PrivateTmp=true
13. Implement Web Application Firewalls (WAF) and Strict TLS
For servers hosting public websites, network firewalls alone cannot inspect HTTP payloads. SQL injections, Cross-Site Scripting (XSS), and malicious file uploads pass through port 443 unhindered.
- Deploy an edge WAF (like Cloudflare or AWS WAF) or local web server WAF modules (ModSecurity / Coraza).
- Enforce modern TLS 1.3 encryption and strong cipher suites.
- Configure HTTP security headers in Nginx or Apache:
add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Content-Security-Policy "default-src 'self';" always;
14. Maintain Tested, Immutable Off-Site Backups
The ultimate safeguard against ransomware, accidental deletion, or catastrophic hardware failure is a proven backup strategy.
- 3-2-1 Rule: Maintain 3 copies of your data on 2 different storage media, with at least 1 copy off-site.
- Immutability: Configure backup storage (such as AWS S3 Object Lock or dedicated backup vaults) with Write-Once-Read-Many (WORM) policies so attackers cannot delete backups even if they compromise server credentials.
- Test Restores Monthly: A backup that has never been restored is an unverified assumption. Run regular restoration drills into isolated staging environments.
See our guide on server backup management for automated snapshot verification.
15. Centralize Logging and Establish Incident Response
Attackers frequently clear local log files (/var/log/auth.log, journalctl) after gaining access to cover their tracks.
- Ship logs in real time to a remote, centralized syslog server or SIEM (such as Wazuh, Elastic, or Grafana Loki).
- Document an incident response runbook before an emergency occurs:
- How to isolate an infected server from the private network.
- How to take a forensically sound memory snapshot.
- Escalation contacts and service recovery procedures.
Review our 24/7 server security services for managed SIEM monitoring.
Summary Checklist: 15 Linux Security Controls
| # | Security Control | Verification Command |
|---|---|---|
| 1 | SSH Key Authentication | grep PubkeyAuthentication /etc/ssh/sshd_config |
| 2 | Root & Password Login Disabled | grep PermitRootLogin /etc/ssh/sshd_config |
| 3 | Auto Security Updates | systemctl is-active unattended-upgrades dnf-automatic.timer |
| 4 | Host Firewall Enabled | ufw status or firewall-cmd --state |
| 5 | Fail2ban Active | fail2ban-client status sshd |
| 6 | Sudo Privileges Audited | grep -Po '^sudo:.*:\K.*' /etc/group |
| 7 | Unused Services Disabled | systemctl list-unit-files --state=enabled |
| 8 | Public Ports Audited | ss -tulpn |
| 9 | Sysctl Kernel Hardening | sysctl net.ipv4.tcp_syncookies |
| 10 | Auditd Framework Enabled | systemctl is-active auditd |
| 11 | Rootkit Scanners Configured | rkhunter --check --versioncheck |
| 12 | Non-Root Application Users | ps aux | grep -E 'node|php|gunicorn' |
| 13 | Web Application Firewall / TLS | curl -I https://localhost |
| 14 | Off-Site Tested Backups | Verify latest backup snapshot timestamp |
| 15 | Remote Log Centralization | systemctl is-active rsyslog |
Frequently Asked Questions
Does changing the default SSH port (22) improve security?
Changing your SSH port to a custom port (such as 2222) stops basic automated internet noise and keeps authentication logs cleaner. However, it is security through obscurity—port scanners like nmap will find the open port in seconds. SSH key-only authentication and firewall restrictions are vastly more important than changing the port.
How often should I reboot a production Linux server for kernel updates?
When critical kernel security patches are released (e.g., severe local privilege escalation vulnerabilities), servers should be rebooted during an approved maintenance window. Alternatively, deploy live-patching technologies (such as KernelCare or Canonical Livepatch) to apply kernel security updates without rebooting.
Is Fail2ban enough to stop brute-force attacks?
Fail2ban is effective against low-intensity automated scans. However, distributed botnets that rotate through thousands of different IP addresses with only 1 attempt per IP can bypass simple fail limits. For high-threat environments, combine Fail2ban with behavioral network firewalls (like CrowdSec or Cloudflare edge challenges).
How does ServerCare360 help companies secure their server infrastructure?
Our certified server security and Linux server support specialists provide end-to-end security audits, deploy Wazuh SIEM threat monitoring, configure custom firewall policies, perform kernel hardening, and provide 24/7 incident containment.