What's Actually Happening

Firewall rules are created but not taking effect. Traffic is still blocked or allowed despite rule changes, or rules disappear after reboot.

The Error You'll See

Rule not working:

```bash $ iptables -A INPUT -p tcp --dport 80 -j ACCEPT $ iptables -L INPUT

Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- anywhere anywhere tcp dpt:80

# But traffic still blocked: $ nc -zv server 80

nc: connect to server port 80 (tcp) failed: Connection refused ```

Rule disappears after reboot:

```bash $ iptables -L

Chain INPUT (policy ACCEPT) # Rules missing after restart ```

FirewallD rule not active:

```bash $ firewall-cmd --add-port=80/tcp

success $ firewall-cmd --list-ports

# Port not listed ```

UFW rule inactive:

```bash $ ufw allow 80

Rule added $ ufw status

Status: inactive ```

Why This Happens

  1. 1.Rule order - Earlier rule blocking before new rule
  2. 2.Rule not persistent - Rules lost on reboot
  3. 3.Wrong chain - Rule in wrong chain for traffic
  4. 4.Interface mismatch - Rule bound to wrong interface
  5. 5.Firewall disabled - Firewall service not running
  6. 6.Syntax error - Invalid rule specification

Step 1: Check Current Firewall Status

```bash # iptables: iptables -L -n -v iptables -L INPUT -n -v iptables -L OUTPUT -n -v

# Show rule numbers: iptables -L INPUT --line-numbers -n

# Check NAT: iptables -t nat -L -n -v

# FirewallD: firewall-cmd --state firewall-cmd --list-all firewall-cmd --list-all-zones

# UFW: ufw status verbose ufw status numbered

# Check service: systemctl status iptables # CentOS 6 systemctl status firewalld # CentOS 7/8 systemctl status ufw # Ubuntu

# Check if enabled: systemctl is-enabled firewalld ufw status | head -1 ```

Step 2: Check Rule Order and Priority

```bash # iptables rules match in order:

# List with numbers: iptables -L INPUT --line-numbers -n

# Example output: # Chain INPUT (policy DROP) # num target prot opt source destination # 1 DROP all -- 0.0.0.0/0 0.0.0.0/0 # 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80

# Rule 1 blocks all traffic, rule 2 never reached!

# Fix: Insert rule at correct position: iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT

# Now check: iptables -L INPUT --line-numbers -n # num target prot opt source destination # 1 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 # 2 DROP all -- 0.0.0.0/0 0.0.0.0/0

# Delete blocking rule or adjust order: iptables -D INPUT 2

# For FirewallD, zones have priority: firewall-cmd --get-active-zones firewall-cmd --zone=public --list-all

# Check zone binding: firewall-cmd --get-zone-of-interface=eth0 ```

Step 3: Check Rule Syntax and Target

```bash # Verify rule syntax: iptables -S INPUT

# Output shows rules: # -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT

# Check specific rule: iptables -C INPUT -p tcp --dport 80 -j ACCEPT // Returns 0 if exists, 1 if not

# Test rule creation: iptables -A INPUT -p tcp --dport 80 -j ACCEPT echo $? # Should be 0

# Common syntax errors: # Wrong protocol: -p udp for tcp port # Missing port: --dport without -p tcp # Wrong jump: -j to nonexistent chain

# Check jump target exists: iptables -N mychain # Create custom chain iptables -A INPUT -j mychain

# Check target chain: iptables -L mychain -n

# For FirewallD: firewall-cmd --add-port=80/tcp --permanent firewall-cmd --reload

# Check syntax: firewall-cmd --check-config ```

Step 4: Check Interface Binding

```bash # Rules can be interface-specific:

# List interfaces: ip addr show | grep -E "^[0-9:"

# Check interface in rule: iptables -L INPUT -n -v | grep eth0

# Add interface-specific rule: iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT

# Check traffic comes from correct interface: tcpdump -i eth0 port 80

# For multiple interfaces: iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT iptables -A INPUT -i eth1 -p tcp --dport 80 -j ACCEPT

# Or all interfaces (default): iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# FirewallD interface binding: firewall-cmd --zone=public --change-interface=eth0

# Check: firewall-cmd --get-zone-of-interface=eth0

# UFW: ufw allow in on eth0 to any port 80 ```

Step 5: Check Firewall Service Status

```bash # iptables service (CentOS 6): systemctl status iptables systemctl start iptables systemctl enable iptables

# FirewallD (CentOS 7/8): systemctl status firewalld systemctl start firewalld systemctl enable firewalld

# Check if running: firewall-cmd --state // running

# If not running: systemctl start firewalld

# UFW (Ubuntu): ufw status systemctl status ufw

# Enable UFW: ufw enable ufw status // Status: active

# Check iptables module loaded: lsmod | grep ip_tables lsmod | grep nf_tables

# Load module: modprobe ip_tables ```

Step 6: Make Rules Persistent

```bash # iptables persistence:

# CentOS/RHEL: iptables-save > /etc/sysconfig/iptables systemctl restart iptables

# Or: service iptables save

# Ubuntu/Debian: apt install iptables-persistent iptables-save > /etc/iptables/rules.v4 ip6tables-save > /etc/iptables/rules.v6

# Or use netfilter-persistent: netfilter-persistent save

# FirewallD permanent rules: firewall-cmd --add-port=80/tcp --permanent firewall-cmd --reload

# Check permanent config: firewall-cmd --list-all --permanent

# UFW (persistent by default): ufw allow 80 # Automatically saved

# Edit UFW config: vim /etc/ufw/user.rules vim /etc/ufw/user6.rules

# Reload UFW: ufw reload ```

Step 7: Check Default Policy

```bash # Default policy affects unmatched traffic:

# Check default policy: iptables -L | grep policy

# Output: # Chain INPUT (policy DROP) # Chain FORWARD (policy ACCEPT) # Chain OUTPUT (policy ACCEPT)

# If policy is DROP, need explicit ACCEPT rules: iptables -P INPUT ACCEPT # Change policy # Or add rules before default

# Set default policy: iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT

# For FirewallD: # Zone default target: firewall-cmd --zone=public --get-target // default (usually reject)

# Set target: firewall-cmd --zone=public --set-target=ACCEPT --permanent firewall-cmd --reload

# UFW default policy: ufw default deny incoming ufw default allow outgoing ```

Step 8: Test Rule Effectiveness

```bash # Test from external host: nc -zv server 80 telnet server 80

# Test from localhost: nc -zv localhost 80

# Test with curl: curl -I http://server

# Check connection tracking: iptables -L -n -v | grep dpt:80

# Use conntrack: conntrack -L | grep dport=80

# Monitor packets: tcpdump -i any port 80 -nn

# Check iptables counters: iptables -L INPUT -n -v # Look at packets column

# Zero counters: iptables -Z INPUT

# Add logging rule: iptables -A INPUT -p tcp --dport 80 -j LOG --log-prefix "PORT80: " tail /var/log/messages | grep PORT80

# Check kernel logs: dmesg | grep PORT80 ```

Step 9: Debug Rule Issues

```bash # Trace packet through iptables: iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE

# Check trace output: dmesg | grep TRACE

# Monitor all rules: watch -n 1 iptables -L INPUT -n -v

# Check packet flow: # PREROUTING -> INPUT -> Local Process # PREROUTING -> FORWARD -> POSTROUTING

# Add rule to different chain: iptables -A PREROUTING -t raw -p tcp --dport 80 -j ACCEPT

# Check NAT interference: iptables -t nat -L -n -v

# For complex debugging: iptables -I INPUT -p tcp --dport 80 -j LOG --log-prefix "IN80: " --log-level 4 iptables -I OUTPUT -p tcp --sport 80 -j LOG --log-prefix "OUT80: " --log-level 4

# Check logs: tail -f /var/log/kern.log | grep "IN80|OUT80" ```

Step 10: Firewall Verification Script

```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-firewall.sh #!/bin/bash

echo "=== Firewall Status ===" systemctl status firewalld 2>/dev/null || systemctl status ufw 2>/dev/null || systemctl status iptables 2>/dev/null

echo "" echo "=== Active Rules ===" if command -v firewall-cmd &> /dev/null; then firewall-cmd --state firewall-cmd --list-all elif command -v ufw &> /dev/null; then ufw status numbered else iptables -L -n -v --line-numbers fi

echo "" echo "=== Default Policy ===" iptables -L | grep "(policy"

echo "" echo "=== Recent Logs ===" tail -20 /var/log/kern.log 2>/dev/null | grep -i "iptables|ufw|firewall" || \ tail -20 /var/log/messages 2>/dev/null | grep -i "iptables|ufw|firewall"

echo "" echo "=== Port 80 Test ===" nc -zv localhost 80 2>&1 || echo "Port 80 not accessible locally"

echo "" echo "=== Connection Stats ===" ss -tlnp | grep :80 || echo "No service on port 80" EOF

chmod +x /usr/local/bin/check-firewall.sh

# Run: /usr/local/bin/check-firewall.sh

# Monitor in real-time: watch -n 5 iptables -L -n -v ```

Firewall Rule Checklist

CheckCommandExpected
Service statussystemctl statusActive/running
Rules existiptables -LRule listed
Rule order--line-numbersBefore blocking rules
Interface-i optionCorrect interface
Persistenceiptables-saveRules saved
Default policyiptables -LPolicy appropriate

Verify the Fix

```bash # After fixing firewall rule

# 1. Check rule exists iptables -L INPUT -n | grep dpt:80 // Rule visible

# 2. Check rule position iptables -L INPUT --line-numbers | grep dpt:80 // First or early position

# 3. Test connection nc -zv server 80 // Connection succeeded

# 4. Check counters iptables -L INPUT -n -v | grep dpt:80 // Packets incrementing

# 5. Verify persistence systemctl restart iptables iptables -L INPUT | grep dpt:80 // Rule still present

# 6. Test service curl http://server // Response received ```

  • [Fix Network Port Not Listening](/articles/fix-network-port-not-listening)
  • [Fix SSH Connection Refused](/articles/fix-ssh-connection-refused)
  • [Fix Network Interface Not Found](/articles/fix-network-interface-not-found)