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

1. What You'll Learn


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:

BASH
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:

BASH
# 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

BASH
# 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
⚠️ Warning: ufw/iptables rules match in order — the first matching rule takes effect. If you write 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.

⚠️ Warning: When using 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:

⚠️ Warning: 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.

BASH
# 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

BASH
# 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

⚠️ Warning: Before changing the SSH port (e.g., to 2222), ensure you have an alternative access method. If your cloud provider offers a VNC console, use it as a backup. After changing the port, remember to allow the new port in ufw (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:

TEXT
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:

BASH
sudo sshd -t                   # Test config syntax
sudo systemctl restart sshd    # Restart SSH service

(4) fail2ban

💡 Tip: fail2ban's 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.

BASH
# 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

BASH
# 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:

TEXT
Rule 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

BASH
# 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:

TEXT
Generating 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

BASH
# 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

BASH
# 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:

TEXT
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
1 unbanned

[sshd]
enabled = true
maxretry = 3
bantime = 86400
findtime = 600

(9) ▶ Example: Automatic Security Updates

BASH
# 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:

TEXT
Reading 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

BASH
#!/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 enable lock me out? A: It could, if you haven't allowed SSH first. Always run sudo ufw allow 22/tcp before sudo 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 configure Port 2222 in ~/.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


📝 Exercises

  1. Basic (⭐): Check current ufw status, open or close a port, then generate an Ed25519 SSH key pair locally
  2. Intermediate (⭐⭐): Configure SSH key login to a test server (or localhost testing), then install fail2ban and check the SSH jail status
  3. 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
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