Freorit

Open Source & Security Practitioner
Linux Security Server Administration LPIC-2 Production ca. 20 min read Advanced Updated: 2025

Linux Server Hardening

Production-grade security configuration for Linux servers: SSH key management, firewall rules, intrusion prevention with Fail2ban, centralized logging, and backup strategies based on LPIC-2 practices.

Why Server Hardening Matters

A freshly installed Linux server is functional but not secure. It listens on many ports, accepts password authentication, logs everything to disk, and has minimal protection against brute-force attacks.

Server hardening is the process of systematically reducing these attack surfaces. It's not a single action — it's a layered approach.

The Problem

Default Linux installations make sense for development, not production. Common security oversights:

The Solution

This guide walks through six layers of hardening, each building on the previous:

  1. Foundation: Users, permissions, updates
  2. SSH: Key-based authentication, protocol restrictions
  3. Firewall: Stateful rules, egress filtering
  4. Intrusion Prevention: Fail2ban rate limiting
  5. Monitoring: Logs, metrics, alerting
  6. Backup: Automated recovery, tested restores
Scope

This guide focuses on single-server hardening. Multi-server architectures (load balancers, reverse proxies, segmented networks) require additional layers covered in LPIC-3.

Layer 1: System Foundation

Before SSH, firewall, or Fail2ban, the foundation must be solid: minimal user privileges, filesystem permissions, and current software.

Principle of Least Privilege

Create accounts only for what's necessary. Disable unused services.

Terminal
sudo useradd -m -s /bin/bash -G sudo deploy
sudo passwd deploy
# Grant sudo without password for specific commands (optional)
# echo "deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl" | sudo tee -a /etc/sudoers.d/deploy
    

Never use root for daily tasks. Always use a regular user with sudo for administrative actions.

Filesystem Permissions

Restrict sensitive files to root-only read:

Terminal
sudo chmod 600 /etc/ssh/sshd_config
sudo chmod 700 /root
sudo chmod 644 /etc/passwd
sudo chmod 640 /etc/shadow
    

Automatic Security Updates

Enable unattended upgrades to patch CVEs automatically:

Terminal
sudo apt-get install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
    

Verify it's running:

Terminal
sudo systemctl status unattended-upgrades
sudo grep -r "^Unattended-Upgrade::Automatic-Reboot:" /etc/apt/apt.conf.d/
    

Audit Kernel Parameters

Harden kernel behavior to mitigate common exploits:

/etc/sysctl.conf
# Disable IP forwarding (unless router)
net.ipv4.ip_forward = 0

# Enable SYN cookies (protect against SYN flood)
net.ipv4.tcp_syncookies = 1

# Ignore ICMP redirects (prevent MITM)
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.accept_redirects = 0

# Log suspicious packets
net.ipv4.conf.all.log_martians = 1

# Ignore ICMP ping requests (optional, breaks some monitoring)
# net.ipv4.icmp_echo_ignore_all = 1
    

Apply changes:

Terminal
sudo sysctl -p
    
Verification:

Check that updates are installed daily:

sudo journalctl -u unattended-upgrades | tail -10

Layer 2: SSH Hardening

SSH is the primary remote access mechanism. Misconfiguration here is the quickest path to compromise.

SSH Key-Based Authentication

Generate a strong ED25519 key (preferred over RSA for new keys):

Terminal (local machine)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_prod -C "deploy@server"
    

Copy the public key to the server:

Terminal
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub deploy@server.example.com
    

Disable Password Authentication

Once keys are in place, disable password login:

/etc/ssh/sshd_config
Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# Stronger algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com

# Limit concurrent sessions
MaxSessions 2
MaxStartups 10:30:100

# Client keepalive
ClientAliveInterval 300
ClientAliveCountMax 2

# Disable X11, tunneling, agent forwarding unless needed
X11Forwarding no
AllowAgentForwarding no
PermitTunnel no

# Restrict to specific users
AllowUsers deploy
    

Validate and reload:

Terminal
sudo sshd -t
sudo systemctl reload ssh
    
Critical

Test SSH login in a new terminal before closing your current session. If the configuration is broken, you could lock yourself out.

Port Changes (Optional Defense-in-Depth)

Moving SSH off port 22 reduces automated scanner noise (not a security measure, just operational hygiene):

/etc/ssh/sshd_config
Port 2222
    

Update your local SSH config:

~/.ssh/config
Host server.example.com
Port 2222
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
    

Layer 3: Firewall Configuration

A firewall blocks unsolicited inbound traffic and optionally restricts outbound.

UFW (Uncomplicated Firewall)

UFW is a frontend for iptables that's easier to manage:

Terminal
sudo apt-get install -y ufw

# Set default policies: deny inbound, allow outbound
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (essential to not lock yourself out)
sudo ufw allow 22/tcp
# Or if using a custom port:
# sudo ufw allow 2222/tcp

