Linux Security Hardening
Essential security hardening procedures for Linux servers
Table of Contents
Practical security hardening steps for Linux servers in production environments. These procedures balance security with operational requirements.
SSH Security
Disable Root Login
Why: Prevents direct root access attempts. Forces attackers to know both a valid username and password.
# Edit SSH config
sudo vi /etc/ssh/sshd_config
# Set these directives
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no# Restart SSH
sudo systemctl restart sshdChange SSH Port
Why: Reduces automated scanning attacks on default port 22.
# Edit SSH config
sudo vi /etc/ssh/sshd_config
# Change port
Port 2222
# Restart SSH
sudo systemctl restart sshd
# Update firewall
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reloadNote: Update your connection commands:
ssh -p 2222 user@hostnameSSH Key Authentication
Generate Key Pair (On Client)
# Generate ED25519 key (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Or RSA 4096-bit
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Accept default location: ~/.ssh/id_ed25519
# Set a strong passphraseCopy Public Key to Server
# Method 1: Using ssh-copy-id
ssh-copy-id user@hostname
# Method 2: Manual copy
cat ~/.ssh/id_ed25519.pub | ssh user@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Method 3: Manually paste into server
# Copy contents of ~/.ssh/id_ed25519.pub
# On server:
mkdir -p ~/.ssh
vi ~/.ssh/authorized_keys
# Paste public key
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keysTest Key Authentication
ssh user@hostname
# Should log in without password promptDisable Password Authentication
# Only after confirming key auth works!
sudo vi /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
sudo systemctl restart sshdAdditional SSH Hardening
# Edit /etc/ssh/sshd_config
# Limit SSH protocol to version 2
Protocol 2
# Specify allowed users
AllowUsers user1 user2
# Or allowed groups
AllowGroups sshusers
# Set login grace time
LoginGraceTime 60
# Max authentication attempts
MaxAuthTries 3
# Max concurrent sessions
MaxSessions 2
# Disable X11 forwarding (if not needed)
X11Forwarding no
# Disable TCP forwarding (if not needed)
AllowTcpForwarding no
# Set strong ciphers and MACs
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
# Restart SSH
sudo systemctl restart sshdFirewall Configuration
firewalld (RHEL/CentOS/Fedora)
Basic Setup
# Install firewalld
sudo dnf install firewalld
# Start and enable
sudo systemctl start firewalld
sudo systemctl enable firewalld
# Check status
sudo firewall-cmd --state
# List all rules
sudo firewall-cmd --list-allCommon Operations
# Allow service
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
# Allow specific port
sudo firewall-cmd --permanent --add-port=8080/tcp
# Remove service
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
# Block IP address
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" reject'
# Allow specific IP to specific port
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port port="22" protocol="tcp" accept'
# Reload firewall
sudo firewall-cmd --reload
# List active zones
sudo firewall-cmd --get-active-zones
# List services in zone
sudo firewall-cmd --zone=public --list-services
# List ports in zone
sudo firewall-cmd --zone=public --list-portsufw (Ubuntu/Debian)
Basic Setup
# Install ufw
sudo apt install ufw
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH before enabling
sudo ufw allow ssh
# Or specific port
sudo ufw allow 22/tcp
# Enable firewall
sudo ufw enable
# Check status
sudo ufw status verboseCommon Operations
# Allow service
sudo ufw allow http
sudo ufw allow https
# Allow specific port
sudo ufw allow 8080/tcp
# Allow port range
sudo ufw allow 6000:6007/tcp
# Allow from specific IP
sudo ufw allow from 192.168.1.100
# Allow from subnet to specific port
sudo ufw allow from 10.0.0.0/8 to any port 22
# Deny specific IP
sudo ufw deny from 192.168.1.100
# Delete rule
sudo ufw delete allow 8080/tcp
# Numbered rules (easier deletion)
sudo ufw status numbered
sudo ufw delete 2
# Reset firewall
sudo ufw resetiptables (Legacy but still common)
Basic Setup
# List current rules
sudo iptables -L -n -v
# Flush all rules (careful!)
sudo iptables -F
# Set default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow from specific IP
sudo iptables -A INPUT -s 192.168.1.100 -j ACCEPT
# Save rules
# RHEL/CentOS
sudo service iptables save
# Ubuntu/Debian
sudo iptables-save > /etc/iptables/rules.v4Fail2ban
Purpose: Automatically ban IPs with multiple failed authentication attempts.
Installation and Setup
# Install fail2ban
# RHEL/CentOS
sudo dnf install fail2ban
# Ubuntu/Debian
sudo apt install fail2ban
# Start and enable
sudo systemctl start fail2ban
sudo systemctl enable fail2banConfiguration
# Don't edit jail.conf directly - use jail.local
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vi /etc/fail2ban/jail.local
# Basic configuration
[DEFAULT]
# Ban time in seconds (10 minutes)
bantime = 600
# Time window for findtime
findtime = 600
# Number of failures before ban
maxretry = 5
# Email notifications (optional)
destemail = admin@domain.com
sendername = Fail2Ban
action = %(action_mwl)s
# SSH jail (usually enabled by default)
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 3600Common Operations
# Check status
sudo fail2ban-client status
# Check specific jail
sudo fail2ban-client status sshd
# Unban IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
# Ban IP manually
sudo fail2ban-client set sshd banip 192.168.1.100
# Reload configuration
sudo fail2ban-client reload
# View banned IPs
sudo fail2ban-client status sshd
# Check logs
sudo tail -f /var/log/fail2ban.logCustom Jails
Apache Auth
[apache-auth]
enabled = true
port = http,https
logpath = %(apache_error_log)s
maxretry = 3WordPress
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/auth.log
maxretry = 3
port = http,httpsUser and Sudo Management
User Account Security
Create Non-Root User
# Add user
sudo adduser username
# Set strong password
sudo passwd username
# Add to sudo group
# Ubuntu/Debian
sudo usermod -aG sudo username
# RHEL/CentOS
sudo usermod -aG wheel usernameDisable Unused Accounts
# List all users
cat /etc/passwd
# Disable account
sudo usermod -L username
sudo usermod -s /sbin/nologin username
# Or delete account
sudo userdel username
# Delete with home directory
sudo userdel -r usernamePassword Policy
# Edit /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_MIN_LEN 12
PASS_WARN_AGE 7
# Install password quality checking
# Ubuntu/Debian
sudo apt install libpam-pwquality
# RHEL/CentOS
sudo dnf install libpwquality
# Edit /etc/security/pwquality.conf
minlen = 12
dcredit = -1 # At least 1 digit
ucredit = -1 # At least 1 uppercase
lcredit = -1 # At least 1 lowercase
ocredit = -1 # At least 1 special charSudo Configuration
Limit Sudo Access
# Edit sudoers file (always use visudo!)
sudo visudo
# Allow user to run all commands
username ALL=(ALL:ALL) ALL
# Allow user to run specific commands
username ALL=(ALL) /usr/bin/systemctl, /usr/bin/apt
# Allow group
%groupname ALL=(ALL:ALL) ALL
# No password for specific commands (use carefully!)
username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart apache2
# Require password every time
Defaults timestamp_timeout=0
# Log sudo commands
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_outputAutomatic Security Updates
Ubuntu/Debian
# Install unattended-upgrades
sudo apt install unattended-upgrades
# Configure
sudo dpkg-reconfigure -plow unattended-upgrades
# Edit configuration
sudo vi /etc/apt/apt.conf.d/50unattended-upgrades
# Enable automatic security updates
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
# Email notifications
Unattended-Upgrade::Mail "admin@domain.com";
Unattended-Upgrade::MailReport "on-change";
# Automatic reboot if required
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";RHEL/CentOS
# Install dnf-automatic
sudo dnf install dnf-automatic
# Configure
sudo vi /etc/dnf/automatic.conf
[commands]
upgrade_type = security
download_updates = yes
apply_updates = yes
[emitters]
emit_via = email
email_from = root@localhost
email_to = admin@domain.com
# Enable and start
sudo systemctl enable --now dnf-automatic.timerFile System Security
File Permissions
Secure Important Files
# Secure SSH keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
# Secure sensitive files
chmod 600 /etc/ssh/sshd_config
chmod 644 /etc/passwd
chmod 000 /etc/shadow
chmod 000 /etc/gshadow
# Find world-writable files
find / -type f -perm -002 -ls 2>/dev/null
# Find SUID/SGID files
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/nullDisable Unused Filesystems
# Edit /etc/modprobe.d/disabled-filesystems.conf
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install udf /bin/truePartition Security
Secure /tmp
# Edit /etc/fstab
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
# Remount
sudo mount -o remount /tmpAudit Logging
Configure auditd
# Install auditd
# RHEL/CentOS
sudo dnf install audit
# Ubuntu/Debian
sudo apt install auditd
# Start and enable
sudo systemctl start auditd
sudo systemctl enable auditdCommon Audit Rules
# Edit /etc/audit/rules.d/audit.rules
# Monitor /etc/passwd
-w /etc/passwd -p wa -k passwd_changes
# Monitor /etc/group
-w /etc/group -p wa -k group_changes
# Monitor sudo usage
-w /etc/sudoers -p wa -k sudoers_changes
-w /var/log/sudo.log -p wa -k sudo_log
# Monitor SSH
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Monitor login/logout
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
# Monitor network changes
-w /etc/sysconfig/network -p wa -k network_changes
# Reload rules
sudo augenrules --loadSearch Audit Logs
# Search for events
ausearch -k passwd_changes
# Search by time
ausearch -ts today -k sudo_log
# Generate report
aureport --summaryKernel Hardening
sysctl Security Settings
# Edit /etc/sysctl.conf or /etc/sysctl.d/99-security.conf
# IP Forwarding (disable if not a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Syn flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Enable reverse path filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Log martian packets
net.ipv4.conf.all.log_martians = 1
# Ignore ICMP ping requests (optional)
net.ipv4.icmp_echo_ignore_all = 1
# Ignore broadcast pings
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Enable bad error message protection
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Apply settings
sudo sysctl -pSELinux / AppArmor
SELinux (RHEL/CentOS/Fedora)
Check Status
# Check SELinux status
getenforce
sestatus
# View denials
ausearch -m avc -ts recentSet to Enforcing
# Temporarily
sudo setenforce 1
# Permanently (edit /etc/selinux/config)
SELINUX=enforcing
# Reboot required for permanent changeCommon Operations
# Restore default context
restorecon -Rv /path/to/directory
# Change context permanently
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
restorecon -Rv /web
# Allow specific action
sealert -a /var/log/audit/audit.log
# Review suggestions and apply if appropriateAppArmor (Ubuntu/Debian)
Check Status
# Check status
sudo apparmor_status
# List profiles
sudo aa-statusManage Profiles
# Put profile in complain mode
sudo aa-complain /etc/apparmor.d/usr.sbin.apache2
# Put profile in enforce mode
sudo aa-enforce /etc/apparmor.d/usr.sbin.apache2
# Disable profile
sudo ln -s /etc/apparmor.d/usr.sbin.apache2 /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.apache2
# Enable profile
sudo rm /etc/apparmor.d/disable/usr.sbin.apache2
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.apache2Disable Unnecessary Services
# List all services
systemctl list-unit-files --type=service
# Disable service
sudo systemctl disable servicename
sudo systemctl stop servicename
# Common services to disable (if not needed)
sudo systemctl disable bluetooth
sudo systemctl disable cups # Printing
sudo systemctl disable avahi-daemon # mDNSRemove Unnecessary Packages
# List installed packages
# Ubuntu/Debian
dpkg --list
# RHEL/CentOS
rpm -qa
# Remove package
sudo apt remove package-name
sudo dnf remove package-name
# Remove with dependencies
sudo apt autoremoveSecurity Scanning
Lynis
# Install Lynis
# Ubuntu/Debian
sudo apt install lynis
# RHEL/CentOS
sudo dnf install lynis
# Run audit
sudo lynis audit system
# View report
cat /var/log/lynis-report.dat
# Implement suggestions from reportClamAV
# Install ClamAV
sudo apt install clamav clamav-daemon
# Update virus definitions
sudo freshclam
# Scan directory
sudo clamscan -r /home
# Scan and remove infected files
sudo clamscan -r --remove /home
# Scan and quarantine
sudo clamscan -r --move=/quarantine /homeMonitoring and Alerting
Log Monitoring with logwatch
# Install logwatch
sudo apt install logwatch
# Run manually
sudo logwatch --detail High --mailto admin@domain.com --range today
# Configure daily email
sudo vi /etc/cron.daily/00logwatch
#!/bin/bash
/usr/sbin/logwatch --output mail --mailto admin@domain.com --detail highFile Integrity Monitoring with AIDE
# Install AIDE
sudo apt install aide
# Initialize database
sudo aideinit
# Move database to proper location
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Run check
sudo aide --check
# Update database after legitimate changes
sudo aide --update
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Automate daily checks
sudo vi /etc/cron.daily/aide
#!/bin/bash
/usr/bin/aide --check | mail -s "AIDE Report for $(hostname)" admin@domain.comHardening Checklist
Essential (Do on all servers)
- Disable root SSH login
- Use SSH keys only
- Configure firewall
- Install and configure fail2ban
- Keep system updated
- Remove unnecessary packages
- Disable unused services
- Set strong password policy
- Configure sudo properly
- Enable audit logging
Recommended
- Change SSH port
- Enable automatic security updates
- Configure SELinux/AppArmor
- Implement kernel hardening
- Set up log monitoring
- Configure file integrity monitoring
- Regular security scans with Lynis
Advanced
- Two-factor authentication for SSH
- Intrusion detection system (IDS)
- Security Information and Event Management (SIEM)
- Regular penetration testing
- Container security (if using Docker)