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
+4
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,6 +462,7 @@ 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.
""" """
with _forward_stateful_lock:
try: try:
# Remove all existing instances so we can re-anchor at position 1. # Remove all existing instances so we can re-anchor at position 1.
# PostUp -I FORWARD rules drift this rule down on every wg0 restart. # PostUp -I FORWARD rules drift this rule down on every wg0 restart.