From 0bab6378628b1d52e8f88b63034aaafb784336df Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 14 Aug 2026 20:11:11 +0200 Subject: [PATCH] some more updates --- .../gluetun-pia-wireguard-rotator/Dockerfile | 1 - .../gluetun-pia-wireguard-rotator/README.md | 8 +- .../gluetun-pia-wireguard-rotator/rotate.py | 93 +------------------ 3 files changed, 6 insertions(+), 96 deletions(-) diff --git a/Dockers/gluetun-pia-wireguard-rotator/Dockerfile b/Dockers/gluetun-pia-wireguard-rotator/Dockerfile index 18dbc70..9ac488a 100644 --- a/Dockers/gluetun-pia-wireguard-rotator/Dockerfile +++ b/Dockers/gluetun-pia-wireguard-rotator/Dockerfile @@ -23,7 +23,6 @@ ENV SERVERLIST_CACHE_MAX_AGE=168h ENV TOKEN_CACHE_PATH=/config/cache/pia-token.json ENV TOKEN_CACHE_TTL=20h ENV PIA_CA_PATH=/config/cache/ca.rsa.4096.crt -ENV WG_CONFIG_MAX_AGE=7d ENV LATENCY_SWITCH_MARGIN_MS=15 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/Dockers/gluetun-pia-wireguard-rotator/README.md b/Dockers/gluetun-pia-wireguard-rotator/README.md index fa33987..b750922 100644 --- a/Dockers/gluetun-pia-wireguard-rotator/README.md +++ b/Dockers/gluetun-pia-wireguard-rotator/README.md @@ -49,8 +49,6 @@ docker run --rm --entrypoint /opt/venv/bin/python \ | `TOKEN_CACHE_PATH` | `/config/cache/pia-token.json` | Disk-cache voor PIA auth-token | | `TOKEN_CACHE_TTL` | `20h` | Hergebruik token i.p.v. opnieuw inloggen | | `PIA_CA_PATH` | `/config/cache/ca.rsa.4096.crt` | Gecachete PIA CA voor `addKey` TLS | -| `WG_CONFIG_MAX_AGE` | `7d` | Geen nieuwe token/`addKey` zolang endpoint gelijk blijft en config jonger is | -| `FORCE_ROTATE` | `false` | `true` = altijd nieuwe config + restarts | | `FORCE_TOKEN_REFRESH` | `false` | `true` = token-cache negeren | | `RATE_LIMIT_WAIT_SECONDS` | `3600` | Wachttijd bij PIA rate-limit vóór retry | | `TZ` | `Europe/Brussels` | Tijdzone voor scheduling | @@ -113,8 +111,8 @@ Behoud minimaal: 1. Latency meten over WG-servers in `PIA_REGIONS` (of random) 2. **Pin** de snelste server-IP (niet alleen regio) -3. Keypair lokaal genereren; token + `addKey` alleen bij echte refresh -4. Caches: serverlist, token (~20u), CA-cert -5. Skip token/`addKey`/restarts als endpoint gelijk blijft én config jonger dan `WG_CONFIG_MAX_AGE` +3. Altijd opnieuw authenticeren: lokaal keypair + token (cached) + `addKey` + nieuwe `wg0.conf` +4. Caches: serverlist, token (~20u), CA-cert — die beperken rate limits, niet de rotatie zelf +5. Daarna `GLUETUN_CONTAINER` + `RESTART_CONTAINERS` herstarten **Let op:** zet gluetun **niet** in `RESTART_CONTAINERS`; gebruik `GLUETUN_CONTAINER`. Elke echte rotatie geeft korte downtime. diff --git a/Dockers/gluetun-pia-wireguard-rotator/rotate.py b/Dockers/gluetun-pia-wireguard-rotator/rotate.py index bacf4e0..e4c94c3 100644 --- a/Dockers/gluetun-pia-wireguard-rotator/rotate.py +++ b/Dockers/gluetun-pia-wireguard-rotator/rotate.py @@ -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,24 +279,13 @@ 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") + log("Reauthenticating: generating WireGuard config via native PIA client") pia.generate_wg_config( require_env("PIA_USER"), require_env("PIA_PASS"), @@ -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}")