# Allow HTTP/HTTPS for web service (if running)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Verify
sudo ufw status
    

Rate Limiting

Limit repeated connections from the same IP to prevent brute force:

Terminal
sudo ufw limit 22/tcp
    

This allows max 6 connections per 30 seconds from a single IP.

Logging

Enable firewall logging to catch dropped packets:

Terminal
sudo ufw logging on
sudo ufw logging high
    

View logs:

Terminal
sudo tail -f /var/log/ufw.log
    
Verification:

Test port access from another machine:

nmap -p 22,80,443 server.example.com

Only allowed ports should show as open.

Layer 4: Intrusion Prevention with Fail2ban

Even with key-based auth, attackers will probe for weak passwords. Fail2ban automatically blocks IPs after repeated failed login attempts.

Installation & Configuration

Terminal
sudo apt-get install -y fail2ban

# Start and enable
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
    

Create a local config override (don't edit /etc/fail2ban/jail.conf):

/etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

# IP whitelist (trusted network, office, VPN)
ignoreip = 127.0.0.1/8 192.168.1.0/24 10.0.0.0/8

[sshd]
enabled = true
port = ssh,2222
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600
    

Reload Fail2ban:

Terminal
sudo systemctl reload fail2ban
    

Monitor Active Bans

Terminal
sudo fail2ban-client status
sudo fail2ban-client status sshd
    

Unban an IP if needed:

Terminal
sudo fail2ban-client set sshd unbanip 192.0.2.100
    

Layer 5: Monitoring & Logging

Logs are only useful if centralized, rotated, and monitored. A single server filling its disk with logs is a denial-of-service scenario.

Log Rotation

Configure logrotate to compress old logs:

/etc/logrotate.d/syslog
/var/log/syslog
/var/log/auth.log
/var/log/ufw.log
{
daily
rotate 14
compress
delaycompress
notifempty
create 0640 syslog adm
sharedscripts
postrotate
/lib/systemd/systemd-logind-control reload-config > /dev/null 2>&1 || true
endscript
}
    

Journal Limits

Systemd journal can grow large. Limit its size:

/etc/systemd/journald.conf
SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=30day
    

Reload:

Terminal
sudo systemctl restart systemd-journald
    

Key Logs to Monitor

Layer 6: Backup & Recovery

No security hardening helps if you can't recover from compromise or data loss. Backups are not optional.

Backup Strategy: 3-2-1 Rule

Local Automated Backup

Use rsync for efficient daily backups to a secondary disk:

/etc/cron.daily/backup.sh
#!/bin/bash
BACKUP_DIR="/backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Backup critical directories
rsync -av --delete \
--exclude='/proc' \
--exclude='/sys' \
--exclude='/dev' \
--exclude='/tmp' \
--exclude='/var/tmp' \
--exclude='/var/log' \
/ "$BACKUP_DIR/backup_$TIMESTAMP/"

# Keep only last 7 daily backups
find $BACKUP_DIR -maxdepth 1 -type d -name "backup_*" -mtime +7 -exec rm -rf {} \;
    

Offsite Backup (Cloud)

For smaller data, use S3 or equivalent:

Terminal
sudo apt-get install -y awscli

# Configure AWS credentials
aws configure

# Backup to S3 (example)
aws s3 sync /home/deploy/app s3://my-backups/app/ --delete
    

Test Your Restore

A backup you can't restore from is useless. Test regularly:

Terminal
# Restore a single file
rsync -av /backup/backup_20250610_120000/etc/passwd /tmp/restored_passwd

# Verify
cat /tmp/restored_passwd
    
Best Practice

Store backup encryption keys separately from the server. If compromised, backups are also compromised if the key is on-server.

Best Practices

1. Defense in Depth

No single layer is sufficient. SSH keys alone don't stop a compromised user account. Firewalls alone don't stop application exploits. Layer protections.

2. Minimal Exposure

Only expose what's necessary. If a service isn't needed, uninstall it.

Terminal
sudo systemctl disable apache2
sudo apt-get remove apache2
    

3. Change Detection

Detect unauthorized modifications with AIDE (Advanced Intrusion Detection Environment):

Terminal
sudo apt-get install -y aide aide-common
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# Check for changes
sudo aide --check
    

4. User Accountability

Audit sudo usage and SSH logins. Know who logged in and when:

Terminal
sudo grep "sudo:" /var/log/auth.log | tail -5
sudo lastlog
    

5. Keep Secrets Separate

Never commit passwords, API keys, or certificates to version control. Use environment variables or secret management tools.

.bashrc / .bash_profile
export DB_PASSWORD="$(cat /root/.db_password)"
# Or use a secret manager
export DB_PASSWORD="$(vault read -field=password secret/db)"
    

Lessons Learned

Technical Insights

Operational Insights

Open-Source Projects

This guide relies on industry-standard open-source tools: