diff --git a/api/firewall_manager.py b/api/firewall_manager.py index 37b4e7b..c7b94fc 100644 --- a/api/firewall_manager.py +++ b/api/firewall_manager.py @@ -8,10 +8,13 @@ import os import subprocess import logging import re +import threading from typing import Dict, List, Any, Optional logger = logging.getLogger(__name__) +_forward_stateful_lock = threading.Lock() + # Virtual IPs assigned to Caddy per service — must match Caddyfile listeners. # Populated at import time from the default subnet; call update_service_ips() # whenever ip_range changes so all downstream callers see the new values. @@ -459,19 +462,20 @@ def ensure_forward_stateful() -> bool: which pushes this rule down every time wg0 restarts — causing ICMP to hit the per-peer DROP rule before reaching the stateful ACCEPT. """ - try: - # Remove all existing instances so we can re-anchor at position 1. - # PostUp -I FORWARD rules drift this rule down on every wg0 restart. - while _wg_exec(['iptables', '-D', 'FORWARD', '-m', 'state', - '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']).returncode == 0: - pass - _wg_exec(['iptables', '-I', 'FORWARD', '1', '-m', 'state', - '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']) - logger.info('ensure_forward_stateful: ESTABLISHED,RELATED anchored at FORWARD position 1') - return True - except Exception as e: - logger.error(f'ensure_forward_stateful: {e}') - return False + with _forward_stateful_lock: + try: + # Remove all existing instances so we can re-anchor at position 1. + # PostUp -I FORWARD rules drift this rule down on every wg0 restart. + while _wg_exec(['iptables', '-D', 'FORWARD', '-m', 'state', + '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']).returncode == 0: + pass + _wg_exec(['iptables', '-I', 'FORWARD', '1', '-m', 'state', + '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']) + logger.info('ensure_forward_stateful: ESTABLISHED,RELATED anchored at FORWARD position 1') + return True + except Exception as e: + logger.error(f'ensure_forward_stateful: {e}') + return False def ensure_cell_api_dnat() -> bool: