feat: routing page — port forwarding tab, live iptables, diagnostics, firewall delete
Backend: - routing_manager.remove_firewall_rule(): remove stored rule + iptables -D - routing_manager.get_live_iptables(): dump filter/nat tables from cell-wireguard - DELETE /api/routing/firewall/<rule_id> endpoint (was missing) - GET /api/routing/live-iptables endpoint Frontend Routing.jsx — 7 tabs: - Overview: proper routing table with destination/gateway/interface columns - Port Forwarding: clean DNAT form (protocol, ext port → internal IP:port) - NAT Rules: MASQUERADE/SNAT only, cleaner layout - Peer Routes: IP route entries through VPN peers - Firewall: custom rules with working delete button - Live iptables: read-only terminal view of actual running rules in cell-wireguard - Diagnostics: ping + traceroute test from server with output display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -485,6 +485,49 @@ class RoutingManager(BaseServiceManager):
|
||||
logger.error(f"Failed to get routing logs: {e}")
|
||||
return {'error': str(e)}
|
||||
|
||||
def remove_firewall_rule(self, rule_id: str) -> bool:
|
||||
"""Remove a stored firewall rule and delete it from iptables."""
|
||||
try:
|
||||
rules = self._load_rules()
|
||||
rule = next((r for r in rules['firewall_rules'] if r['id'] == rule_id), None)
|
||||
if not rule:
|
||||
return False
|
||||
rules['firewall_rules'] = [r for r in rules['firewall_rules'] if r['id'] != rule_id]
|
||||
self._save_rules(rules)
|
||||
try:
|
||||
cmd = ['iptables', '-D', rule['rule_type'],
|
||||
'-s', rule['source'], '-d', rule['destination']]
|
||||
if rule.get('protocol') and rule['protocol'] != 'ALL':
|
||||
cmd += ['-p', rule['protocol'].lower()]
|
||||
if rule.get('port'):
|
||||
cmd += ['--dport', str(rule['port'])]
|
||||
if rule.get('port_range'):
|
||||
cmd += ['--dport', rule['port_range'].replace('-', ':')]
|
||||
cmd += ['-j', rule['action']]
|
||||
subprocess.run(cmd, capture_output=True, timeout=10)
|
||||
except Exception as e:
|
||||
logger.warning(f"iptables -D failed (rule may already be gone): {e}")
|
||||
logger.info(f"Removed firewall rule {rule_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to remove firewall rule: {e}")
|
||||
return False
|
||||
|
||||
def get_live_iptables(self) -> dict:
|
||||
"""Return live iptables rules from the WireGuard container."""
|
||||
out = {}
|
||||
for table in ('filter', 'nat'):
|
||||
try:
|
||||
r = subprocess.run(
|
||||
['docker', 'exec', 'cell-wireguard',
|
||||
'iptables', '-t', table, '-L', '-n', '-v', '--line-numbers'],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
out[table] = r.stdout if r.returncode == 0 else r.stderr
|
||||
except Exception as e:
|
||||
out[table] = str(e)
|
||||
return out
|
||||
|
||||
def get_nat_rules(self):
|
||||
"""Return all NAT rules."""
|
||||
rules = self._load_rules()
|
||||
|
||||
Reference in New Issue
Block a user