Firewall Setup & SSH Hardening

Secure your Linux servers with professional firewall configuration and SSH hardening. This DevOps guide covers UFW, iptables, fail2ban, and essential security practices to protect your infrastructure from attacks.

Why Security Hardening Matters

Unsecured SSH and open ports are the #1 attack vector for Linux servers. Automated bots scan the internet 24/7 for vulnerabilities. Proper hardening reduces your attack surface by 90%.

Port 22: SSH (Default)
Port 80: HTTP
Port 443: HTTPS
Port 21: FTP ❌
Port 23: Telnet ❌
Port 3306: MySQL ❌

Essential Security Tools

FW
Firewall Management

UFW (Uncomplicated Firewall) - Simple frontend for iptables

iptables - Advanced packet filtering

nftables - Modern replacement for iptables

SSH
SSH Hardening

sshd_config - SSH server configuration

ssh-keygen - Key pair generation

ssh-copy-id - Key deployment

F2B
Intrusion Prevention

fail2ban - Block brute force attacks

crowdsec - Modern IPS alternative

logwatch - Log analysis

Step 1: Firewall Setup with UFW

Basic UFW Configuration

# Install UFW (if not installed)
sudo apt update
sudo apt install ufw -y

# Check UFW status
sudo ufw status
sudo ufw status verbose

# Default policies: DENY incoming, ALLOW outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (essential - do this BEFORE enabling!)
sudo ufw allow ssh
# OR specify custom port
sudo ufw allow 2222/tcp

# Allow web ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Allow specific IP addresses
sudo ufw allow from 192.168.1.100
sudo ufw allow from 192.168.1.0/24

# Enable UFW (CAREFUL: ensure SSH is allowed!)
sudo ufw enable

# Disable UFW (if locked out)
sudo ufw disable
Warning: Always allow SSH port BEFORE enabling UFW! Otherwise you'll lock yourself out of the server.

Advanced UFW Rules

# Allow port range
sudo ufw allow 3000:4000/tcp

# Allow specific protocol
sudo ufw allow 53/udp  # DNS

# Rate limiting (prevents brute force)
sudo ufw limit ssh
sudo ufw limit 2222/tcp

# Delete rules
sudo ufw status numbered  # List with numbers
sudo ufw delete 2         # Delete rule #2

# Deny specific IP
sudo ufw deny from 203.0.113.10

# Application profiles
sudo ufw app list
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'

# Logging
sudo ufw logging on
sudo ufw logging high

# Show rules with numbers
sudo ufw status numbered

# Backup UFW rules
sudo ufw show added > ufw-backup.txt
# Restore
sudo ufw reset
cat ufw-backup.txt | sudo ufw

Step 2: SSH Hardening

Essential sshd_config Changes

# Backup original config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Edit SSH configuration
sudo nano /etc/ssh/sshd_config

# === SECURE SETTINGS ===
# Change SSH port (pick between 1024-65535)
Port 2222

# Disable root login
PermitRootLogin no

# Limit user access
AllowUsers alice bob charlie
# OR use groups
AllowGroups ssh-users

# Use key authentication only
PasswordAuthentication no
ChallengeResponseAuthentication no

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

# Connection settings
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
MaxSessions 10

# Restrict SFTP users (if needed)
Match Group sftp-only
    ChrootDirectory /home/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no

# Reload SSH (don't restart - test first!)
sudo systemctl reload ssh
# If locked out, use console or VNC to revert changes
Pro Tip: Test SSH changes with another terminal window BEFORE closing your current SSH session. Use: ssh -p 2222 user@server

SSH Key Management

# Generate SSH key pair (on your local machine)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Or for legacy systems
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

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

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

# Add extra security to authorized_keys
# Edit ~/.ssh/authorized_keys and add restrictions:
from="192.168.1.*",command="/bin/true",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3...

# SSH agent for passphrase management
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# Test connection without password
ssh -p 2222 user@server

Step 3: Install & Configure fail2ban

# Install fail2ban
sudo apt update
sudo apt install fail2ban -y

# Copy configuration file
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Edit main configuration
sudo nano /etc/fail2ban/jail.local

# === IMPORTANT SETTINGS ===
[DEFAULT]
# Ban IP for 1 hour
bantime = 3600
# Maximum retries before ban
maxretry = 5
# Time window for retries
findtime = 600
# Whitelist your IP
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

# SSH jail (custom port)
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3

# Nginx jail
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log

# Custom jail for specific attacks
[my-custom-jail]
enabled = true
filter = myfilter
logpath = /var/log/myapp.log
maxretry = 3
bantime = 86400

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

# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd

# Unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

DevOps Comparison: Firewall Tools

Tool Best For Complexity DevOps Use Case
UFW Quick setup, simple rules LOW Development servers, small projects
iptables Full control, complex rules HIGH Production, network gateways
firewalld Dynamic rules, zones MEDIUM RHEL/CentOS servers
nftables Modern, high performance HIGH New deployments, high traffic

Advanced iptables Configuration

# Save current rules
sudo iptables-save > iptables-backup.txt

# Basic iptables script
#!/bin/bash

# Flush all rules
iptables -F
iptables -X

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (custom port)
iptables -A INPUT -p tcp --dport 2222 -j ACCEPT

# Allow web ports
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Rate limiting for SSH
iptables -A INPUT -p tcp --dport 2222 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 2222 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

# ICMP (ping) - allow but limit
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/second -j ACCEPT

# Save rules permanently
sudo apt install iptables-persistent
sudo netfilter-persistent save

# View rules
sudo iptables -L -v -n
sudo iptables -L -v -n --line-numbers

Complete Security Checklist

Change SSH port from 22 to custom port (2222, 5022, etc.)
Disable root SSH login
Disable password authentication (use keys only)
Install and configure fail2ban
Set up UFW/iptables with minimum required ports
Use strong SSH key algorithms (ed25519 preferred)
Implement SSH connection timeouts
Restrict SSH access to specific users/groups
Regularly update SSH keys and remove unused ones
Monitor auth.log for failed attempts

Automation Scripts for DevOps

1. Automated Server Hardening Script

#!/bin/bash
# auto-harden.sh - Automated server hardening

set -e

echo "=== Starting Server Hardening ==="

# Backup everything first
BACKUP_DIR="/root/backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
cp /etc/ssh/sshd_config $BACKUP_DIR/
cp /etc/ufw/user.rules $BACKUP_DIR/ 2>/dev/null || true

# 1. Update system
apt update && apt upgrade -y

# 2. Configure UFW
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp comment 'SSH Custom Port'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw --force enable

# 3. Harden SSH
sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^ChallengeResponseAuthentication yes/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config

echo "AllowUsers $(whoami)" >> /etc/ssh/sshd_config

# 4. Install fail2ban
apt install -y fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

cat >> /etc/fail2ban/jail.local << EOF
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF

systemctl restart fail2ban

# 5. Set secure kernel parameters
echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.rp_filter = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.conf
sysctl -p

echo "=== Hardening Complete ==="
echo "IMPORTANT: Test SSH on port 2222 before closing this session!"
echo "Command: ssh -p 2222 $(whoami)@$(hostname -I | awk '{print $1}')"

2. SSH Security Audit Script

#!/bin/bash
# ssh-audit.sh - Check SSH security configuration

echo "=== SSH Security Audit ==="
echo "Date: $(date)"
echo ""

# 1. Check SSH port
echo "1. SSH Port:"
ss -tlnp | grep sshd || echo "SSH not running!"

# 2. Check sshd_config
echo -e "\n2. SSH Configuration:"
echo "Root Login: $(grep -i 'PermitRootLogin' /etc/ssh/sshd_config | tail -1)"
echo "Password Auth: $(grep -i 'PasswordAuthentication' /etc/ssh/sshd_config | tail -1)"
echo "Max Auth Tries: $(grep -i 'MaxAuthTries' /etc/ssh/sshd_config | tail -1)"

# 3. Check authorized_keys permissions
echo -e "\n3. SSH Key Permissions:"
for user in /home/*; do
    if [ -f "$user/.ssh/authorized_keys" ]; then
        perms=$(stat -c "%a" "$user/.ssh/authorized_keys" 2>/dev/null || echo "N/A")
        echo "  $user: $perms"
    fi
done

# 4. Check failed login attempts
echo -e "\n4. Failed Login Attempts (last 24h):"
grep "Failed password" /var/log/auth.log | grep "$(date +%Y-%m-%d)" | wc -l

# 5. Check connected SSH sessions
echo -e "\n5. Current SSH Sessions:"
who | grep -E "pts/|ssh"

# 6. Check for weak SSH keys
echo -e "\n6. SSH Key Types:"
for key in /etc/ssh/ssh_host_*_key.pub; do
    if [ -f "$key" ]; then
        type=$(ssh-keygen -l -f "$key" | awk '{print $4}')
        echo "  $key: $type"
    fi
done

echo -e "\n=== Audit Complete ==="

Troubleshooting Common Issues

Problem: Locked out after SSH changes

# Solution 1: Use console/VNC access
# 1. Log in via cloud console or VNC
# 2. Restore backup config
sudo cp /etc/ssh/sshd_config.backup /etc/ssh/sshd_config
sudo systemctl restart ssh

# Solution 2: If UFW blocked SSH
sudo ufw disable
sudo ufw allow ssh
sudo ufw enable

# Solution 3: Check iptables
sudo iptables -F
sudo iptables -P INPUT ACCEPT
sudo systemctl restart ssh

Problem: fail2ban not blocking attacks

# Check fail2ban status
sudo fail2ban-client status
sudo fail2ban-client status sshd

# Check logs
sudo tail -f /var/log/fail2ban.log

# Test filter
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# Manual ban
sudo fail2ban-client set sshd banip 192.168.1.100

# Restart fail2ban
sudo systemctl restart fail2ban

Monitoring & Maintenance

# Daily security checks
# 1. Check failed SSH attempts
sudo grep "Failed password" /var/log/auth.log | tail -20

# 2. Check fail2ban bans
sudo fail2ban-client status sshd

# 3. Check open ports
sudo netstat -tulpn | grep LISTEN
sudo ss -tulpn

# 4. Check UFW status
sudo ufw status verbose

# 5. Check connected SSH users
sudo who
sudo w

# 6. Monitor real-time attacks
sudo tail -f /var/log/auth.log | grep -E "Failed|Invalid"

# 7. Check for root login attempts
sudo grep "root" /var/log/auth.log | grep "Failed"

# 8. Weekly: Update SSH keys
# Remove unused keys from authorized_keys
# Rotate host keys (optional)
sudo rm /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server
Production Best Practices:
1. Always test changes in staging first
2. Keep backup SSH sessions open during changes
3. Document all firewall and SSH rules
4. Regularly audit and remove unused rules
5. Use Infrastructure as Code (Ansible/Terraform) for consistency
6. Implement automated security scanning
7. Set up alerts for failed login attempts
8. Regularly rotate SSH keys (every 6-12 months)

DevOps Security Mindset

Security is not a one-time setup but an ongoing process. A properly hardened server reduces attack surface by 90% and prevents most automated attacks.

Remember: Default configurations are insecure by design. Always assume your servers will be attacked and build defenses accordingly.

Next Steps: Implement centralized logging (ELK stack), set up intrusion detection systems (OSSEC), and automate compliance scanning with tools like Lynis or OpenSCAP.

Quick Reference Commands:
• UFW status: sudo ufw status verbose
• Test SSH: ssh -p 2222 user@server
• Check banned IPs: sudo fail2ban-client status sshd
• View open ports: sudo ss -tulpn
• Monitor attacks: sudo tail -f /var/log/auth.log
• Backup SSH config: sudo cp /etc/ssh/sshd_config{,.backup}