Remove WG_CONFIG_MAX_AGE environment variable and update README to reflect changes in WireGuard configuration handling. Refactor rotation logic in rotate.py to always generate a new keypair and simplify state management.
Build and Push Docker Images / build-and-push (push) Successful in 20s

This commit is contained in:
2026-08-14 21:19:35 +02:00
parent bc9d79de02
commit 6952789722
3 changed files with 5 additions and 95 deletions
@@ -236,83 +236,13 @@ def restart_containers(containers: list[str]) -> None:
raise SystemExit(f"docker restart failed for container={container}")
def config_age_seconds(wg_path: Path) -> float | None:
if not wg_path.is_file():
return None
try:
return time.time() - wg_path.stat().st_mtime
except OSError:
return None
def should_skip_regeneration(
server: pia.WgServer,
state: dict[str, Any],
wg_path: Path,
latency_results: list[dict[str, Any]],
) -> bool:
if pia.env_bool("FORCE_ROTATE", False):
log("FORCE_ROTATE=true; regenerating WireGuard config")
return False
previous_region = state.get("region")
previous_server_ip = state.get("server_ip")
if previous_region != server.region:
return False
age = config_age_seconds(wg_path)
if age is None:
return False
max_age = pia.parse_duration_seconds(os.environ.get("WG_CONFIG_MAX_AGE", "7d"), 604800)
if age > max_age:
log(f"Existing WireGuard config is stale (age {int(age)}s > max {max_age}s); regenerating")
return False
if not re.search(
r"^\[Interface\]",
wg_path.read_text(encoding="utf-8", errors="replace"),
flags=re.MULTILINE,
):
return False
# Same region, different server: only regenerate if clearly faster.
if previous_server_ip and server.ip != previous_server_ip:
margin = float(os.environ.get("LATENCY_SWITCH_MARGIN_MS", "15"))
previous_latency = state.get("latency_ms")
winner = next((item for item in latency_results if item.get("server_ip") == server.ip), None)
new_latency = winner.get("latency_ms") if winner else None
if (
isinstance(previous_latency, (int, float))
and isinstance(new_latency, (int, float))
and (previous_latency - new_latency) < margin
):
log(
f"Keeping current server {previous_server_ip}; "
f"{server.ip} only {previous_latency - new_latency:.2f}ms faster "
f"(margin {margin:g}ms)"
)
return True
log(f"Switching server within {server.region}: {previous_server_ip} -> {server.ip}")
return False
log(
f"Skipping PIA token/addKey; endpoint unchanged "
f"({server.region}/{server.ip}) and config age {int(age)}s <= {max_age}s"
)
return True
def write_state(
state_path: Path,
server: pia.WgServer,
mode: str,
restarted: list[str],
latency_results: list[dict[str, Any]],
*,
skipped: bool = False,
) -> None:
previous = read_state(state_path)
winner = next(
(
item
@@ -329,17 +259,11 @@ def write_state(
"selection": mode,
"wg_config": os.environ.get("WG_CONFIG_PATH", "/config/wireguard/wg0.conf"),
"restarted_containers": restarted,
"rotated_at": now,
"last_checked_at": now,
"skipped_regeneration": skipped,
}
if skipped and isinstance(previous.get("rotated_at"), str):
payload["rotated_at"] = previous["rotated_at"]
else:
payload["rotated_at"] = now
if winner and winner.get("latency_ms") is not None:
payload["latency_ms"] = winner["latency_ms"]
elif skipped and previous.get("latency_ms") is not None:
payload["latency_ms"] = previous["latency_ms"]
if latency_results:
payload["latency_results"] = latency_results
@@ -355,21 +279,10 @@ def rotate_once() -> None:
state_path = Path(os.environ.get("ROTATOR_STATE_PATH", "/config/rotator-state.json"))
wg_path = Path(os.environ.get("WG_CONFIG_PATH", "/config/wireguard/wg0.conf"))
state = read_state(state_path)
server, mode, latency_results = pick_server(state_path)
log(f"Selected endpoint: {server.region} / {server.cn} / {server.ip} (mode={mode})")
if should_skip_regeneration(server, state, wg_path, latency_results):
# If we decided to keep the previous server IP, persist that identity.
keep_ip = state.get("server_ip") if isinstance(state.get("server_ip"), str) else server.ip
keep_cn = state.get("server_cn") if isinstance(state.get("server_cn"), str) else server.cn
if keep_ip != server.ip:
server = pia.WgServer(region=server.region, ip=keep_ip, cn=str(keep_cn or server.cn))
write_state(state_path, server, mode, [], latency_results, skipped=True)
log(f"Rotation skipped for {server.region}/{server.ip}")
return
with tempfile.TemporaryDirectory(prefix="pia-rotate-") as tmp:
tmp_conf = Path(tmp) / "wg0.conf"
log("Generating WireGuard config via native PIA client")
@@ -391,7 +304,7 @@ def rotate_once() -> None:
restarted = parse_restart_containers()
restart_containers(restarted)
write_state(state_path, server, mode, restarted, latency_results, skipped=False)
write_state(state_path, server, mode, restarted, latency_results)
log(f"Rotation complete for {server.region}/{server.ip}")