Linux: Firewall and Security Basics
Within minutes of connecting a new Linux server to the internet, it will be scanned and targeted by attack attempts. Security hardening isn't optional — it's mandatory.
📋 Prerequisites: You should already know
- Lesson 23: Networking Basics
1. What You'll Learn
- ufw basic rule configuration
- SSH key authentication
- SSH security hardening
- fail2ban brute-force protection
- System security updates
2. A Real Story of Being Attacked
(1) The Pain Point: Brute-Force Attacks on Day Two
Xiaoming bought a cloud server to host his blog. On day two, checking the logs was terrifying:
grep "Failed password" /var/log/auth.log | wc -l
# 1234 attempts!
Automated scanners worldwide were constantly trying to log in on SSH port 22.
(2) Three Layers of Hardening
He did three things:
# 1. Switch to key authentication
ssh-keygen -t ed25519
ssh-copy-id user@server
# 2. Disable password login
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
# 3. Install fail2ban: 5 failures → 24-hour ban
sudo apt install fail2ban
sudo systemctl enable fail2ban
(3) The Benefit: Attacks Drop to Zero
After hardening, failed attempts in auth.log went from 1000+ per day to 0.
3. Knowledge Points
(1) ufw — Uncomplicated Firewall
# Basic operations
sudo ufw status # View status
sudo ufw enable # Enable firewall
sudo ufw disable # Disable firewall
sudo ufw reload # Reload rules
# Rule management
sudo ufw allow 22/tcp # Allow SSH
sudo ufw allow 80/tcp # Allow HTTP
sudo ufw allow 443/tcp # Allow HTTPS
sudo ufw deny 23/tcp # Deny Telnet
sudo ufw deny from 10.0.0.100 # Block specific IP
# Application profiles
sudo ufw app list # List applications
sudo ufw allow "Nginx Full" # Allow Nginx (80+443)
sudo ufw allow "OpenSSH" # Allow SSH
# Rule management
sudo ufw status numbered # Show rules with numbers
sudo ufw delete 2 # Delete rule 2
allow from 0.0.0.0/0 before deny from malicious_ip, the deny never triggers — the allow already matched. Pay attention to rule order and use ufw status numbered to check the rule list.
ufw allow from to specify trusted IPs, never use 0.0.0.0/0 (allows all IPs) — this is equivalent to no restriction and defeats the purpose of a firewall. Always specify concrete IPs or CIDR ranges, like ufw allow from 192.168.1.0/24.
New Server Security Policy:
ufw default deny incoming means all inbound connections are denied by default — a whitelist approach where only explicitly allowed ports are accessible. This is the safest default policy, but you must allow SSH (22/tcp) before enabling it, or you'll lock yourself out of the server.
# Default deny all incoming
sudo ufw default deny incoming
# Default allow all outgoing
sudo ufw default allow outgoing
# Only open needed ports
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
(2) SSH Key Authentication
# Generate key pair (Ed25519 recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Traditional RSA (4096-bit)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Copy public key to server
ssh-copy-id user@server-ip
# Or add manually
cat ~/.ssh/id_ed25519.pub | ssh user@server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Verify key login
ssh user@server-ip
(3) SSH Security Hardening
ufw allow 2222/tcp), or the firewall will block connections on the new port. Keep an existing SSH session open before making changes as a safety fallback.
Edit /etc/ssh/sshd_config:
Port 2222 # Change default port (evade automated scans)
PermitRootLogin no # Disable direct root login
PasswordAuthentication no # Disable password login
PubkeyAuthentication yes # Enable key login
AllowUsers alice bob # Only allow specified users
MaxAuthTries 3 # Maximum authentication attempts
ClientAliveInterval 60 # Client keepalive interval (seconds)
ClientAliveCountMax 3 # Maximum keepalive failures
Verify configuration and restart:
sudo sshd -t # Test config syntax
sudo systemctl restart sshd # Restart SSH service
(4) fail2ban
jail.conf is provided by the package and may be overwritten on upgrades. Custom configurations should be placed in jail.local or the /etc/fail2ban/jail.d/ directory — these files won't be overwritten by upgrades and have higher priority.
# Install
sudo apt install fail2ban
# View status
sudo fail2ban-client status
sudo fail2ban-client status sshd
# Default config (/etc/fail2ban/jail.conf), usually no need to change
# [sshd]
# enabled = true
# port = ssh
# filter = sshd
# logpath = /var/log/auth.log
# maxretry = 5
# bantime = 3600
# Create local override
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo systemctl restart fail2ban
# View banned IPs
sudo iptables -L f2b-sshd -n
(5) ▶ Example: Complete ufw Configuration
# If SSH is connected, allow SSH first
sudo ufw allow 22/tcp
# Configure default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Open service ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 2222/tcp # If SSH port was changed
# Enable
sudo ufw enable
sudo ufw status verbose
Output:
TEXTRule added Rule added Default incoming policy changed to 'deny' Default outgoing policy changed to 'allow' Rule added Rule added Rule added Firewall is active and enabled on system startup Status: active Logging: on (low) To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 80/tcp ALLOW IN Anywhere 443/tcp ALLOW IN Anywhere 2222/tcp ALLOW IN Anywhere
(6) ▶ Example: SSH Key Creation and Usage
# 1. Generate key pair on local machine
ssh-keygen -t ed25519
# 2. Copy to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@192.168.1.100
# 3. Test passwordless login
ssh alice@192.168.1.100
# 4. Use SSH config file for simplified login
cat >> ~/.ssh/config << 'EOF'
Host myserver
HostName 192.168.1.100
User alice
Port 2222
IdentityFile ~/.ssh/id_ed25519
EOF
# Now you can use the shorthand
ssh myserver
Output:
TEXTGenerating public/private ed25519 key pair. Enter file in which to save the key (/home/alice/.ssh/id_ed25519): Your identification has been saved in /home/alice/.ssh/id_ed25519 Your public key has been saved in /home/alice/.ssh/id_ed25519.pub The key fingerprint is: SHA256:abc123def456 alice@workstation /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/alice/.ssh/id_ed25519.pub" Number of key(s) added: 1 Now try logging into the machine: ssh alice@192.168.1.100 Welcome to Ubuntu 22.04.3 LTS Last login: Tue Jul 7 10:23:45 2026
(7) ▶ Example: SSH Hardening in Practice
# Back up original config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# Modify configuration
sudo sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config
sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
# Verify configuration
sudo sshd -t
# Restart SSH
sudo systemctl restart sshd
Output:
TEXT(no output from sshd -t means config is valid)
(8) ▶ Example: fail2ban Configuration
# View fail2ban status
sudo fail2ban-client status
# View SSH jail status
sudo fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# | |- Currently failed: 3
# | |- Total failed: 45
# | `- File list: /var/log/auth.log
# `- Actions
# |- Currently banned: 5
# |- Total banned: 12
# `- Banned IP list: 192.168.1.100 10.0.0.50
# Manually unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
# Custom ban duration
sudo tee /etc/fail2ban/jail.d/custom-sshd.conf << 'EOF'
[sshd]
enabled = true
maxretry = 3
bantime = 86400 # 24 hours
findtime = 600 # within 10 minutes
EOF
sudo systemctl restart fail2ban
Output:
TEXTStatus for the jail: sshd |- Filter | |- Currently failed: 3 | |- Total failed: 45 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 5 |- Total banned: 12 `- Banned IP list: 192.168.1.100 10.0.0.50 1 unbanned [sshd] enabled = true maxretry = 3 bantime = 86400 findtime = 600
(9) ▶ Example: Automatic Security Updates
# Install unattended-upgrades
sudo apt install unattended-upgrades
# Configure
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Or manually edit config
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}:${distro_codename}-updates";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
EOF
Output:
TEXTReading package lists... Done Building dependency tree... Done The following NEW packages will be installed: unattended-upgrades 0 upgraded, 1 newly installed, 0 to remove
(10) ▶ Comprehensive Example: New Server One-Click Security Hardening
#!/bin/bash
# secure-server.sh - New server security hardening script
# Instructions: Run immediately after first SSH login
# Note: Make sure you have another SSH session open in a separate terminal as a backup!
set -euo pipefail
SSH_PORT="${1:-2222}"
echo "========================================"
echo " Server Security Hardening"
echo " Time: $(date)"
echo "========================================"
# 1. Update system
echo ">>> 1/6 Updating system..."
sudo apt update && sudo apt upgrade -y
# 2. Configure firewall
echo ">>> 2/6 Configuring firewall..."
sudo ufw --force disable
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow "$SSH_PORT/tcp" comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw --force enable
echo "Firewall configured (SSH port: $SSH_PORT)"
# 3. Configure SSH keys
echo ">>> 3/6 Configuring SSH keys..."
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 4. SSH hardening
echo ">>> 4/6 SSH security hardening..."
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo sed -i "s/^#Port 22/Port $SSH_PORT/" /etc/ssh/sshd_config
sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#MaxAuthTries 6/MaxAuthTries 3/' /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshd
echo "SSH hardening complete"
# 5. Install fail2ban
echo ">>> 5/6 Installing fail2ban..."
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.d/sshd.conf << EOF
[sshd]
enabled = true
port = $SSH_PORT
maxretry = 3
bantime = 86400
findtime = 600
EOF
sudo systemctl enable --now fail2ban
# 6. Automatic security updates
echo ">>> 6/6 Configuring automatic updates..."
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades -y
echo ""
echo "========================================"
echo "✅ Security hardening complete!"
echo ""
echo "Important notes:"
echo " 1. SSH port changed to $SSH_PORT"
echo " 2. Root login disabled"
echo " 3. Password login disabled"
echo " 4. Ensure your public key is added to ~/.ssh/authorized_keys"
echo " 5. Before closing this SSH session, test connection in a new terminal:"
echo " ssh -p $SSH_PORT ${USER}@$(curl -s ifconfig.me)"
echo "========================================"
❓ FAQ
Q: Will
ufw enablelock me out? A: It could, if you haven't allowed SSH first. Always runsudo ufw allow 22/tcpbeforesudo ufw enable. Keep an existing SSH session open as a safety net, and test in a new terminal before closing the old one.
Q: Are SSH keys or passwords more secure? A: Key authentication is far more secure than passwords. Keys use 4096-bit RSA or Ed25519 asymmetric encryption that's theoretically impossible to brute-force. Passwords can be guessed, dictionary-attacked, or phished.
Q: Are fail2ban bans permanent? A: No, they're temporary by default (usually 1 hour). You can customize duration via
bantime. Permanent bans aren't recommended — attackers' IPs change frequently, and the ban list would grow indefinitely.
Q: How to connect after changing the SSH port? A:
ssh -p 2222 user@host. Or configurePort 2222in~/.ssh/config.
Q: Why keep password login enabled for a while? A: As a fallback. If your key is lost or corrupted, password login is your only way in. Only disable it after confirming key authentication works reliably. Always keep a backup access method (like VNC console from your cloud provider).
📖 Summary
- ufw:
allow 22/tcp→default deny incoming→enable - SSH keys:
ssh-keygen -t ed25519→ssh-copy-id user@host→ disable password login - SSH hardening: change port, disable root, disable password, restrict users
- fail2ban:
maxretry=3,bantime=86400,findtime=600 unattended-upgradesfor automatic security patches
📝 Exercises
- Basic (⭐): Check current ufw status, open or close a port, then generate an Ed25519 SSH key pair locally
- Intermediate (⭐⭐): Configure SSH key login to a test server (or localhost testing), then install fail2ban and check the SSH jail status
- Challenge (⭐⭐⭐): Write a security hardening script that includes all the steps above (ufw config, SSH keys, SSH hardening, fail2ban), requiring each step to verify success before proceeding, and rolling back on failure