Skip to content

🚧 TRANSLATION PENDING - Last updated in Spanish: 2026-01-25

Linux Server Hardening

Introduction

This guide provides a complete checklist for securing production Linux servers, following security best practices and CIS (Center for Internet Security) standards.

Hardening Checklist

1. Updates and Patches

Debian/Ubuntu

# Update system
apt update && apt upgrade -y

# Configure automatic updates
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades

# Verify package integrity
debsums -c

RHEL/CentOS/Rocky

# Update system
yum update -y

# Configure automatic updates
yum install dnf-automatic -y
systemctl enable --now dnf-automatic.timer

# Verify integrity
rpm -Va

2. User and Password Management

Create administrative user

# Create user with sudo
useradd -m -s /bin/bash admin
passwd admin
usermod -aG sudo admin  # Debian/Ubuntu
usermod -aG wheel admin # RHEL/CentOS

# Remove unnecessary default users
userdel -r games
userdel -r irc

Configure strong password policies

# /etc/security/pwquality.conf
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
minclass = 4

# Password expiration
chage -M 90 -m 7 -W 14 admin

# Account lockout after failed attempts
# /etc/pam.d/common-auth (Debian) or /etc/pam.d/system-auth (RHEL)
auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900

3. Advanced SSH Hardening

Complete SSH configuration

# /etc/ssh/sshd_config
Port 2222                              # Change default port
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 60
AllowUsers admin                       # Specific users only

# Strong cryptography
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256

# Restart SSH
systemctl restart sshd

SSH key authentication

# On client, generate SSH key
ssh-keygen -t ed25519 -C "admin@server"

# Copy key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@server -p 2222

# On server, secure permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Implement 2FA with Google Authenticator

# Install
apt install libpam-google-authenticator -y

# Configure for user
google-authenticator

# /etc/pam.d/sshd (add)
auth required pam_google_authenticator.so

# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

4. Firewall (UFW and firewalld)

UFW (Debian/Ubuntu)

# Install and enable
apt install ufw -y
ufw default deny incoming
ufw default allow outgoing

# Allow specific services
ufw allow 2222/tcp comment 'SSH'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'

# Rate limit SSH
ufw limit 2222/tcp

# Enable
ufw enable
ufw status verbose

firewalld (RHEL/CentOS)

# Install and enable
yum install firewalld -y
systemctl enable --now firewalld

# Configure default zone
firewall-cmd --set-default-zone=public

# Allow services
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=2222/tcp

# SSH rate limiting
firewall-cmd --permanent --add-rich-rule='rule service name="ssh" limit value="3/m" accept'

# Apply changes
firewall-cmd --reload

5. Kernel and Sysctl - Complete Configuration

# /etc/sysctl.conf or /etc/sysctl.d/99-hardening.conf

# IP Forwarding (disable if not a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Don't send ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048

# Ignore pings (optional)
net.ipv4.icmp_echo_ignore_all = 1

# ASLR (Address Space Layout Randomization)
kernel.randomize_va_space = 2

# Core dumps (disable)
kernel.core_uses_pid = 1
fs.suid_dumpable = 0

# Apply changes
sysctl -p

6. Advanced Logging and Auditing

Configure auditd

# Install
apt install auditd audispd-plugins -y

# /etc/audit/rules.d/hardening.rules
# Monitor config file changes
-w /etc/passwd -p wa -k passwd_changes
-w /etc/group -p wa -k group_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes

# Monitor login attempts
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins

# Monitor privileged commands
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands

# Load rules
auditctl -R /etc/audit/rules.d/hardening.rules
systemctl restart auditd

Configure logrotate

