upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30s poll — port-80 pokes at gateways that aren't TollGates

クローズ
#768 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@Amperstrand がすでに取り組んでいます。

2026年10月9日 から。

  • #795 @Amperstrand による — オープン

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
20/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
停滞
技術スタック
go
領域
backend, security

調査の方向性

Start with src/upstream_session_manager/tollgate_prober.go and upstream_session_manager.go:237-245 to trace when probing triggers the port-80 request versus when advertisement validation succeeds; then read upstream_detector/main.go:274 and the related tests. Done means the trigger cannot run before validation, does not repeat for rejected gateways, and the workaround is gated and registered with a contract test. The reporter says a worktree is already in progress and plans to link a PR.

索引モデルが issue の本文から書いたものです。

説明

needs:human-review

Title: upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30 s poll — port-80 pokes at gateways that aren't TollGates

Found live during the rc1 bench-verify campaign (rig: ai-legion:~/tollgate-vm, evidence in bench-verify/trigger/evidence.log); this is the "future ticket" promised in the #754–#757 verification comments.

What is going on

src/upstream_session_manager/tollgate_prober.go carries a self-described temporary workaround:

  • TriggerCaptivePortalSession (line 224) GETs http://<gateway>:80/ with a browser-mimicking User-Agent ("Mozilla/5.0 (compatible; TollGate-Client/1.0)") so that "the upstream TollGate's ndsctl creates a client session for our device". Its own comment: "TEMPORARY WORKAROUND … NOT a long-term solution as it goes against the TollGate protocol specification." with a TODO: Remove this workaround once upstream TollGate implementations properly handle automatic session creation.
  • It is invoked from inside ProbeGatewayWithContext (line 110-120) the moment the :2121 probe returns any HTTP 200 with a non-empty body — i.e. before ValidateAdvertisementFromBytes runs in the caller (upstream_session_manager.go:237-245).

Three defects follow:

  1. Fire-then-validate. Any HTTP service on the gateway's :2121 earns a browser-UA GET to the gateway's :80 — TollGate or not. The prober's own log line says "validating TollGate advertisement" but validation happens later in the caller, after the side effect.
  2. Every 30 s, forever, for gateways that never validate. upstream_detector/main.go:274 reports candidate gateways on a 30 s ticker; HandleGatewayConnected re-probes whenever gateway.Session == nil (in-memory map, wiped on every restart — the rc1 VM bounced the service a few times post-install, amplifying it). A gateway whose advertisement persistently fails validation is probed — and poked on :80 — every cycle, indefinitely.
  3. No gate, no expiry, no tracking. The workaround is unconditional: no config kill-switch, no added-in version, no owning issue, no removal condition. "TEMPORARY" exists only as a string in three places.

Live evidence (rc1, 2026-10-08)

The QEMU user-net gateway (10.0.2.2 = host loopback) happened to have mockgate (a TollGate mock from another lane) on :2121 and an unrelated socat JSON service on :80. The rc1 module, with no payment made and no valid advertisement accepted (mockgate's event is unsigned → ValidateAdvertisementFromBytes fails; the rejection logs at Debug so nothing shows at INFO), did this every 30 s:

📡 Reporting gateway to upstream session manager  gateway=10.0.2.2 interface=eth1 module=upstream_detector
🔍 Checking if gateway is a TollGate              gateway=10.0.2.2 module=upstream_session_manager
🚨 TEMPORARY: Triggering captive portal session for ndsctl  gateway_ip=10.0.2.2 url=http://10.0.2.2:80/ purpose=trigger_ndsctl_session protocol_note="TEMPORARY WORKAROUND - not part of TollGate protocol"
✅ Captive portal request completed - ndsctl session should be triggered  gateway_ip=10.0.2.2 status_code=401 content_type=application/json
✅ Gateway responded - validating TollGate advertisement   gateway=10.0.2.2 module=upstream_session_manager
   (validation fails → nothing at INFO → next tick repeats)

So a deployed router makes repeated, browser-spoofed, unauthenticated requests to whatever answers on its upstream gateway's port 80 — including gateways it has already rejected as non-TollGates. On a real captive-portal upstream that is session-probe spam against a third-party box; on hostile upstreams it's a scripted, predictable, self-identifying request pattern ("compatible; TollGate-Client") from a payment device.

Ask

  1. Gate the trigger behind successful validation — fire only after ValidateAdvertisementFromBytes accepts the advertisement (move the call to getUpstreamAdvertisement's success path / session bootstrap), so non-TollGates never get poked.
  2. Fire once per gateway adoption, not per poll — skip when a session is active; consider persisting the gateway-decision so restarts don't re-run discovery side effects (AGENTS.md already flags process-memory state as a known gap class).
  3. Make the workaround auditable: config kill-switch (default off until an upstream needs it), and a registry (single source of truth) of every TEMPORARY WORKAROUND marker with owning issue, added-in version, removal condition, and review-by — plus a contract test that fails the build when a marker exists unregistered (fail fast on workaround debt).

I have a worktree in progress with (1), the unit tests for the misfire class, and (3); will link the PR here.

主要言語
Go
スター
12
フォーク
14
平均マージ
3日 22時間
マージ済み PR(30日)
262

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

OpenTollGate/tollgate-module-basic-go のほかの issue

OpenTollGate/tollgate-module-basic-go の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。