upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30s poll — port-80 pokes at gateways that aren't TollGates
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 20/100
調査の方向性
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 の本文から書いたものです。
説明
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) GETshttp://<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 aTODO: Remove this workaround once upstream TollGate implementations properly handle automatic session creation.- It is invoked from inside
ProbeGatewayWithContext(line 110-120) the moment the:2121probe returns any HTTP 200 with a non-empty body — i.e. beforeValidateAdvertisementFromBytesruns in the caller (upstream_session_manager.go:237-245).
Three defects follow:
- Fire-then-validate. Any HTTP service on the gateway's
:2121earns 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. - Every 30 s, forever, for gateways that never validate.
upstream_detector/main.go:274reports candidate gateways on a 30 s ticker;HandleGatewayConnectedre-probes whenevergateway.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. - 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
- Gate the trigger behind successful validation — fire only after
ValidateAdvertisementFromBytesaccepts the advertisement (move the call togetUpstreamAdvertisement's success path / session bootstrap), so non-TollGates never get poked. - 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).
- Make the workaround auditable: config kill-switch (default off until an upstream needs it), and a registry (single source of truth) of every
TEMPORARY WORKAROUNDmarker 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
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
OpenTollGate/tollgate-module-basic-go のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
OpenTollGate/tollgate-module-basic-go#1159 ·
メンテナーはふだん 1 日以内に返信
-
Degraded-mode hygiene follow-ups: dead walletNotInitializedError() + main.go's startup log still over-promises mint recovery対応中かも @Amperstrand が 3 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1135 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
setup_uhttpd_portal hardcodes the GUEST portal docroot to the TollGate path — the brand token cannot select a re-brand guest portal対応中かも @Amperstrand が 4 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1122 ·
メンテナーはふだん 1 日以内に返信
-
changelog: dependency-security sweep missing from [Unreleased] [Amperstrand fork #105 review debt]オープン
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1055 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
OpenTollGate/tollgate-module-basic-go#1027 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
OpenTollGate/tollgate-module-basic-go の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
kubeflow/semantic-operator#52 ·
メンテナーはふだん 10 日以内に返信
-
enhancement help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
PramodBasavarajMenasi/siftwise#52 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
dragonflydb/dragonfly-operator#619 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 2 日以内に返信
-
lazygit is not Rustオープン
難易度 1/5 1時間未満 初心者へのやさしさ 78/100