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.

Lang: bash
# Edit SSH config
sudo vi /etc/ssh/sshd_config

# Set these directives
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
Lang: bash
# Restart SSH
sudo systemctl restart sshd

Change SSH Port

Why: Reduces automated scanning attacks on default port 22.

Lang: bash
# 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 --reload

Note: Update your connection commands:

Lang: bash
ssh -p 2222 user@hostname

SSH Key Authentication

Generate Key Pair (On Client)

Lang: bash
# 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 passphrase

Copy Public Key to Server

Lang: bash
# 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_keys

Test Key Authentication

Lang: bash
ssh user@hostname
# Should log in without password prompt

Disable Password Authentication

Lang: bash
# Only after confirming key auth works!
sudo vi /etc/ssh/sshd_config

PasswordAuthentication no
ChallengeResponseAuthentication no

sudo systemctl restart sshd

Additional SSH Hardening

Lang: bash
# 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 sshd

Firewall Configuration

firewalld (RHEL/CentOS/Fedora)

Basic Setup

Lang: bash
# 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-all

Common Operations

Lang: bash
# 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-ports

ufw (Ubuntu/Debian)

Basic Setup

Lang: bash
# 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 verbose

Common Operations

Lang: bash
# 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 reset

iptables (Legacy but still common)

Basic Setup

Lang: bash
# 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.v4

Fail2ban

Purpose: Automatically ban IPs with multiple failed authentication attempts.

Installation and Setup

Lang: bash
# Install fail2ban
# RHEL/CentOS
sudo dnf install fail2ban

# Ubuntu/Debian
sudo apt install fail2ban

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

Configuration

Lang: bash
# 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 = 3600

Common Operations

Lang: bash
# 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.log

Custom Jails

Apache Auth

Lang: ini
[apache-auth]
enabled = true
port = http,https
logpath = %(apache_error_log)s
maxretry = 3

WordPress

Lang: ini
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/auth.log
maxretry = 3
port = http,https

User and Sudo Management

User Account Security

Create Non-Root User

Lang: bash
# 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 username

Disable Unused Accounts

Lang: bash
# 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 username

Password Policy

Lang: bash
# 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 char

Sudo Configuration

Limit Sudo Access

Lang: bash
# 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_output

Automatic Security Updates

Ubuntu/Debian

Lang: bash
# 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

Lang: bash
# 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.timer

File System Security

File Permissions

Secure Important Files

Lang: bash
# 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/null

Disable Unused Filesystems

Lang: bash
# 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/true

Partition Security

Secure /tmp

Lang: bash
# Edit /etc/fstab
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0

# Remount
sudo mount -o remount /tmp

Audit Logging

Configure auditd

Lang: bash
# Install auditd
# RHEL/CentOS
sudo dnf install audit

# Ubuntu/Debian
sudo apt install auditd

# Start and enable
sudo systemctl start auditd
sudo systemctl enable auditd

Common Audit Rules

Lang: bash
# 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 --load

Search Audit Logs

Lang: bash
# Search for events
ausearch -k passwd_changes

# Search by time
ausearch -ts today -k sudo_log

# Generate report
aureport --summary

Kernel Hardening

sysctl Security Settings

Lang: bash
# 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 -p

SELinux / AppArmor

SELinux (RHEL/CentOS/Fedora)

Check Status

Lang: bash
# Check SELinux status
getenforce
sestatus

# View denials
ausearch -m avc -ts recent

Set to Enforcing

Lang: bash
# Temporarily
sudo setenforce 1

# Permanently (edit /etc/selinux/config)
SELINUX=enforcing

# Reboot required for permanent change

Common Operations

Lang: bash
# 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 appropriate

AppArmor (Ubuntu/Debian)

Check Status

Lang: bash
# Check status
sudo apparmor_status

# List profiles
sudo aa-status

Manage Profiles

Lang: bash
# 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.apache2

Disable Unnecessary Services

Lang: bash
# 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  # mDNS

Remove Unnecessary Packages

Lang: bash
# 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 autoremove

Security Scanning

Lynis

Lang: bash
# 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 report

ClamAV

Lang: bash
# 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 /home

Monitoring and Alerting

Log Monitoring with logwatch

Lang: bash
# 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 high

File Integrity Monitoring with AIDE

Lang: bash
# 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.com

Hardening 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)