Fix race condition in ensure_forward_stateful: add threading.Lock

Concurrent callers (health monitor + startup) could both pass the
delete-all loop and each insert a copy, producing duplicate
ESTABLISHED,RELATED rules. Lock serialises all calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 10:12:18 -04:00
parent 1b61e9e290
commit b8e57b6e51
+17 -13
View File
@@ -8,10 +8,13 @@ import os
import subprocess import subprocess
import logging import logging
import re import re
import threading
from typing import Dict, List, Any, Optional from typing import Dict, List, Any, Optional
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_forward_stateful_lock = threading.Lock()
# Virtual IPs assigned to Caddy per service — must match Caddyfile listeners. # Virtual IPs assigned to Caddy per service — must match Caddyfile listeners.
# Populated at import time from the default subnet; call update_service_ips() # Populated at import time from the default subnet; call update_service_ips()
# whenever ip_range changes so all downstream callers see the new values. # 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 which pushes this rule down every time wg0 restarts — causing ICMP to hit the
per-peer DROP rule before reaching the stateful ACCEPT. per-peer DROP rule before reaching the stateful ACCEPT.
""" """
try: with _forward_stateful_lock:
# Remove all existing instances so we can re-anchor at position 1. try:
# PostUp -I FORWARD rules drift this rule down on every wg0 restart. # Remove all existing instances so we can re-anchor at position 1.
while _wg_exec(['iptables', '-D', 'FORWARD', '-m', 'state', # PostUp -I FORWARD rules drift this rule down on every wg0 restart.
'--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']).returncode == 0: while _wg_exec(['iptables', '-D', 'FORWARD', '-m', 'state',
pass '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']).returncode == 0:
_wg_exec(['iptables', '-I', 'FORWARD', '1', '-m', 'state', pass
'--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT']) _wg_exec(['iptables', '-I', 'FORWARD', '1', '-m', 'state',
logger.info('ensure_forward_stateful: ESTABLISHED,RELATED anchored at FORWARD position 1') '--state', 'ESTABLISHED,RELATED', '-j', 'ACCEPT'])
return True logger.info('ensure_forward_stateful: ESTABLISHED,RELATED anchored at FORWARD position 1')
except Exception as e: return True
logger.error(f'ensure_forward_stateful: {e}') except Exception as e:
return False logger.error(f'ensure_forward_stateful: {e}')
return False
def ensure_cell_api_dnat() -> bool: def ensure_cell_api_dnat() -> bool: