funwithlinux guide

Building Redundant Firewalls with iptables

In today’s interconnected world, network security is non-negotiable. Firewalls act as the first line of defense, filtering traffic to protect critical infrastructure from unauthorized access, malware, and cyberattacks. However, a single firewall introduces a **single point of failure (SPOF)**: if it crashes, loses power, or is compromised, your entire network becomes vulnerable or inaccessible. Redundant firewalls solve this problem by ensuring continuous operation even if one firewall fails. By combining the flexibility of `iptables` (a powerful, Linux-based firewall utility) with high-availability (HA) tools like `Keepalived` (for failover), you can build a resilient firewall setup that minimizes downtime and maximizes security. This blog will guide you through designing, implementing, and testing a redundant firewall architecture using `iptables`. We’ll cover everything from basic concepts to advanced configurations, ensuring you can replicate the setup in your environment.

Table of Contents

  1. Understanding Redundant Firewalls & iptables

    • 1.1 What is iptables?
    • 1.2 Why Redundancy Matters
    • 1.3 Redundancy Architectures: Active-Passive vs. Active-Active
  2. Prerequisites & Architecture Design

    • 2.1 Hardware/Software Requirements
    • 2.2 Network Topology
  3. Step 1: Setting Up the Base Firewalls

    • 3.1 Installing Linux & iptables
    • 3.2 Configuring Network Interfaces
    • 3.3 Basic iptables Rule Setup
  4. Step 2: Ensuring Rule Consistency with Sync

    • 4.1 Manual Rule Sync (iptables-save/restore)
    • 4.2 Automated Sync with Configuration Management
  5. Step 3: Implementing Failover with Keepalived (VRRP)

    • 5.1 What is VRRP?
    • 5.2 Installing Keepalived
    • 5.3 Configuring Keepalived for Active-Passive Failover
    • 5.4 Health Checks for Firewall Availability
  6. Step 4: Testing the Redundant Setup

    • 6.1 Simulating a Firewall Failure
    • 6.2 Verifying Traffic Flow & Rule Enforcement
    • 6.3 Testing Stateful Connection Continuity
  7. Advanced: Active-Active & State Sync with conntrackd

    • 7.1 Active-Active Architecture
    • 7.2 Syncing Connection States with conntrackd
  8. Security Best Practices

  9. Troubleshooting Common Issues

  10. Conclusion

  11. References

1. Understanding Redundant Firewalls & iptables

1.1 What is iptables?

iptables is a user-space utility for configuring Linux kernel firewall rules. It filters traffic based on predefined rules (e.g., allow/deny traffic from a specific IP, port, or protocol) and operates at the network layer (Layer 3) and transport layer (Layer 4) of the OSI model. Key features include:

  • Stateful inspection: Tracks active connections (e.g., ESTABLISHED, NEW states) to allow return traffic.
  • Chain-based structure: Rules are organized into chains (INPUT, OUTPUT, FORWARD) and tables (filter, nat, mangle).
  • Flexibility: Supports custom rules, logging, and integration with other tools (e.g., ipset for IP list management).

1.2 Why Redundancy Matters

A single firewall is a SPOF. Redundancy ensures:

  • High Availability (HA): Traffic continues flowing if one firewall fails (e.g., hardware failure, network outage).
  • Business Continuity: Critical services (e.g., web servers, databases) remain accessible to users and clients.
  • Security Resilience: A failed firewall won’t expose the network to unfiltered traffic during downtime.

1.3 Redundancy Architectures

Two common redundancy models exist:

Active-Passive (Default for Simplicity)

  • Active Firewall: Handles all traffic and holds a “virtual IP” (VIP) that clients use to route traffic.
  • Passive Firewall: Idle until the active firewall fails, then takes over the VIP and traffic.
  • Pros: Simple to configure; minimal complexity.
  • Cons: Underutilizes the passive firewall’s resources.

Active-Active

  • Both firewalls handle traffic simultaneously (e.g., via load balancing).
  • Pros: Better resource utilization; higher throughput.
  • Cons: Complex (requires syncing stateful connections and load balancing).

We’ll focus on active-passive first (easier to implement) and touch on active-active later.

2. Prerequisites & Architecture Design