# /etc/logrotate.d/syslog
/var/log/syslog
/var/log/auth.log
{
    rotate 90
    daily
    missingok
    notifempty
    compress
    delaycompress
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

Send logs to centralized server

# /etc/rsyslog.conf
*.* @@log-server.example.com:514  # TCP
*.* @log-server.example.com:514   # UDP

systemctl restart rsyslog

7. Service Management

List and disable unnecessary services

# List active services
systemctl list-units --type=service --state=running

# Disable unnecessary services
systemctl disable --now avahi-daemon
systemctl disable --now cups
systemctl disable --now bluetooth

# Check services listening on network
ss -tulpn
netstat -tulpn

Configure SELinux (RHEL/CentOS)

# Check status
sestatus

# Enable SELinux in enforcing mode
# /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted

# Apply contexts
restorecon -Rv /var/www/html

# Troubleshooting
audit2allow -a -M custom_policy
semodule -i custom_policy.pp

Configure AppArmor (Debian/Ubuntu)

# Check status
aa-status

# Create profile for application
aa-genprof /usr/bin/myapp

# Enable profile
aa-enforce /etc/apparmor.d/usr.bin.myapp

8. Malware Protection

ClamAV

# Install
apt install clamav clamav-daemon -y

# Update definitions
freshclam

# Scan system
clamscan -r --infected --remove /home

# Scheduled scan (crontab)
0 2 * * * /usr/bin/clamscan -r --quiet --infected --log=/var/log/clamav/scan.log /home

rkhunter and chkrootkit

# Install
apt install rkhunter chkrootkit -y

# Run rkhunter
rkhunter --update
rkhunter --check

# Run chkrootkit
chkrootkit

9. Filesystem Protection

Configure partitions with secure mount options

# /etc/fstab
/dev/sda1 /tmp    ext4 defaults,noexec,nosuid,nodev 0 0
/dev/sda2 /var    ext4 defaults,nosuid                0 0
/dev/sda3 /home   ext4 defaults,nodev,nosuid          0 0

# Apply changes
mount -o remount /tmp

Configure critical permissions

# Protect sensitive files
chmod 600 /etc/shadow
chmod 600 /etc/gshadow
chmod 644 /etc/passwd
chmod 644 /etc/group

# Remove unnecessary SUID/SGID
find / -perm /4000 -type f -exec ls -ld {} \;
find / -perm /2000 -type f -exec ls -ld {} \;

# Remove SUID from non-essential files
chmod u-s /usr/bin/wall

Automated Hardening Script

#!/bin/bash
# hardening.sh - Automated hardening script

set -euo pipefail

LOGFILE="/var/log/hardening.log"

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}

log "Starting system hardening..."

# Update system
log "Updating packages..."
apt update && apt upgrade -y

# Configure firewall
log "Configuring UFW..."
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp
ufw --force enable

# SSH hardening
log "Configuring SSH..."
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# Kernel hardening
log "Applying kernel configuration..."
cat >> /etc/sysctl.d/99-hardening.conf <<EOF
net.ipv4.ip_forward=0
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.all.accept_redirects=0
net.ipv4.tcp_syncookies=1
kernel.randomize_va_space=2
EOF
sysctl -p /etc/sysctl.d/99-hardening.conf

# Install security tools
log "Installing tools..."
apt install -y fail2ban auditd rkhunter

log "Hardening completed. Review $LOGFILE"

Automation and Audit Tools

Lynis

# Install
apt install lynis -y

# Run full audit
lynis audit system

# Review recommendations
cat /var/log/lynis.log

OpenSCAP

# Install
apt install libopenscap8 -y

# Download CIS profiles
wget https://github.com/ComplianceAsCode/content/releases/download/v0.1.66/scap-security-guide-0.1.66.zip
unzip scap-security-guide-0.1.66.zip

# Scan system
oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \
  --results scan-results.xml \
  ssg-ubuntu2004-ds.xml

# Generate HTML report
oscap xccdf generate report scan-results.xml > report.html

Ansible for Hardening

# hardening.yml
---
- name: Linux Server Hardening
  hosts: all
  become: yes
  tasks:
    - name: Update all packages
      apt:
        upgrade: dist
        update_cache: yes

    - name: Configure SSH
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no' }
      notify: restart ssh

    - name: Configure UFW
      ufw:
        rule: allow
        port: '{{ item }}'
        proto: tcp
      loop:
        - 2222
        - 80
        - 443

  handlers:
    - name: restart ssh
      service:
        name: sshd
        state: restarted

Continuous Monitoring

Fail2Ban for brute-force protection

# Install
apt install fail2ban -y

# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

systemctl restart fail2ban

# Check bans
fail2ban-client status sshd

AIDE (Advanced Intrusion Detection Environment)

# Install
apt install aide -y

# Initialize database
aideinit

# Move database
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# Check integrity (run daily)
aide --check

# Cron job
0 3 * * * /usr/bin/aide --check | mail -s "AIDE Report" admin@example.com

Final Validation Checklist

  • [ ] Automatic updates configured
  • [ ] SSH configured on non-standard port with keys
  • [ ] Root login disabled
  • [ ] Firewall active with minimal rules
  • [ ] SELinux/AppArmor in enforcing mode
  • [ ] Auditd configured and functional
  • [ ] Fail2Ban active for SSH
  • [ ] Unnecessary services disabled
  • [ ] Security kernel parameters applied
  • [ ] Logs rotating correctly
  • [ ] AIDE or similar for intrusion detection
  • [ ] Lynis scan passed
  • [ ] Backups configured and tested

References