Firewall Hardening: UFW, iptables, and firewalld Guide
Deep dive into Linux firewalls for homelabs. Configure UFW, iptables, and firewalld for maximum security.
A properly configured firewall is your homelab’s first line of defense against unauthorized access. This comprehensive guide covers three major Linux firewall solutions: UFW (Uncomplicated Firewall), iptables, and firewalld. You’ll learn how to implement defense-in-depth strategies, create advanced rules, and harden your network security.
Why Firewall Hardening Matters
Your homelab is constantly probed by automated scanners, bots, and potential attackers. Without proper firewall configuration, exposed services become easy targets. A hardened firewall provides:
- Network segmentation: Isolate services and limit lateral movement
- Attack surface reduction: Only expose necessary ports
- Traffic filtering: Block malicious traffic patterns
- Logging and monitoring: Track connection attempts
- Defense in depth: Multiple security layers working together
Understanding Linux Firewall Architecture
All three tools ultimately configure the Linux kernel’s netfilter framework, but they differ in approach:
- iptables: Low-level, maximum control, steep learning curve
- UFW: High-level, Ubuntu/Debian friendly, simple syntax
- firewalld: Zone-based, dynamic updates, RHEL/Fedora default
Choose based on your distribution and complexity needs. Many homelabbers start with UFW and graduate to iptables for advanced scenarios.
UFW: Uncomplicated Firewall
Installation and Initial Setup
Ubuntu/Debian:
sudo apt update
sudo apt install ufw Initial Configuration:
# Check status (should be inactive initially)
sudo ufw status verbose
# Set default policies (deny incoming, allow outgoing)
sudo ufw default deny incoming
sudo ufw default allow outgoing
# CRITICAL: Allow SSH before enabling to prevent lockout
sudo ufw allow ssh
# Or specific port: sudo ufw allow 22/tcp
# Enable firewall
sudo ufw enable
# Verify status
sudo ufw status numbered Essential UFW Rules
Allow specific services:
# Web services
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Custom application ports
sudo ufw allow 8080/tcp comment 'Home Assistant'
sudo ufw allow 9000/tcp comment 'Portainer'
sudo ufw allow 32400/tcp comment 'Plex'
# Allow from specific IP
sudo ufw allow from 192.168.1.100 to any port 22
sudo ufw allow from 192.168.1.0/24 to any port 445 comment 'SMB local network'
# Allow port range
sudo ufw allow 51000:51999/tcp comment 'Transmission' Advanced UFW Rules:
# Limit SSH to prevent brute force (max 6 connections per 30 seconds)
sudo ufw limit ssh
# Allow from specific interface
sudo ufw allow in on eth1 to any port 3306 comment 'MySQL on internal network'
# Deny specific IP
sudo ufw deny from 203.0.113.50
sudo ufw reject from 198.51.100.0/24
# Allow specific application profile
sudo ufw app list
sudo ufw allow 'Nginx Full'
# Delete rules by number
sudo ufw status numbered
sudo ufw delete 5
# Delete by specification
sudo ufw delete allow 80/tcp UFW Application Profiles
Create custom application profiles in /etc/ufw/applications.d/:
Example: /etc/ufw/applications.d/homelab
[Jellyfin]
title=Jellyfin Media Server
description=Media streaming server
ports=8096/tcp
[Home Assistant]
title=Home Assistant
description=Home automation platform
ports=8123/tcp
[Nextcloud]
title=Nextcloud
description=Self-hosted cloud storage
ports=443/tcp
[Minecraft]
title=Minecraft Server
description=Minecraft game server
ports=25565/tcp|25565/udp Use application profiles:
# Reload application profiles
sudo ufw app update Jellyfin
# Allow application
sudo ufw allow Jellyfin Advanced UFW Configuration
Edit /etc/ufw/before.rules for custom iptables rules:
# Allow all traffic on loopback
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-output -o lo -j ACCEPT
# Allow established connections
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Drop invalid packets
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP
# Allow ICMP (ping) with rate limiting
-A ufw-before-input -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
# Log dropped packets
-A ufw-before-input -j LOG --log-prefix "[UFW BLOCK] " --log-level 4 Enable logging:
# Enable logging
sudo ufw logging on
# Set log level (low, medium, high, full)
sudo ufw logging medium
# View logs
sudo tail -f /var/log/ufw.log
sudo journalctl -u ufw -f iptables: Maximum Control
Understanding iptables Tables and Chains
iptables organizes rules into tables, each containing chains:
Tables:
- filter: Default table for packet filtering (INPUT, OUTPUT, FORWARD)
- nat: Network Address Translation (PREROUTING, POSTROUTING, OUTPUT)
- mangle: Packet alteration (all chains)
- raw: Connection tracking exemption (PREROUTING, OUTPUT)
Chains:
- INPUT: Incoming packets destined for local system
- OUTPUT: Locally generated packets going out
- FORWARD: Packets routed through the system
- PREROUTING: Packets before routing decision
- POSTROUTING: Packets after routing decision
Basic iptables Setup
Check current rules:
# List all rules
sudo iptables -L -v -n
# List with line numbers
sudo iptables -L --line-numbers
# List specific table
sudo iptables -t nat -L -v -n Flush existing rules (CAREFUL - may lock you out):
# Flush all rules
sudo iptables -F
# Flush specific chain
sudo iptables -F INPUT
# Delete all chains
sudo iptables -X Set default policies:
# Accept loopback traffic
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
# Allow established connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Set default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT Essential iptables Rules
Allow SSH with rate limiting:
# SSH with connection rate limiting
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT Allow web services:
# HTTP and HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow from local network only
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 8080 -j ACCEPT Port forwarding (NAT):
# Forward external port 8080 to internal server port 80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# Enable masquerading for outbound traffic
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE Advanced filtering:
# Drop invalid packets
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Block specific IP
sudo iptables -A INPUT -s 203.0.113.50 -j DROP
# Block IP range
sudo iptables -A INPUT -s 198.51.100.0/24 -j DROP
# Allow ping with rate limit
sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
# Log dropped packets
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 7
sudo iptables -A INPUT -j DROP Port Knocking with iptables
Port knocking adds a secret knock sequence before opening ports:
# Create port knocking chains
sudo iptables -N KNOCKING
sudo iptables -N GATE1
sudo iptables -N GATE2
sudo iptables -N GATE3
# Knock sequence: 7000, 8000, 9000
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j KNOCKING
sudo iptables -A KNOCKING -m recent --name SSH --remove
sudo iptables -A KNOCKING -m recent --name GATE3 --rcheck --seconds 5 -j ACCEPT
sudo iptables -A KNOCKING -m recent --name GATE3 --remove
sudo iptables -A KNOCKING -j DROP
sudo iptables -A INPUT -p tcp --dport 7000 -m conntrack --ctstate NEW -j GATE1
sudo iptables -A GATE1 -m recent --name GATE1 --set
sudo iptables -A GATE1 -j DROP
sudo iptables -A INPUT -p tcp --dport 8000 -m conntrack --ctstate NEW -m recent --name GATE1 --rcheck --seconds 5 -j GATE2
sudo iptables -A GATE2 -m recent --name GATE1 --remove
sudo iptables -A GATE2 -m recent --name GATE2 --set
sudo iptables -A GATE2 -j DROP
sudo iptables -A INPUT -p tcp --dport 9000 -m conntrack --ctstate NEW -m recent --name GATE2 --rcheck --seconds 5 -j GATE3
sudo iptables -A GATE3 -m recent --name GATE2 --remove
sudo iptables -A GATE3 -m recent --name GATE3 --set
sudo iptables -A GATE3 -j DROP Use port knocking:
# Client side - knock sequence
nc -z YOUR-SERVER-IP 7000
nc -z YOUR-SERVER-IP 8000
nc -z YOUR-SERVER-IP 9000
ssh user@YOUR-SERVER-IP Persisting iptables Rules
Ubuntu/Debian:
# Install persistence package
sudo apt install iptables-persistent
# Save current rules
sudo netfilter-persistent save
# Or manually
sudo iptables-save | sudo tee /etc/iptables/rules.v4
sudo ip6tables-save | sudo tee /etc/iptables/rules.v6 RHEL/CentOS/Fedora:
# Save rules
sudo service iptables save
# Or
sudo iptables-save > /etc/sysconfig/iptables Restore on boot with systemd service:
Create /etc/systemd/system/iptables-restore.service:
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/sbin/ip6tables-restore /etc/iptables/rules.v6
[Install]
WantedBy=multi-user.target Enable service:
sudo systemctl enable iptables-restore
sudo systemctl start iptables-restore firewalld: Zone-Based Firewall
Installation and Setup
RHEL/CentOS/Fedora:
sudo dnf install firewalld
sudo systemctl enable firewalld
sudo systemctl start firewalld Ubuntu/Debian:
sudo apt install firewalld
sudo systemctl enable firewalld
sudo systemctl start firewalld Check status:
sudo firewall-cmd --state
sudo firewall-cmd --list-all Understanding Zones
Zones define trust levels for network connections:
- drop: Drop all incoming, allow outgoing (most restrictive)
- block: Reject all incoming with icmp-host-prohibited
- public: Public networks, selective incoming allowed
- external: External networks with masquerading
- dmz: DMZ - limited access to internal network
- work: Work networks, more services allowed
- home: Home networks, most services allowed
- internal: Internal networks, trust most traffic
- trusted: All traffic accepted (least restrictive)
View zones:
# List all zones
sudo firewall-cmd --get-zones
# Get default zone
sudo firewall-cmd --get-default-zone
# List active zones
sudo firewall-cmd --get-active-zones
# View zone configuration
sudo firewall-cmd --zone=public --list-all Basic firewalld Configuration
Set default zone:
sudo firewall-cmd --set-default-zone=public Add services to zones:
# Add service temporarily
sudo firewall-cmd --zone=public --add-service=http
# Add service permanently
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
# Reload to apply permanent rules
sudo firewall-cmd --reload
# Add custom port
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
sudo firewall-cmd --zone=public --add-port=51000-51999/tcp --permanent
# Remove service
sudo firewall-cmd --zone=public --remove-service=ssh --permanent Assign interfaces to zones:
# Assign interface to zone
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
# List interfaces in zone
sudo firewall-cmd --zone=internal --list-interfaces Advanced firewalld Rules
Rich rules for complex scenarios:
# Allow SSH from specific IP
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" service name="ssh" accept' --permanent
# Limit SSH connections (10 per minute)
sudo firewall-cmd --zone=public --add-rich-rule='rule service name="ssh" limit value="10/m" accept' --permanent
# Allow port from subnet
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="445" protocol="tcp" accept' --permanent
# Block specific IP
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="203.0.113.50" reject' --permanent
# Log dropped packets
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" log prefix="FIREWALLD_DROP: " level="info" limit value="5/m" drop' --permanent
# Allow port with logging
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" port port="3000" protocol="tcp" log prefix="GRAFANA_ACCESS: " level="info" accept' --permanent Port forwarding:
# Enable masquerading
sudo firewall-cmd --zone=public --add-masquerade --permanent
# Forward port 8080 to 80
sudo firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=80 --permanent
# Forward to different host
sudo firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=192.168.1.100 --permanent Direct rules (iptables syntax):
# Add direct iptables rule
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --permanent
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP --permanent Custom Services and Zones
Create custom service:
File: /etc/firewalld/services/homeassistant.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>Home Assistant</short>
<description>Home automation platform</description>
<port protocol="tcp" port="8123"/>
</service> Load and use custom service:
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --add-service=homeassistant --permanent Create custom zone:
File: /etc/firewalld/zones/homelab.xml
<?xml version="1.0" encoding="utf-8"?>
<zone target="default">
<short>Homelab</short>
<description>Trusted homelab network</description>
<service name="ssh"/>
<service name="http"/>
<service name="https"/>
<service name="samba"/>
<port protocol="tcp" port="8080"/>
<port protocol="tcp" port="9000"/>
<source address="192.168.1.0/24"/>
</zone> Load custom zone:
sudo firewall-cmd --reload
sudo firewall-cmd --get-zones
sudo firewall-cmd --zone=homelab --change-interface=eth1 --permanent Docker and Firewall Integration
Docker manipulates iptables directly, which can bypass firewall rules. Here’s how to maintain control:
UFW and Docker
Docker bypasses UFW by default. Fix this:
Edit /etc/ufw/after.rules:
# Add at the end
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
-A DOCKER-USER -j RETURN
COMMIT Restart UFW:
sudo ufw reload Now UFW rules apply to Docker:
# Allow Docker container port from local network only
sudo ufw allow from 192.168.1.0/24 to any port 8080 firewalld and Docker
Add Docker to trusted zone:
sudo firewall-cmd --zone=trusted --add-interface=docker0 --permanent
sudo firewall-cmd --reload Control Docker published ports:
# Use Docker's published port with firewalld rich rules
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="8080" protocol="tcp" accept' --permanent Network Segmentation Strategies
VLAN-Based Segmentation
Separate networks by trust level:
# Public-facing services (DMZ) - VLAN 10
sudo firewall-cmd --zone=dmz --change-interface=eth0.10 --permanent
sudo firewall-cmd --zone=dmz --add-service=http --permanent
sudo firewall-cmd --zone=dmz --add-service=https --permanent
# Internal services - VLAN 20
sudo firewall-cmd --zone=internal --change-interface=eth0.20 --permanent
sudo firewall-cmd --zone=internal --add-service=samba --permanent
sudo firewall-cmd --zone=internal --add-service=nfs --permanent
# IoT devices (restricted) - VLAN 30
sudo firewall-cmd --zone=block --change-interface=eth0.30 --permanent
# Only allow specific outbound, deny all inbound Interface-Based Segmentation
# External interface (WAN)
sudo firewall-cmd --zone=external --change-interface=eth0 --permanent
sudo firewall-cmd --zone=external --add-masquerade --permanent
# Internal interface (LAN)
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
# Management interface
sudo firewall-cmd --zone=trusted --change-interface=eth2 --permanent Security Best Practices
Principle of Least Privilege
Only open what you absolutely need:
# Start restrictive
sudo ufw default deny incoming
# Add services incrementally
sudo ufw allow from 192.168.1.0/24 to any port 445 comment 'SMB - local only'
sudo ufw allow from 192.168.1.0/24 to any port 2049 comment 'NFS - local only'
# Never expose everything
# BAD: sudo ufw allow from 192.168.1.0/24
# GOOD: Specify exact ports needed Regular Audit and Review
Monthly firewall audit script:
File: /usr/local/bin/firewall-audit.sh
#!/bin/bash
echo "=== Firewall Audit Report ==="
echo "Date: $(date)"
echo ""
echo "=== UFW Status ==="
sudo ufw status numbered
echo ""
echo "=== Active Connections ==="
sudo ss -tulpn | grep LISTEN
echo ""
echo "=== Recent Blocked Attempts ==="
sudo tail -100 /var/log/ufw.log | grep BLOCK | tail -20
echo ""
echo "=== Open Ports ==="
sudo nmap -sT localhost
echo ""
echo "=== End Report ===" Make executable and run:
sudo chmod +x /usr/local/bin/firewall-audit.sh
sudo /usr/local/bin/firewall-audit.sh Defense in Depth
Layer multiple security controls:
- Firewall: Block unauthorized network access
- Fail2ban: Ban IPs after failed attempts
- Application-level: Configure service security
- Monitoring: Alert on suspicious activity
- Updates: Keep systems patched
Logging and Monitoring
Enable comprehensive logging:
UFW:
sudo ufw logging high iptables:
# Log all dropped packets
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables_DROP: " --log-level 7
sudo iptables -A INPUT -j DROP firewalld:
# Enable logging in default zone
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" log prefix="FIREWALLD: " level="info" limit value="5/m"' --permanent Monitor logs:
# UFW logs
sudo tail -f /var/log/ufw.log
# iptables logs (syslog)
sudo journalctl -k | grep iptables
# firewalld logs
sudo journalctl -u firewalld -f Troubleshooting
Connection Issues After Enabling Firewall
Symptom: Can’t connect to services after enabling firewall.
Solution:
# Check if service is listening
sudo ss -tulpn | grep :PORT
# Check firewall rules
sudo ufw status numbered # UFW
sudo iptables -L -v -n # iptables
sudo firewall-cmd --list-all # firewalld
# Test from localhost
curl http://localhost:PORT
# Check logs for blocks
sudo tail -f /var/log/ufw.log | grep BLOCK SSH Lockout
Prevention:
# ALWAYS allow SSH before enabling firewall
sudo ufw allow ssh
sudo ufw enable
# Or use console access to fix Recovery (requires console/physical access):
# Disable firewall temporarily
sudo ufw disable
# Or reset rules
sudo iptables -F
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT Docker Containers Not Accessible
Symptom: Published Docker ports blocked by firewall.
Solution for UFW:
# Edit /etc/ufw/after.rules as shown in Docker section
# Then reload
sudo ufw reload Solution for firewalld:
# Add docker0 to trusted zone
sudo firewall-cmd --zone=trusted --add-interface=docker0 --permanent
sudo firewall-cmd --reload Rules Not Persisting After Reboot
UFW:
# Check if UFW is enabled
sudo systemctl status ufw
sudo systemctl enable ufw iptables:
# Install persistence
sudo apt install iptables-persistent
# Save rules
sudo netfilter-persistent save firewalld:
# Make sure using --permanent flag
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --reload Checking Rule Order
Rules are processed top-down. Order matters:
# UFW - numbered list
sudo ufw status numbered
# iptables - line numbers
sudo iptables -L INPUT --line-numbers
# Insert rule at specific position
sudo iptables -I INPUT 3 -p tcp --dport 22 -j ACCEPT Real-World Use Cases
Case 1: Home Media Server
Requirements: Plex accessible remotely, SMB only on LAN.
# UFW approach
sudo ufw allow 32400/tcp comment 'Plex remote access'
sudo ufw allow from 192.168.1.0/24 to any port 445 comment 'SMB LAN only'
sudo ufw allow from 192.168.1.0/24 to any port 139 comment 'NetBIOS LAN only' Case 2: Multi-Tenant Homelab
Requirements: Separate zones for production, development, IoT.
# firewalld approach
# Production zone - eth0.10
sudo firewall-cmd --new-zone=production --permanent
sudo firewall-cmd --zone=production --add-source=10.0.10.0/24 --permanent
sudo firewall-cmd --zone=production --add-service=http --permanent
sudo firewall-cmd --zone=production --add-service=https --permanent
# Development zone - eth0.20
sudo firewall-cmd --new-zone=development --permanent
sudo firewall-cmd --zone=development --add-source=10.0.20.0/24 --permanent
sudo firewall-cmd --zone=development --add-port=3000-4000/tcp --permanent
# IoT zone - eth0.30 (restrictive)
sudo firewall-cmd --new-zone=iot --permanent
sudo firewall-cmd --zone=iot --set-target=DROP --permanent
sudo firewall-cmd --zone=iot --add-source=10.0.30.0/24 --permanent
# Only allow specific services out, block all incoming
sudo firewall-cmd --reload Case 3: Reverse Proxy with Backend Protection
Requirements: Nginx reverse proxy exposed, backends isolated.
# iptables approach
# Allow HTTP/HTTPS on reverse proxy
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow backends only from reverse proxy IP
sudo iptables -A INPUT -p tcp -s 192.168.1.50 --dport 8080 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 192.168.1.50 --dport 8081 -j ACCEPT
# Block backends from internet
sudo iptables -A INPUT -p tcp --dport 8080 -j DROP
sudo iptables -A INPUT -p tcp --dport 8081 -j DROP Automation and Scripts
Automated Firewall Setup Script
File: /usr/local/bin/setup-firewall.sh
#!/bin/bash
set -e
echo "Setting up UFW firewall for homelab..."
# Reset UFW
sudo ufw --force reset
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Essential services
sudo ufw allow ssh comment 'SSH access'
sudo ufw limit ssh
# Web services
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Homelab services (LAN only)
sudo ufw allow from 192.168.1.0/24 to any port 445 comment 'Samba'
sudo ufw allow from 192.168.1.0/24 to any port 2049 comment 'NFS'
sudo ufw allow from 192.168.1.0/24 to any port 9000 comment 'Portainer'
# Media services
sudo ufw allow 32400/tcp comment 'Plex'
sudo ufw allow 8096/tcp comment 'Jellyfin'
# Enable logging
sudo ufw logging medium
# Enable firewall
sudo ufw --force enable
echo "Firewall setup complete!"
sudo ufw status verbose Backup Firewall Rules
#!/bin/bash
BACKUP_DIR="/backup/firewall"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup UFW
if command -v ufw &> /dev/null; then
sudo cp /etc/ufw/*.rules "$BACKUP_DIR/ufw_$DATE/"
fi
# Backup iptables
if command -v iptables &> /dev/null; then
sudo iptables-save > "$BACKUP_DIR/iptables_$DATE.rules"
sudo ip6tables-save > "$BACKUP_DIR/ip6tables_$DATE.rules"
fi
# Backup firewalld
if command -v firewall-cmd &> /dev/null; then
sudo tar -czf "$BACKUP_DIR/firewalld_$DATE.tar.gz" /etc/firewalld/
fi
echo "Firewall rules backed up to $BACKUP_DIR" Conclusion
A properly hardened firewall is essential for homelab security. Start with UFW for simplicity, graduate to iptables for advanced scenarios, or use firewalld for zone-based management. Key takeaways:
- Default deny: Block everything, allow only what’s needed
- Layer security: Combine firewall with fail2ban, monitoring, and good application configuration
- Regular audits: Review rules monthly, remove unused ports
- Test thoroughly: Verify rules work as expected before deploying
- Document everything: Comment your rules, maintain a network diagram
Your homelab’s security is only as strong as your weakest link. A hardened firewall provides a strong foundation for building a secure, resilient infrastructure.