check / check (push) Failing after 3m27s
Every setting of discriminator and rproxy is read through viper, in place of the os.LookupEnv function Main passed down. NewViper turns AutomaticEnv and AllowEmptyEnv on, so a setting that is set but empty reads as set. A setting may also be given as a file, the _FILE form, which rproxy's settings now have too; rproxy still writes nothing to disk. The settings are renamed. Discriminator's take the prefix SWW_WAF_, rproxy's take SWW_RPROXY_, and the shared SWW_COMMAND keeps SWW_. The Core Rule Set's settings take SWW_WAF_CORE_RULE_SET_ rather than SWW_WAF_WAF_. Every place a name appeared changed: the code, the viper keys, tests, documents, examples, the README's settings table and the image files. Model: deepseek-v4-pro
79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
package discriminator
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"sneak.berlin/go/smallwebwaf/internal/lookup"
|
|
)
|
|
|
|
// The headers in which an answer of allow gives rproxy the client's AS number
|
|
// and country, for the app, while SWW_WAF_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; 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
|
|
// SWW_WAF_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,
|
|
// unless refusedWhateverTheAnswer reports true. ctx is the request's own
|
|
// context.
|
|
func (rq *request) lookUp(ctx context.Context, now time.Time) {
|
|
if rq.h.config.LookupSource == "off" || !canBePlaced(rq.client) {
|
|
return
|
|
}
|
|
|
|
client := rq.h.clientGroup(rq.client)
|
|
|
|
switch {
|
|
case rq.h.config.LookupSource == "file":
|
|
rq.lookupAnswer = rq.h.lookupFile.LookUp(client)
|
|
case rq.refusedWhateverTheAnswer(now):
|
|
rq.lookupAnswer = rq.h.geojs.LookUpWithoutWaiting(ctx, client)
|
|
rq.skippedLookupWait = rq.lookupAnswer.Answered.IsZero()
|
|
default:
|
|
rq.lookupAnswer = rq.h.geojs.LookUp(ctx, 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 that give 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()
|
|
}
|