Сайдкар: таймзона браузера берётся из IP узла, а не жёстко МСК (#3187)
camoufox 0.5.5 при geoip=True кладёт координаты из GeoIP-записи exit-IP безусловно, а timezone — через setdefault, поэтому ручной config["timezone"]="Europe/Moscow" перебивал зону из IP. Узлы пула 13 и 15 (T2 NSK, UTC+7) и 14 (МегаФон Самара, UTC+4, RIPE 17.09) заявляли МСК при сибирских/самарских координатах, а библиотека писала LeakWarning на каждом запуске (4715 штук за 13 ч при 665 запусках). Ротация узла идёт внутри подсети оператора, смена узла релончит браузер — зона с запуска не протухает. Тест: в kwargs запуска нет ключей timezone/geolocation:* в config, geoip=True. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
34642e1dd5
commit
c871f804cf
2 changed files with 53 additions and 4 deletions
|
|
@ -895,10 +895,16 @@ async def _launch_browser(provider: str, proxy_override: str | None = None) -> N
|
|||
"block_images": _BLOCK_IMAGES_BY_PROVIDER.get(provider, _BLOCK_IMAGES_DEFAULT_FALLBACK),
|
||||
# Анти-leak: WebRTC может слить реальный IP контейнера мимо прокси → деанон.
|
||||
"block_webrtc": True,
|
||||
# Форсим таймзону +3 МСК. geoip берёт tz одного IP на старте, а БэкКоннект
|
||||
# ротирует IP на каждый запрос (видели Ульяновск UTC+4 при выходе СПб/МСК) →
|
||||
# геолик tz↔IP. Жёсткий Europe/Moscow убирает рассинхрон для РФ-мобильного.
|
||||
"config": {"timezone": "Europe/Moscow"},
|
||||
# Таймзону НЕ задаём (#3187): её ставит geoip=True по exit-IP на запуске —
|
||||
# из той же GeoIP-записи, что и координаты geolocation. Жёсткий
|
||||
# Europe/Moscow в config (было) camoufox 0.5.5 применял поверх geoip
|
||||
# (timezone — setdefault), а координаты брал из IP: для узлов вне UTC+3
|
||||
# браузер заявлял МСК при новосибирских координатах и адресе (узлы 13/15 —
|
||||
# T2 NSK, UTC+7; 14 — МегаФон Самара, UTC+4 по RIPE, 17.09.2026) и писал
|
||||
# LeakWarning «Please use the geoip parameter» на каждом запуске. Ротация
|
||||
# узла пула меняет IP внутри той же подсети оператора (health-логи 48 ч:
|
||||
# 176.59.137.x/176.59.144.x у 15, 178.176.78-79.x у 14), а смена узла
|
||||
# релончит браузер (_launched_proxy) — зона, взятая на запуске, не протухает.
|
||||
}
|
||||
if proxy is not None:
|
||||
kwargs["proxy"] = proxy
|
||||
|
|
|
|||
|
|
@ -149,3 +149,46 @@ def test_ensure_browser_env_then_pool_relaunches(monkeypatch: pytest.MonkeyPatch
|
|||
|
||||
asyncio.run(_run())
|
||||
assert calls == [("avito", None), ("avito", "http://pool:8080")]
|
||||
|
||||
|
||||
# ── #3187: гео-параметры запуска camoufox ────────────────────────────────────────
|
||||
|
||||
|
||||
def test_launch_leaves_timezone_to_geoip(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Зона и координаты — только от geoip по exit-IP, вручную в config не задаются.
|
||||
|
||||
camoufox 0.5.5 (utils.launch_options): geoip кладёт в config координаты
|
||||
безусловно, а timezone — через setdefault, то есть ручной config["timezone"]
|
||||
перебивает зону из IP, и координаты с зоной расходятся (узел в Новосибирске
|
||||
с заявленной МСК). Ключи 'timezone' и 'geolocation:*' в config — ровно те,
|
||||
на которые библиотека пишет LeakWarning (warn_manual_config).
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
class _FakeCamoufox:
|
||||
def __init__(self, **kwargs: object) -> None:
|
||||
captured.update(kwargs)
|
||||
|
||||
async def __aenter__(self) -> object:
|
||||
return object()
|
||||
|
||||
fake_mod = types.ModuleType("camoufox.async_api")
|
||||
fake_mod.AsyncCamoufox = _FakeCamoufox # type: ignore[attr-defined]
|
||||
monkeypatch.setitem(sys.modules, "camoufox", types.ModuleType("camoufox"))
|
||||
monkeypatch.setitem(sys.modules, "camoufox.async_api", fake_mod)
|
||||
|
||||
asyncio.run(server._launch_browser("avito", proxy_override="http://u:p@176.59.137.239:8080"))
|
||||
|
||||
assert captured["geoip"] is True
|
||||
assert captured["proxy"] == {
|
||||
"server": "http://176.59.137.239:8080",
|
||||
"username": "u",
|
||||
"password": "p",
|
||||
}
|
||||
config = captured.get("config", {})
|
||||
assert isinstance(config, dict)
|
||||
manual_geo = sorted(k for k in config if k == "timezone" or k.startswith("geolocation:"))
|
||||
assert manual_geo == []
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue