Security is not a feature; it is a continuous process. In the modern threat landscape, leaving a default Linux configuration exposed is an invitation for compromise. As developers and system administrators, we must adopt a "default deny" mindset. This guide covers the critical layers of defense you must implement to secure your production servers against common and advanced attacks. We will move beyond basic tutorials into practical, production-ready hardening techniques that balance security with usability.
Locking Down SSH Access
The Secure Shell (SSH) is the primary entry point for most attacks. Misconfigured SSH is the leading cause of server compromises. Your first step is to eliminate weak authentication methods.
1. Disable Password Authentication: Force the use of public key cryptography.
2. Change Default Port: While not security through obscurity, moving off port 22 reduces the noise from automated scanning bots.
3. Restrict Users: Only allow specific administrative accounts to log in.
# /etc/ssh/sshd_config
# Disable password and root login
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
# Restrict access to specific users
AllowUsers deploy admin
# Change default port (optional)
Port 2222
# Enforce strong key exchange algorithms
KexAlgorithms curve25519-sha256, diffie-hellman-group-exchange-sha256
After modifying these settings, test in a separate session before closing your current one to ensure you haven’t locked yourself out.
Implementing Mandatory Access Control (MAC)
Traditional Unix permissions (read/write/execute) are insufficient for complex environments. MAC systems like SELinux (Standard for RHEL/Fedora) and AppArmor (Standard for Ubuntu/Debian) enforce policies on processes, regardless of the user executing them. If a web server is compromised, MAC prevents it from accessing sensitive files like /etc/shadow or escalating privileges.
For SELinux, use the semanage and audit2allow tools to refine policies. For AppArmor, define profiles using profiles located in /etc/apparmor.d/.
# Check SELinux status
getenforce
# View recent AVC denials to understand why processes are blocked
ausearch -m avc -ts recent
Network Layer Defense: Firewalls and Fail2ban
A firewall acts as your gatekeeper. Use firewalld for RHEL-based systems or ufw for Debian-based systems. Ensure you only open the ports strictly necessary for your services (e.g., 443 for HTTPS, your custom SSH port).
Fail2ban complements your firewall by monitoring log files for failed access attempts and dynamically blocking offending IP addresses. It is crucial for mitigating brute-force attacks.
# Install and enable fail2ban
sudo apt-get install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Create /etc/fail2ban/jail.local to customize
[sshd]
enabled = true
port = 2222
filter = sshd
maxretry = 3
bantime = 1h
System Hardening and Auditing
Hardening involves minimizing the attack surface. This includes disabling unused services (like FTP or Telnet), applying kernel parameter tuning, and enabling audit logging.
Use auditd to track changes to critical system files. You can set up watch rules to alert administrators if /etc/passwd or /etc/sudoers is modified.
# Watch for changes to critical files
sudo auditctl -w /etc/passwd -p wa -k identity
sudo auditctl -w /etc/sudoers -p wa -k sudoers
# Check audit logs
sudo ausearch -k identity
Additionally, ensure your kernel parameters are hardened. Add the following to /etc/sysctl.conf to prevent ICMP redirect attacks and enable IP spoofing protection:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
Conclusion
Server security is not a one-time task but a ongoing responsibility. By combining strong SSH configurations, Mandatory Access Control, dynamic IP banning via Fail2ban, and rigorous auditing, you build a multi-layered defense. Regularly review your logs, keep your software updated, and test your hardening measures. Remember, the best security strategy is one that your team can actually maintain. Start with the basics, layer on complexity as needed, and always prioritize the principle of least privilege in your system design.