Network lists: always allowed, exempt from rate limits, always refused (closes #19)
check / check (push) Successful in 3m47s
check / check (push) Successful in 3m47s
Adds SWWAF_ALLOW_NETS, SWWAF_RATE_LIMIT_EXEMPT_NETS and SWWAF_DENY_NETS, read like SWWAF_TRUSTED_PROXIES and empty by default, and checked against the client's own address before its country is looked up. A client in SWWAF_ALLOW_NETS skips the country lists and the rate limits and is not looked up. One in SWWAF_DENY_NETS is refused with 403, logged as denied and not counted. One in SWWAF_RATE_LIMIT_EXEMPT_NETS is neither counted nor refused by the rate limits. SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES now refuses a private, loopback or link-local client unless SWWAF_ALLOW_NETS lists it. Judgement call: an address in both SWWAF_ALLOW_NETS and SWWAF_DENY_NETS is let through. Judgement call: the size and time limits still apply to SWWAF_ALLOW_NETS. Model: opus-5-5
This commit was merged in pull request #66.
This commit is contained in:
+27
-12
@@ -103,29 +103,44 @@ func (h *handler) newRequest(w http.ResponseWriter, r *http.Request) *request {
|
||||
|
||||
// check is the one place where a request can be refused once its client
|
||||
// is known, before its body is read or anything reaches the app. It
|
||||
// returns nil to let the request through. The country lists come first,
|
||||
// and a request they refuse is not counted for the rate limits; then the
|
||||
// rate limits, so that every other request is counted, one refused for
|
||||
// its size too. ctx is the request's own context.
|
||||
// returns nil to let the request through. A client in SWWAF_ALLOW_NETS
|
||||
// skips every check but the size limit. For any other client,
|
||||
// SWWAF_DENY_NETS comes first, so that a client it refuses is not looked
|
||||
// up, and then the country lists; a request either refuses is not counted
|
||||
// for the rate limits. Then come the rate limits, unless the client is in
|
||||
// SWWAF_RATE_LIMIT_EXEMPT_NETS, so that every other request is counted,
|
||||
// one refused for its size too. ctx is the request's own context.
|
||||
func (rq *request) check(ctx context.Context) *refusal {
|
||||
if rq.countryDenied(ctx) {
|
||||
cfg := rq.h.config
|
||||
allowed := isInside(rq.client, cfg.AllowNets)
|
||||
|
||||
if !allowed && isInside(rq.client, cfg.DenyNets) {
|
||||
return &refusal{
|
||||
status: http.StatusForbidden,
|
||||
action: requestlog.ActionDenied,
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed && rq.countryDenied(ctx) {
|
||||
return &refusal{
|
||||
status: http.StatusForbidden,
|
||||
action: requestlog.ActionCountryDenied,
|
||||
}
|
||||
}
|
||||
|
||||
limitHit := rq.h.limiter.Count(clientGroup(rq.client), rq.start)
|
||||
if limitHit != "" {
|
||||
rq.line.LimitHit = limitHit
|
||||
if !allowed && !isInside(rq.client, cfg.RateLimitExemptNets) {
|
||||
limitHit := rq.h.limiter.Count(clientGroup(rq.client), rq.start)
|
||||
if limitHit != "" {
|
||||
rq.line.LimitHit = limitHit
|
||||
|
||||
return &refusal{
|
||||
status: http.StatusTooManyRequests,
|
||||
action: requestlog.ActionRateLimited,
|
||||
return &refusal{
|
||||
status: http.StatusTooManyRequests,
|
||||
action: requestlog.ActionRateLimited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxBytes := rq.h.config.RequestMaxBytes
|
||||
maxBytes := cfg.RequestMaxBytes
|
||||
if maxBytes > 0 && rq.in.ContentLength > maxBytes {
|
||||
return &refusal{
|
||||
status: http.StatusRequestEntityTooLarge,
|
||||
|
||||
Reference in New Issue
Block a user