Debugging Complex Rule Interactions
Debugging Complex Rule Interactions
When multiple firewall rules interact, unexpected behaviors can emerge. Understanding rule precedence and interaction helps diagnose complex issues.
Create a firewall rule analyzer:
#!/usr/bin/env python3
import re
import ipaddress
import json
class FirewallRuleAnalyzer:
def __init__(self):
self.rules = []
self.conflicts = []
def parse_iptables_rules(self, output):
"""Parse iptables-save output into structured format"""
current_table = None
current_chain = None
for line in output.split('\n'):
line = line.strip()
# Table definition
if line.startswith('*'):
current_table = line[1:]
continue
# Chain definition
if line.startswith(':'):
parts = line.split()
current_chain = parts[0][1:]
continue
# Rule definition
if line.startswith('-A'):
rule = self.parse_rule(line, current_table, current_chain)
if rule:
self.rules.append(rule)
return self.rules
def parse_rule(self, line, table, chain):
"""Parse individual iptables rule"""
rule = {
'table': table,
'chain': chain,
'raw': line,
'source': None,
'destination': None,
'protocol': None,
'dport': None,
'sport': None,
'action': None,
'modules': []
}
# Extract rule components
parts = line.split()
i = 0
while i < len(parts):
if parts[i] == '-s' and i + 1 < len(parts):
rule['source'] = parts[i + 1]
i += 2
elif parts[i] == '-d' and i + 1 < len(parts):
rule['destination'] = parts[i + 1]
i += 2
elif parts[i] == '-p' and i + 1 < len(parts):
rule['protocol'] = parts[i + 1]
i += 2
elif parts[i] == '--dport' and i + 1 < len(parts):
rule['dport'] = parts[i + 1]
i += 2
elif parts[i] == '--sport' and i + 1 < len(parts):
rule['sport'] = parts[i + 1]
i += 2
elif parts[i] == '-j' and i + 1 < len(parts):
rule['action'] = parts[i + 1]
i += 2
elif parts[i] == '-m' and i + 1 < len(parts):
rule['modules'].append(parts[i + 1])
i += 2
else:
i += 1
return rule
def find_conflicts(self):
"""Identify potentially conflicting rules"""
for i, rule1 in enumerate(self.rules):
for j, rule2 in enumerate(self.rules[i+1:], i+1):
conflict = self.check_rule_conflict(rule1, rule2)
if conflict:
self.conflicts.append({
'rule1': rule1,
'rule2': rule2,
'type': conflict
})
return self.conflicts
def check_rule_conflict(self, rule1, rule2):
"""Check if two rules conflict"""
# Skip if different chains
if rule1['chain'] != rule2['chain']:
return None
# Check for overlapping IP ranges
if rule1['source'] and rule2['source']:
try:
net1 = ipaddress.ip_network(rule1['source'], strict=False)
net2 = ipaddress.ip_network(rule2['source'], strict=False)
if net1.overlaps(net2):
# Check if actions differ
if rule1['action'] != rule2['action']:
return 'overlapping_source_different_action'
except:
pass
# Check for same port different actions
if (rule1['dport'] == rule2['dport'] and
rule1['protocol'] == rule2['protocol'] and
rule1['action'] != rule2['action']):
return 'same_port_different_action'
# Check for redundant rules
if (rule1['source'] == rule2['source'] and
rule1['destination'] == rule2['destination'] and
rule1['dport'] == rule2['dport'] and
rule1['action'] == rule2['action']):
return 'redundant_rule'
return None
def simulate_packet(self, src_ip, dst_ip, dst_port, protocol='tcp'):
"""Simulate packet flow through rules"""
print(f"\n=== Simulating packet flow ===")
print(f"Packet: {src_ip} -> {dst_ip}:{dst_port}/{protocol}")
for i, rule in enumerate(self.rules):
if self.packet_matches_rule(src_ip, dst_ip, dst_port, protocol, rule):
print(f"\nRule #{i+1} MATCHES:")
print(f" Chain: {rule['chain']}")
print(f" Action: {rule['action']}")
print(f" Raw: {rule['raw']}")
if rule['action'] in ['DROP', 'REJECT', 'ACCEPT']:
print(f"\nFINAL VERDICT: {rule['action']}")
return rule['action']
print("\nFINAL VERDICT: Default policy")
return 'DEFAULT'