2.1 Hardware/Software Requirements

  • Two Linux Servers: Physical or virtual (e.g., Ubuntu 22.04 LTS, CentOS Stream 9).
    • Minimum specs: 2 CPU cores, 2GB RAM, 20GB storage.
    • Two network interfaces (NICs) per server:
      • eth0/enp0s3: WAN (internet-facing).
      • eth1/enp0s8: LAN (internal network-facing).
  • Network Switches: For connecting WAN/LAN to firewalls (use redundant switches to avoid switch SPOF).
  • Software:
    • iptables (preinstalled on most Linux distros).
    • iptables-persistent (to save rules across reboots).
    • Keepalived (for VRRP-based failover).
    • conntrackd (optional, for syncing stateful connections in active-active setups).

2.2 Network Topology

Redundant Firewall Topology
Diagram: Active-Passive Firewall Topology

  • Clients (e.g., workstations, servers) route traffic to a VIP (e.g., 192.168.1.1).
  • The active firewall owns the VIP and forwards traffic between WAN (10.0.0.0/24) and LAN (192.168.1.0/24).
  • Firewalls are connected to WAN/LAN switches; switches connect to upstream routers (WAN) and internal servers (LAN).

3. Step 1: Setting Up the Base Firewalls

3.1 Installing Linux & iptables

  1. Install a Linux distro (e.g., Ubuntu) on both firewalls. Name them fw-active and fw-passive.
  2. Update packages and install required tools:
    # On Ubuntu/Debian
    sudo apt update && sudo apt install -y iptables iptables-persistent keepalived
    
    # On CentOS/RHEL
    sudo dnf install -y iptables iptables-services keepalived
    sudo systemctl enable --now iptables  # Persist rules

3.2 Configuring Network Interfaces

Assign static IPs to each firewall’s NICs. Replace X with 1 for fw-active and 2 for fw-passive:

WAN Interface (eth0)

  • fw-active: 10.0.0.X/24 (e.g., 10.0.0.1/24).
  • fw-passive: 10.0.0.X/24 (e.g., 10.0.0.2/24).
  • Gateway: 10.0.0.254 (upstream router IP).

LAN Interface (eth1)

  • fw-active: 192.168.1.X/24 (e.g., 192.168.1.10/24).
  • fw-passive: 192.168.1.X/24 (e.g., 192.168.1.11/24).

Example Netplan Config (Ubuntu):
Create /etc/netplan/01-netcfg.yaml on both firewalls:

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:  # WAN
      addresses: [10.0.0.1/24]  # Use 10.0.0.2 for fw-passive
      gateway4: 10.0.0.254
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]
    eth1:  # LAN
      addresses: [192.168.1.10/24]  # Use 192.168.1.11 for fw-passive

Apply with: sudo netplan apply.

3.3 Basic iptables Rule Setup

Define a baseline iptables policy to allow essential traffic (e.g., SSH, HTTP/HTTPS) and block everything else. Apply the same rules to both firewalls to ensure consistency post-failover.

Step 1: Flush Existing Rules

sudo iptables -F  # Flush all chains
sudo iptables -X  # Delete custom chains
sudo iptables -t nat -F  # Flush nat table

Step 2: Set Default Policies

Deny all incoming/forwarded traffic by default; allow outgoing:

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

Step 3: Allow Essential Traffic

# Allow loopback (localhost) traffic
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow SSH (adjust port if using non-default, e.g., 2222)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow established/related connections (stateful inspection)
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Forward HTTP/HTTPS traffic from LAN to WAN (adjust ports as needed)
sudo iptables -A FORWARD -i eth1 -o eth0 -p tcp --dport 80 -j ACCEPT
sudo iptables -A FORWARD -i eth1 -o eth0 -p tcp --dport 443 -j ACCEPT
sudo iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# Enable NAT (MASQUERADE) for LAN clients to access WAN
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Step 4: Save Rules

Persist rules across reboots:

# Ubuntu/Debian
sudo netfilter-persistent save  # Uses iptables-persistent

# CentOS/RHEL
sudo service iptables save

4. Step 2: Ensuring Rule Consistency with Sync

For redundancy to work, both firewalls must have identical iptables rules. If rules differ, failover could expose the network to unfiltered traffic.

4.1 Manual Rule Sync (For Small Setups)

Use iptables-save and iptables-restore to copy rules from fw-active to fw-passive:

# On fw-active: Save rules to a file
sudo iptables-save > /tmp/iptables.rules

# Copy to fw-passive (replace 192.168.1.11 with passive IP)
scp /tmp/iptables.rules [email protected]:/tmp/

# On fw-passive: Restore rules
sudo iptables-restore < /tmp/iptables.rules
sudo netfilter-persistent save  # Persist

4.2 Automated Sync with Configuration Management

For large environments, use tools like Ansible or Puppet to enforce rule consistency:

Ansible Example

Create a playbook (sync-iptables.yml) to push rules to both firewalls:

- name: Sync iptables rules to firewalls
  hosts: firewalls  # Define in /etc/ansible/hosts
  tasks:
    - name: Copy iptables rules file
      copy:
        src: /path/to/iptables.rules  # Centralized rule file
        dest: /tmp/iptables.rules
        mode: '0644'

    - name: Restore iptables rules
      command: iptables-restore < /tmp/iptables.rules

    - name: Persist rules
      command: netfilter-persistent save  # Ubuntu/Debian
      # For CentOS/RHEL: command: service iptables save

Run with: ansible-playbook sync-iptables.yml.

5. Step 3: Implementing Failover with Keepalived (VRRP)

To automate failover, we use Keepalived, which implements the Virtual Router Redundancy Protocol (VRRP). VRRP lets multiple routers (firewalls) share a VIP; the “master” (active) holds the VIP, and the “backup” (passive) takes over if the master fails.

5.1 What is VRRP?

  • VIP: A shared IP (e.g., 192.168.1.1) that clients use to route traffic.
  • Virtual Router ID (VRID): A unique ID (1-255) for the VRRP group (must match on both firewalls).
  • Priority: Determines the master (higher priority wins; default: 100 for master, 90 for backup).

5.2 Installing Keepalived

Already installed in Step 3.1. Enable and start the service:

sudo systemctl enable --now keepalived

5.3 Configuring Keepalived for Active-Passive Failover

On fw-active (Master)

Edit /etc/keepalived/keepalived.conf:

vrrp_instance VI_1 {
  state MASTER           # This is the active firewall
  interface eth1         # LAN interface (where VIP lives)
  virtual_router_id 51   # Unique VRID (1-255)
  priority 100           # Higher than backup (90)
  advert_int 1           # VRRP heartbeat interval (1s)

  # Authentication (prevents rogue VRRP routers)
  authentication {
    auth_type PASS
    auth_pass securepassword123  # Same on both firewalls
  }

  # Virtual IP (VIP) to float between firewalls
  virtual_ipaddress {
    192.168.1.1/24 dev eth1  # VIP on LAN interface
  }

  # Health check: Restart Keepalived if iptables fails
  track_script {
    check_iptables
  }
}

# Custom health check script
vrrp_script check_iptables {
  script "/usr/local/bin/check-iptables.sh"  # Path to script
  interval 2  # Check every 2s
  weight -20  # Reduce priority by 20 if check fails (triggers failover)
}

On fw-passive (Backup)

Edit /etc/keepalived/keepalived.conf (only differences from master are state and priority):

vrrp_instance VI_1 {
  state BACKUP          # Passive until master fails
  interface eth1
  virtual_router_id 51  # Same as master
  priority 90           # Lower than master (100)
  advert_int 1

  authentication {
    auth_type PASS
    auth_pass securepassword123  # Same as master
  }

  virtual_ipaddress {
    192.168.1.1/24 dev eth1
  }

  track_script {
    check_iptables
  }
}

vrrp_script check_iptables {
  script "/usr/local/bin/check-iptables.sh"
  interval 2
  weight -20
}

5.4 Health Check Script

The check-iptables.sh script ensures the firewall is healthy (e.g., iptables rules are loaded, interfaces are up). Create it on both firewalls:

sudo nano /usr/local/bin/check-iptables.sh

Add:

#!/bin/bash
# Check if iptables rules are loaded (e.g., count > 0)
if ! iptables -L | grep -q "Chain INPUT"; then
  exit 1  # Fail: No rules loaded
