The Complete Firewall Guide: From Zero to Locked Down

8 min read
Intermediate Firewall UFW iptables Security Guide

Prerequisites

  • Root or sudo access to a Linux server
  • Basic understanding of TCP/IP and ports

Quick Answer: For quick setup: sudo ufw allow 22 && sudo ufw allow 80,443/tcp && sudo ufw enable. For advanced control: use iptables. This guide covers both from scratch, plus rate limiting, geo-blocking, NAT, and production-ready templates.

Need a VPS? Vultr (free credit), DigitalOcean ($200 free credit), or RackNerd (cheap annual deals).

Why You Need a Firewall

A server with no firewall is an open door. Within minutes of going online, bots will:

  • Try to brute-force SSH (port 22)
  • Scan for open databases (3306, 5432, 27017)
  • Probe for vulnerable services
  • Attempt to use your server as a relay

A firewall blocks all incoming traffic except the ports you explicitly allow.

# See how many failed SSH attempts you've had
grep "Failed password" /var/log/auth.log | wc -l

Part 1: Understanding Firewall Basics

How Traffic Flows

Internet → Your Server's Network Interface → Firewall Rules → Application

Three chains:
INPUT    → Traffic coming TO your server
OUTPUT   → Traffic leaving FROM your server  
FORWARD  → Traffic passing THROUGH your server (routing/NAT)

Actions

Action What Happens
ACCEPT Allow the packet through
DROP Silently discard (sender gets no response)
REJECT Discard and send error back to sender
LOG Log the packet then continue to next rule

Rule Order Matters

Rules are checked top to bottom. First match wins. If no rule matches, the default policy applies (usually DROP or ACCEPT).

Rule 1: Allow SSH (port 22) ← Matches SSH traffic
Rule 2: Allow HTTP (port 80) ← Matches HTTP traffic
Rule 3: DROP everything else ← Catches everything else

Part 2: UFW (Uncomplicated Firewall)

UFW is the easiest way to manage a firewall on Ubuntu/Debian. It's a frontend for iptables.

Install and Enable

sudo apt install ufw -y

# IMPORTANT: Allow SSH before enabling!
sudo ufw allow 22/tcp

# Enable
sudo ufw enable

# Check status
sudo ufw status verbose

Essential Commands

# Allow
sudo ufw allow 80/tcp              # HTTP
sudo ufw allow 443/tcp             # HTTPS
sudo ufw allow 80,443/tcp          # Both
sudo ufw allow 8000:9000/tcp       # Port range
sudo ufw allow from 10.0.0.5       # Specific IP
sudo ufw allow from 10.0.0.0/24 to any port 22   # Subnet to SSH

# Deny
sudo ufw deny 3306                 # Block MySQL
sudo ufw deny from 1.2.3.4        # Block IP

# Delete rules
sudo ufw status numbered
sudo ufw delete 3                  # By number
sudo ufw delete allow 80           # By rule

# Rate limit SSH (6 attempts per 30 seconds)
sudo ufw limit 22/tcp

# Reset everything
sudo ufw reset

Default Policies

sudo ufw default deny incoming     # Block all incoming
sudo ufw default allow outgoing    # Allow all outgoing

Server Templates

Web Server:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Proxy Server (3X-UI):

sudo ufw allow 22/tcp
sudo ufw allow 443/tcp        # VLESS-WS
sudo ufw allow 8443/tcp       # Reality
sudo ufw allow 2053/tcp       # Panel
sudo ufw allow 2082,8880/tcp  # CDN HTTP
sudo ufw allow 2083,2087/tcp  # CDN HTTPS
sudo ufw enable

VPN Server (WireGuard):

sudo ufw allow 22/tcp
sudo ufw allow 51820/udp
sudo ufw enable

Full reference: UFW Cheat Sheet


Part 3: iptables (Power Users)

iptables gives you complete control over every packet. More complex than UFW but infinitely more flexible.

View Rules

sudo iptables -L -n -v                    # All rules
sudo iptables -L INPUT -n --line-numbers  # Input chain with line numbers
sudo iptables-save                        # Dump rules in save format

Basic Server Protection

# Flush existing rules
sudo iptables -F

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

# Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow loopback
sudo iptables -A INPUT -i lo -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 ping
sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT

Rate Limiting

# SSH brute-force protection (4 attempts per minute)
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Limit connections per IP (25 simultaneous)
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 25 -j DROP

Block and Allow

# Block an IP
sudo iptables -A INPUT -s 1.2.3.4 -j DROP

# Block a subnet
sudo iptables -A INPUT -s 10.0.0.0/8 -j DROP

# Allow only from specific IP to a port
sudo iptables -A INPUT -p tcp -s 10.0.0.5 --dport 3306 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3306 -j DROP

Save Rules

