AS number and country looked up for every client (closes #95)
check / check (push) Waiting to run
check / check (push) Waiting to run
GeoJS's geo.json is asked about every new visitor unless SWWAF_LOOKUP_SOURCE is off. A request waits for its client's first answer only while a country list or SWWAF_ADD_LOOKUP_HEADERS needs it; otherwise the answer reaches the client's history and ban notes when it comes. The AS number and name go beside the country in the request log, history, ban notes, alerts and lookups.json, with metrics by AS number; 64512 counts as unknown. A client's own X-Client-ASN and X-Client-Country never reach the app, whatever the setting says, and make example-app sends no address to GeoJS. Judgement call: AS numbers are written AS64496, as SPEC's settings write them. Judgement call: SWWAF_LOOKUP_TIMEOUT is added, default 1s, and cannot be off. Model: opus-5-5
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
||||
"sneak.berlin/go/smallwebwaf/internal/lookup"
|
||||
)
|
||||
|
||||
// The headers in which the app is passed the client's AS number and
|
||||
// country while SWWAF_ADD_LOOKUP_HEADERS is set. Go writes every header
|
||||
// name in this form, as it sends it and as it receives it, so X-Client-ASN
|
||||
// arrives as X-Client-Asn, and Del removes a client's own whatever their
|
||||
// case; header names are not case-sensitive.
|
||||
const (
|
||||
asnHeader = "X-Client-Asn"
|
||||
countryHeader = "X-Client-Country"
|
||||
)
|
||||
|
||||
// lookUp looks up the client's AS number and country, and notes them for
|
||||
// the log line, unless SWWAF_LOOKUP_SOURCE is off or the client is on a
|
||||
// private, loopback or link-local address, which no lookup can place.
|
||||
// While a setting needs the answer, a new client's request waits for it.
|
||||
// ctx is the request's own context.
|
||||
func (rq *request) lookUp(ctx context.Context) {
|
||||
if rq.h.config.LookupSource == "off" || !canBePlaced(rq.client) {
|
||||
return
|
||||
}
|
||||
|
||||
answer := rq.h.geojs.LookUp(ctx, clientGroup(rq.client))
|
||||
rq.lookedUp = true
|
||||
rq.line.ASN = answer.ASN
|
||||
rq.line.ASName = answer.ASName
|
||||
rq.line.Country = answer.Country
|
||||
}
|
||||
|
||||
// addLookup adds answer, GeoJS's answer about a client, to the client's
|
||||
// history, and to the notes of the bans on its netblock that have no AS
|
||||
// number, AS name or country yet.
|
||||
func (h *handler) addLookup(answer lookup.Answer) {
|
||||
h.limiter.AddLookup(answer.Client, answer.Answered,
|
||||
answer.ASN, answer.ASName, answer.Country)
|
||||
h.ledger.AddLookup(h.netblock(answer.Client.Addr()),
|
||||
answer.ASN, answer.ASName, answer.Country)
|
||||
}
|
||||
|
||||
// setLookupHeaders sets the headers in which the app is passed the
|
||||
// client's AS number and country, leaving out one that is unknown.
|
||||
func setLookupHeaders(header http.Header, asn, country string) {
|
||||
if asn != "" {
|
||||
header.Set(asnHeader, asn)
|
||||
}
|
||||
|
||||
if country != "" {
|
||||
header.Set(countryHeader, country)
|
||||
}
|
||||
}
|
||||
|
||||
// canBePlaced reports whether a lookup can place addr: private, loopback
|
||||
// and link-local addresses have no AS number or country.
|
||||
func canBePlaced(addr netip.Addr) bool {
|
||||
return !addr.IsPrivate() && !addr.IsLoopback() && !addr.IsLinkLocalUnicast()
|
||||
}
|
||||
Reference in New Issue
Block a user