fi

# Check if LAN interface (eth1) is up
if ! ip link show eth1 | grep -q "UP"; then
  exit 1  # Fail: Interface down
fi

exit 0  # Success: Firewall is healthy

Make executable:

sudo chmod +x /usr/local/bin/check-iptables.sh

5.5 Restart Keepalived

Apply the config on both firewalls:

sudo systemctl restart keepalived
sudo systemctl enable keepalived  # Start on boot

6. Step 4: Testing the Setup

Verify failover works by simulating a failure and checking if the VIP floats to the passive firewall.

6.1 Verify Initial State

On fw-active, confirm it holds the VIP:

ip addr show eth1  # Look for "inet 192.168.1.1/24"

On fw-passive, the VIP should not appear on eth1.

6.2 Simulate Active Firewall Failure

Shut down fw-active or disconnect its LAN interface:

sudo poweroff  # Or: sudo ip link set eth1 down

6.3 Verify Failover

On fw-passive, check if the VIP is now present:

ip addr show eth1  # Should show "inet 192.168.1.1/24"

From a LAN client, test connectivity to the internet (e.g., ping 8.8.8.8). Traffic should flow through fw-passive.

6.4 Test Rule Enforcement

On fw-passive, verify iptables rules are active:

sudo iptables -L  # Rules should match fw-active’s

Attempt to access a blocked port (e.g., telnet 192.168.1.1 23). The connection should fail, confirming rules are enforced.

7. Advanced: Active-Active & State Sync with conntrackd

For active-active setups, use conntrackd to sync iptables’ connection tracking (conntrack) tables between firewalls. This ensures stateful connections (e.g., ongoing SSH sessions) aren’t dropped during failover.

7.1 Install conntrackd

sudo apt install -y conntrackd  # Ubuntu/Debian
# CentOS/RHEL: sudo dnf install -y conntrackd

7.2 Configure conntrackd

Edit /etc/conntrackd/conntrackd.conf on both firewalls. Example for active-active sync:

SyncMode FTFW  # Full sync (Forward Track Forward)
UDPInterface {
  Address 192.168.1.10  # Local firewall’s LAN IP
  Port 3780
  OtherAddress 192.168.1.11  # Peer firewall’s LAN IP
}
General {
  Nice 0
  HashSize 32768
  HashLimit 131072
}

Start conntrackd:

sudo systemctl enable --now conntrackd

8. Security Best Practices

  • Harden Firewalls: Disable unused services (e.g., telnet), restrict SSH access (use AllowUsers in sshd_config), and enable ufw/firewalld as a secondary layer.
  • Limit VRRP Access: Use iptables to block VRRP traffic from untrusted networks (VRRP uses IP protocol 112):
    sudo iptables -A INPUT -p vrrp -s 192.168.1.10,192.168.1.11 -j ACCEPT  # Allow only peers
    sudo iptables -A INPUT -p vrrp -j DROP  # Block others
  • Audit Rules: Regularly review iptables rules with iptables -L -v to remove unnecessary entries.
  • Monitor Logs: Use rsyslog to centralize logs (e.g., /var/log/iptables.log) and tools like ELK Stack for analysis.

9. Troubleshooting Common Issues

IssueSolution
VIP not floatingCheck Keepalived logs: journalctl -u keepalived. Ensure VRID, auth pass, and interface match on both firewalls.
iptables rules not syncingVerify Ansible/Puppet playbooks are running; check file permissions on iptables.rules.
Conntrackd sync failuresEnsure UDP port 3780 is open between firewalls; check conntrackd -s for sync status.
Traffic drops post-failoverUse conntrackd to sync connection states; test with long-lived sessions (e.g., scp transfers).

10. Conclusion

Redundant firewalls with iptables and Keepalived provide a robust defense against downtime and security breaches. By following this guide, you’ve built an active-passive setup with automated failover, consistent rule enforcement, and stateful connection support.

Key takeaways:

  • Sync Rules: Always ensure both firewalls have identical iptables rules.
  • Test Failover: Regularly simulate failures to validate HA.
  • Monitor: Use tools like Nagios or Prometheus to track firewall health and VRRP status.

With this architecture, your network will remain secure and available—even when hardware or software fails.

11. References