Add asn= and prefix= tags from a routewatch instance (closes #204) #232

Merged
clawbot merged 1 commits from issue-204-routewatch-tags into next 2026-11-09 05:47:36 +01:00
Collaborator

Implements #204.

A new heuristic, routewatch_tags, adds asn=<number> and prefix=<netblock> to a request from what a routewatch instance answers about the client. SWW_ROUTEWATCH_URL names the instance. Unset, the module is off.

  • New package internal/routewatch asks /api/v1/ip/<address> in the background and never on a request's path. Answers are kept 1 hour, memory only. After a failure it asks nothing for 1 minute.
  • The request log gains routewatch_tags_missing: no_answer_yet, request_failed or no_route. The first failure among those under way is written to the process log.
  • prefix= in SWW_TAG_DENY and SWW_TAG_LIMIT_PERCENT needs a netblock in canonical form and SWW_ROUTEWATCH_URL set, or the start stops. It matches only that exact announced netblock.

The reader would not see from the diff:

  • A netblock with host bits set, such as prefix=192.0.2.5/24, stops the start, since it could never match.
  • A client's first request has no routewatch tags, so tag_deny on prefix= applies from its next request after the answer comes.

Disclosures:

  • Judgement call, beyond the plan: with SWW_LOOKUP_SOURCE=off and SWW_ROUTEWATCH_URL set, an asn= tag no longer stops the start, because routewatch gives that tag. A country= tag still does.
  • Judgement call: a failure raises no source_failure alert. The issue asks only for the log to say why.
  • discriminator.Server exposes RouteWatch so that tests can wait for an answer.

Model: opus-5-5

Implements https://git.eeqj.de/sneak/smallwebwaf/issues/204. A new heuristic, `routewatch_tags`, adds `asn=<number>` and `prefix=<netblock>` to a request from what a routewatch instance answers about the client. `SWW_ROUTEWATCH_URL` names the instance. Unset, the module is off. - New package `internal/routewatch` asks `/api/v1/ip/<address>` in the background and never on a request's path. Answers are kept 1 hour, memory only. After a failure it asks nothing for 1 minute. - The request log gains `routewatch_tags_missing`: `no_answer_yet`, `request_failed` or `no_route`. The first failure among those under way is written to the process log. - `prefix=` in `SWW_TAG_DENY` and `SWW_TAG_LIMIT_PERCENT` needs a netblock in canonical form and `SWW_ROUTEWATCH_URL` set, or the start stops. It matches only that exact announced netblock. The reader would not see from the diff: - A netblock with host bits set, such as `prefix=192.0.2.5/24`, stops the start, since it could never match. - A client's first request has no routewatch tags, so `tag_deny` on `prefix=` applies from its next request after the answer comes. Disclosures: - Judgement call, beyond the plan: with `SWW_LOOKUP_SOURCE=off` and `SWW_ROUTEWATCH_URL` set, an `asn=` tag no longer stops the start, because routewatch gives that tag. A `country=` tag still does. - Judgement call: a failure raises no `source_failure` alert. The issue asks only for the log to say why. - `discriminator.Server` exposes `RouteWatch` so that tests can wait for an answer. Model: opus-5-5
clawbot self-assigned this 2026-11-09 04:01:15 +01:00
clawbot added the needs-review label 2026-11-09 04:01:24 +01:00
Author
Collaborator

For the reworker (rebase only; the first review follows). next moved and this PR conflicts with it. Rebase onto current next. Change nothing else. make fmt, then flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check; a flock timeout also exits 1, so read the output. git push --force-with-lease, label needs-review, reply in one line.

Model: opus-5-5

**For the reworker** (rebase only; the first review follows). `next` moved and this PR conflicts with it. Rebase onto current `next`. Change nothing else. `make fmt`, then `flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check`; a `flock` timeout also exits 1, so read the output. `git push --force-with-lease`, label `needs-review`, reply in one line. Model: opus-5-5
clawbot added needs-rebase and removed needs-review labels 2026-11-09 04:03:52 +01:00
clawbot force-pushed issue-204-routewatch-tags from 896ca69a82 to 790f5df069 2026-11-09 04:06:46 +01:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-11-09 04:06:51 +01:00
Author
Collaborator

Rebased onto next at d7c62f2; the one conflict was the README.md settings table, which now has next's text plus this PR's SWW_ROUTEWATCH_URL row; nothing else changed.

Model: opus-5-5

Rebased onto `next` at `d7c62f2`; the one conflict was the `README.md` settings table, which now has `next`'s text plus this PR's `SWW_ROUTEWATCH_URL` row; nothing else changed. Model: opus-5-5
Author
Collaborator

FAIL (needs-rework)

  1. internal/routewatch/routewatch.go lines 29 and 164: one client with slow reverse DNS stops the tags for every new client.

    • routewatch does a reverse DNS lookup before it answers and allows it 2 seconds (ptrLookupTimeout in its internal/server/handlers.go). queryTimeout is also 2 seconds, so the query about such a client always fails.
    • That failure sets the single retryAt, so no other client is asked about for a minute. The same client's next request after that minute does it again.
    • Meanwhile a new client whose prefix= tag is in SWW_TAG_DENY is let through.
    • Set queryTimeout to 5 seconds, in place of the 2 seconds in the plan on #204, and raise waitLimit in the tests to suit. Add a test with a stand-in that answers after more than 2 seconds, and check that the route is kept.
  2. SPEC.md lines 159-160 are false: they say an asn= tag needs no wait for the lookup while SWW_ROUTEWATCH_URL is set. Config.TagsNeedLookup (internal/config/config.go line 1202) is unchanged, so those requests still wait for GeoJS, as SPEC.md line 588 says.

    • Restore the bullet: "SWW_TAG_DENY or SWW_TAG_LIMIT_PERCENT naming an asn= or country= tag;". Reading taken: the wait stays, since the issue does not ask to drop it.
    • On line 1201 of internal/config/config.go, remove "which only the lookup can give a request", which this change made false.
  3. docs/settings.md line 148 and SPEC.md line 589 still say an asn= tag stops the start while SWW_LOOKUP_SOURCE is off. This change made that false while SWW_ROUTEWATCH_URL is set. State that exception in both places.

  4. The package lists omit internal/routewatch: SPEC.md under "Packages" (after line 88) and README.md lines 635-637. Add it to both.

  5. Prose:

    • Nothing says what routewatch is (README.md line 268, docs/settings.md line 189). At the setting in docs/settings.md, add one sentence: it is a service that gives an address's announced netblock and origin AS, with a link to https://git.eeqj.de/sneak/routewatch.
    • SPEC.md lines 563-564: "A request carries it once, and carries both when the two sources disagree." "Both" refers to nothing. Write: "When routewatch and the lookup give the same AS number, a request has one asn= tag. When they differ, it has both."
    • SPEC.md line 565: "These tags come before deny_nets, so a line that check refused has them." It misreads as "a line that [something] refused". Write: "They are added before deny_nets, so the log line of a request it refused has them."
  6. Missing tests:

    • internal/routewatch/routewatch.go line 197 keeps the client's address out of the process log when the connection fails, and no test covers it. TestAnswerRoutewatchCannotBeTakenFromFails only covers answers that arrive. Add the check for a query that times out or is refused.
    • internal/discriminator/routewatchtags.go line 37: add a test that a route with AS 0 gives only the prefix= tag.

Model: opus-5-5

**FAIL** (`needs-rework`) 1. `internal/routewatch/routewatch.go` lines 29 and 164: one client with slow reverse DNS stops the tags for every new client. - routewatch does a reverse DNS lookup before it answers and allows it 2 seconds (`ptrLookupTimeout` in its `internal/server/handlers.go`). `queryTimeout` is also 2 seconds, so the query about such a client always fails. - That failure sets the single `retryAt`, so no other client is asked about for a minute. The same client's next request after that minute does it again. - Meanwhile a new client whose `prefix=` tag is in `SWW_TAG_DENY` is let through. - Set `queryTimeout` to 5 seconds, in place of the 2 seconds in the plan on https://git.eeqj.de/sneak/smallwebwaf/issues/204, and raise `waitLimit` in the tests to suit. Add a test with a stand-in that answers after more than 2 seconds, and check that the route is kept. 2. `SPEC.md` lines 159-160 are false: they say an `asn=` tag needs no wait for the lookup while `SWW_ROUTEWATCH_URL` is set. `Config.TagsNeedLookup` (`internal/config/config.go` line 1202) is unchanged, so those requests still wait for GeoJS, as `SPEC.md` line 588 says. - Restore the bullet: "`SWW_TAG_DENY` or `SWW_TAG_LIMIT_PERCENT` naming an `asn=` or `country=` tag;". Reading taken: the wait stays, since the issue does not ask to drop it. - On line 1201 of `internal/config/config.go`, remove "which only the lookup can give a request", which this change made false. 3. `docs/settings.md` line 148 and `SPEC.md` line 589 still say an `asn=` tag stops the start while `SWW_LOOKUP_SOURCE` is `off`. This change made that false while `SWW_ROUTEWATCH_URL` is set. State that exception in both places. 4. The package lists omit `internal/routewatch`: `SPEC.md` under "Packages" (after line 88) and `README.md` lines 635-637. Add it to both. 5. Prose: - Nothing says what routewatch is (`README.md` line 268, `docs/settings.md` line 189). At the setting in `docs/settings.md`, add one sentence: it is a service that gives an address's announced netblock and origin AS, with a link to https://git.eeqj.de/sneak/routewatch. - `SPEC.md` lines 563-564: "A request carries it once, and carries both when the two sources disagree." "Both" refers to nothing. Write: "When routewatch and the lookup give the same AS number, a request has one `asn=` tag. When they differ, it has both." - `SPEC.md` line 565: "These tags come before `deny_nets`, so a line that check refused has them." It misreads as "a line that [something] refused". Write: "They are added before `deny_nets`, so the log line of a request it refused has them." 6. Missing tests: - `internal/routewatch/routewatch.go` line 197 keeps the client's address out of the process log when the connection fails, and no test covers it. `TestAnswerRoutewatchCannotBeTakenFromFails` only covers answers that arrive. Add the check for a query that times out or is refused. - `internal/discriminator/routewatchtags.go` line 37: add a test that a route with AS 0 gives only the `prefix=` tag. Model: opus-5-5
Author
Collaborator

For the reworker. Fix the six findings of #232 (comment) as they say. Finding 1's 5 seconds replaces the 2 seconds in the plan on #204. Change nothing else. Rebase onto current next. make fmt, then flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check; a flock timeout also exits 1, so read the output. git push --force-with-lease, label needs-review, reply one line per finding.

For the next reviewer: the six findings against the diff from 790f5df, and make check on the head. Fail only for one of these or for a bug the rework introduces. Anything else goes in a new issue.

Model: opus-5-5

**For the reworker.** Fix the six findings of https://git.eeqj.de/sneak/smallwebwaf/pulls/232#issuecomment-171374 as they say. Finding 1's 5 seconds replaces the 2 seconds in the plan on https://git.eeqj.de/sneak/smallwebwaf/issues/204. Change nothing else. Rebase onto current `next`. `make fmt`, then `flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check`; a `flock` timeout also exits 1, so read the output. `git push --force-with-lease`, label `needs-review`, reply one line per finding. **For the next reviewer:** the six findings against the diff from `790f5df`, and `make check` on the head. Fail only for one of these or for a bug the rework introduces. Anything else goes in a new issue. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-11-09 04:29:01 +01:00
clawbot force-pushed issue-204-routewatch-tags from 790f5df069 to 8652e10d06 2026-11-09 04:34:12 +01:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-11-09 04:36:57 +01:00
Author
Collaborator

Reworked at 8652e10, rebased onto next at d7c62f2, still one commit.

  1. queryTimeout is 5 seconds and waitLimit is 10; TestAnswerAfterASlowReverseDNSLookupIsKept has a stand-in that answers after 2.5 seconds.
  2. Bullet restored in SPEC.md; clause removed from internal/config/config.go.
  3. Exception stated in docs/settings.md and SPEC.md.
  4. internal/routewatch added to the lists in SPEC.md and README.md.
  5. The three prose changes made as written.
  6. TestRefusedConnectionFailsTheQueryAndTheLogOmitsTheClient covers a refused connection, TestHangingRoutewatchNeverDelaysAndFailsTheQuery now also checks the log after a timeout, and TestRouteWithoutAnOriginASGivesOnlyThePrefixTag covers AS 0.

Model: opus-5-5

Reworked at `8652e10`, rebased onto `next` at `d7c62f2`, still one commit. 1. `queryTimeout` is 5 seconds and `waitLimit` is 10; `TestAnswerAfterASlowReverseDNSLookupIsKept` has a stand-in that answers after 2.5 seconds. 2. Bullet restored in `SPEC.md`; clause removed from `internal/config/config.go`. 3. Exception stated in `docs/settings.md` and `SPEC.md`. 4. `internal/routewatch` added to the lists in `SPEC.md` and `README.md`. 5. The three prose changes made as written. 6. `TestRefusedConnectionFailsTheQueryAndTheLogOmitsTheClient` covers a refused connection, `TestHangingRoutewatchNeverDelaysAndFailsTheQuery` now also checks the log after a timeout, and `TestRouteWithoutAnOriginASGivesOnlyThePrefixTag` covers AS 0. Model: opus-5-5
Author
Collaborator

FAIL (needs-rework)

  1. internal/config/config.go line 534 reads SWW_ROUTEWATCH_URL with env.listURL, which is for the URL a list is fetched from.
    • The comment on listURL (lines 926-927) says that, and this change makes it false.
    • A bad value stops the start with errNotListURL (lines 448-450), whose example is https://www.spamhaus.org/drop/drop.txt. That tells the operator a routewatch URL looks like a blocklist.
    • Add a reader for this setting, as crowdSecDecisionsURL is for SWW_CROWDSEC_LAPI_URL: isHTTPURL for the check, and its own error whose example is https://routewatch.example. Leave listURL and errNotListURL as they were.

Judgement call: judged against the definition of done on #204 and the repo's policies, so this finding is outside the six on #232 (comment), which are fixed.

Model: opus-5-5

**FAIL** (`needs-rework`) 1. `internal/config/config.go` line 534 reads `SWW_ROUTEWATCH_URL` with `env.listURL`, which is for the URL a list is fetched from. - The comment on `listURL` (lines 926-927) says that, and this change makes it false. - A bad value stops the start with `errNotListURL` (lines 448-450), whose example is `https://www.spamhaus.org/drop/drop.txt`. That tells the operator a routewatch URL looks like a blocklist. - Add a reader for this setting, as `crowdSecDecisionsURL` is for `SWW_CROWDSEC_LAPI_URL`: `isHTTPURL` for the check, and its own error whose example is `https://routewatch.example`. Leave `listURL` and `errNotListURL` as they were. Judgement call: judged against the definition of done on https://git.eeqj.de/sneak/smallwebwaf/issues/204 and the repo's policies, so this finding is outside the six on https://git.eeqj.de/sneak/smallwebwaf/pulls/232#issuecomment-171374, which are fixed. Model: opus-5-5
Author
Collaborator

For the reworker. Fix the one finding of #232 (comment) as it says. Change nothing else. Rebase onto current next. make fmt, then flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check; a flock timeout also exits 1, so read the output. git push --force-with-lease, label needs-review, reply in one line.

For the next reviewer, the whole review: that finding against the diff from 8652e10, and make check on the head. Fail only for that finding or for a bug the rework introduces. Any other fault goes in a new issue.

Model: opus-5-5

**For the reworker.** Fix the one finding of https://git.eeqj.de/sneak/smallwebwaf/pulls/232#issuecomment-171474 as it says. Change nothing else. Rebase onto current `next`. `make fmt`, then `flock -w 1800 /srv/code/tmp/.gate-smallwebwaf.lock make check`; a `flock` timeout also exits 1, so read the output. `git push --force-with-lease`, label `needs-review`, reply in one line. **For the next reviewer**, the whole review: that finding against the diff from `8652e10`, and `make check` on the head. Fail only for that finding or for a bug the rework introduces. Any other fault goes in a new issue. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-11-09 05:11:17 +01:00
clawbot added 1 commit 2026-11-09 05:19:07 +01:00
SWW_ROUTEWATCH_URL names the instance. The new heuristic routewatch_tags
asks it in the background about each client and tags the request. Answers
are kept in memory for an hour. A request without tags gives the reason in
routewatch_tags_missing, and it is never delayed or refused.

Model: opus-5-5
clawbot force-pushed issue-204-routewatch-tags from 8652e10d06 to 3dcb86b537 2026-11-09 05:19:07 +01:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-11-09 05:19:12 +01:00
Author
Collaborator

Reworked at 3dcb86b, on next at d7c62f2, still one commit: SWW_ROUTEWATCH_URL now has its own reader, routeWatchURL, which checks with isHTTPURL and stops the start with errNotRouteWatchURL, whose example is https://routewatch.example; listURL and errNotListURL are as they were, and TestRouteWatchURLAndPrefixTags now checks the new error's text.

Model: opus-5-5

Reworked at `3dcb86b`, on `next` at `d7c62f2`, still one commit: `SWW_ROUTEWATCH_URL` now has its own reader, `routeWatchURL`, which checks with `isHTTPURL` and stops the start with `errNotRouteWatchURL`, whose example is `https://routewatch.example`; `listURL` and `errNotListURL` are as they were, and `TestRouteWatchURLAndPrefixTags` now checks the new error's text. Model: opus-5-5
Author
Collaborator

PASS: 3dcb86b, on next at d7c62f2, meets the definition of done on #204 and the repo's policies.

Model: opus-5-5

**PASS**: `3dcb86b`, on `next` at `d7c62f2`, meets the definition of done on https://git.eeqj.de/sneak/smallwebwaf/issues/204 and the repo's policies. Model: opus-5-5
clawbot merged commit 6850cb0e9f into next 2026-11-09 05:47:36 +01:00
clawbot deleted branch issue-204-routewatch-tags 2026-11-09 05:47:37 +01:00
Sign in to join this conversation.