check / check (push) Waiting to run
The IPv6 group that is one client, the size of the table of clients and the level of the process's own lines become settings. clientGroup reads the group length from them, so limits, bans, history, lookups, AbuseIPDB scores and per-client anomaly counters all follow it; ratelimit.New takes the table size; the process logger takes the level once the settings are read, and request lines, written apart from it, are never held back. Judgement call: SWWAF_IPV6_GROUP_PREFIX accepts 32 to 128, the issue's example range. Model: opus-5-5
72 lines
2.4 KiB
Go
72 lines
2.4 KiB
Go
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, in the lookup
|
|
// database or through GeoJS, 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. The lookup database
|
|
// answers at once. With GeoJS, while a setting needs the answer, such as a
|
|
// country list or a biased threshold, 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
|
|
}
|
|
|
|
if rq.h.config.LookupSource == "file" {
|
|
rq.lookupAnswer = rq.h.lookupFile.LookUp(rq.h.clientGroup(rq.client))
|
|
} else {
|
|
rq.lookupAnswer = rq.h.geojs.LookUp(ctx, rq.h.clientGroup(rq.client))
|
|
}
|
|
|
|
rq.lookedUp = true
|
|
rq.line.ASN = rq.lookupAnswer.ASN
|
|
rq.line.ASName = rq.lookupAnswer.ASName
|
|
rq.line.Country = rq.lookupAnswer.Country
|
|
}
|
|
|
|
// addLookup adds answer, an answer about a client from the lookup
|
|
// database or GeoJS, 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()
|
|
}
|