# Save
sudo iptables-save > /etc/iptables.rules

# Make persistent (Debian/Ubuntu)
sudo apt install iptables-persistent -y
sudo netfilter-persistent save

WARNING: Running iptables -F when default policy is DROP will lock you out of SSH. Always reset default policy to ACCEPT first: iptables -P INPUT ACCEPT

Full reference: iptables Cheat Sheet


Part 4: Geo-Blocking

Block traffic from entire countries using IP range databases.

Using ipset (Efficient)

sudo apt install ipset -y

# Create a set for blocked countries
sudo ipset create blocked-countries hash:net

# Download and add country IP ranges
wget -q https://www.ipdeny.com/ipblocks/data/countries/cn.zone
while read cidr; do sudo ipset add blocked-countries "$cidr"; done < cn.zone

# Block with iptables
sudo iptables -A INPUT -m set --match-set blocked-countries src -j DROP

Block Multiple Countries

for country in cn ru kp; do
  wget -q "https://www.ipdeny.com/ipblocks/data/countries/${country}.zone"
  while read cidr; do sudo ipset add blocked-countries "$cidr" 2>/dev/null; done < "${country}.zone"
done

Part 5: NAT and Port Forwarding

Enable IP Forwarding

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Masquerade (NAT for VPN)

# All traffic from VPN subnet goes out through your server's IP
sudo iptables -t nat -A POSTROUTING -s 10.66.66.0/24 -o eth0 -j MASQUERADE

Port Forwarding

# Forward external port 8080 to internal server 192.168.1.100:80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT

Full guide: Port Forwarding Guide


Part 6: Fail2ban (Automatic Banning)

Fail2ban monitors logs and automatically bans IPs that show malicious behavior.

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

It works immediately for SSH with default settings — bans IPs for 10 minutes after 5 failed login attempts.

Custom Jail

# /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 3
bantime = 3600
findtime = 600

Manage Bans

sudo fail2ban-client status sshd          # See banned IPs
sudo fail2ban-client set sshd unbanip IP  # Unban

Full guide: Fail2ban Setup


Part 7: Logging

Log Dropped Packets

# Add before the final DROP rule
sudo iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROP: " --log-level 4
sudo iptables -A INPUT -j DROP

View Logs

# iptables
grep "IPTABLES-DROP" /var/log/kern.log | tail -20

# UFW
grep "UFW" /var/log/syslog | tail -20

# Fail2ban
sudo tail -f /var/log/fail2ban.log

Part 8: Production Template

Complete firewall for a production server running a web app + proxy:

#!/bin/bash
# production-firewall.sh

# Safety: reset to ACCEPT before flushing (prevents lockout on re-run)
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT

# Reset
iptables -F
iptables -X
iptables -t nat -F

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

# Loopback
iptables -A INPUT -i lo -j ACCEPT

# Established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# SSH (rate limited)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Connection limits per IP (must be BEFORE accept rules)
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j DROP

# Web
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Proxy ports (3X-UI)
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT        # Reality
iptables -A INPUT -p tcp --dport 2082 -j ACCEPT        # CDN HTTP
iptables -A INPUT -p tcp --dport 2083 -j ACCEPT        # CDN HTTPS
iptables -A INPUT -p tcp --dport 8880 -j ACCEPT        # CDN HTTP alt

# WireGuard
iptables -A INPUT -p udp --dport 51820 -j ACCEPT

# Ping (limited)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT

# Log dropped
iptables -A INPUT -j LOG --log-prefix "FW-DROP: " --log-level 4

# Save
iptables-save > /etc/iptables.rules
echo "Firewall configured"
chmod +x production-firewall.sh
sudo ./production-firewall.sh

Part 9: UFW vs iptables vs nftables

UFW iptables nftables
Difficulty Easy Medium Medium
Best for Quick server setup Complex rules, NAT New deployments
Rate limiting Basic (limit) Advanced (recent, connlimit) Advanced
Geo-blocking Not built-in Via ipset Native sets
Status Frontend for iptables Legacy (still works everywhere) Modern replacement
Ubuntu default Yes Underlying Replacing iptables

Recommendation:

  • 80% of servers: UFW is enough
  • Complex setups: iptables (proxy servers, NAT, VPN)
  • New infrastructure: nftables (if you're starting fresh)

Security Checklist

echo "=== Firewall Security Check ==="
echo "UFW status: $(sudo ufw status | head -1)"
echo "iptables rules: $(sudo iptables -L -n | grep -c "ACCEPT\|DROP")"
echo "Fail2ban: $(systemctl is-active fail2ban)"
echo "SSH port: $(grep "^Port" /etc/ssh/sshd_config || echo "22 (default)")"
echo "Open ports:"
sudo ss -tlnp | grep -v "127.0.0" | awk '{print $4}' | sort -u

Related Guides