check / check (push) Failing after 8m17s
`smallwebwaf` with no command now prints the commands and exits with status 2. The image runs rproxy, or servewaf with SMALLWEBWAF_COMMAND=servewaf. The passing-on code, its settings and its log fields and metric go. internal/proxy keeps the checks, answers, reports and own endpoints; its tests run through the real rproxy. The documents describe the pair. Judgement call: clients.json keeps token_refused, since state files refuse unknown fields. Deviation: script/gitea-requests sets SWWAF_LOG_REQUEST_HEADERS, to label requests. Model: opus-5-5
79 lines
2.6 KiB
Go
79 lines
2.6 KiB
Go
package proxy
|
|
|
|
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 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; 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,
|
|
// 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()
|
|
}
|