Network lists: always allowed, exempt from rate limits, always refused (closes #19)
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:
2026-10-06 02:36:27 +02:00
parent df4cf769e0
commit 7f6f89cd83
10 changed files with 350 additions and 55 deletions
+42 -1
View File
@@ -185,7 +185,7 @@ func TestCountryNotLookedUpWithoutAListOrForAPrivateAddress(t *testing.T) {
{"no country list is set", nil, []string{fromKP, fromDE}},
{
"private, loopback and link-local addresses",
map[string]string{allowedCountries: "de"},
map[string]string{deniedCountries: "kp"},
[]string{"10.0.0.5", "192.168.1.9", "fd00::5", "", "169.254.0.9", "fe80::9"},
},
} {
@@ -223,6 +223,47 @@ func TestCountryNotLookedUpWithoutAListOrForAPrivateAddress(t *testing.T) {
}
}
func TestExclusiveListRefusesAPrivateAddressUnlessAllowed(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
allowNets string
status int
action string
}{
{
"not in SWWAF_ALLOW_NETS", "",
http.StatusForbidden, requestlog.ActionCountryDenied,
},
{
"in SWWAF_ALLOW_NETS", "10.0.0.7,fd00::/8",
http.StatusOK, requestlog.ActionForward,
},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
app := startApp(t, func(http.ResponseWriter, *http.Request) {})
geojsURL, asked := startGeoJS(t)
addr, out := startProxyWithGeoJS(t, app.URL, geojsURL, map[string]string{
trustedProxies: trustLocalhost,
allowedCountries: "de",
allowNets: tc.allowNets,
})
wantAnswers(t, addr, out, []sentRequest{
{"10.0.0.7", tc.status, tc.action},
{"fd00::5", tc.status, tc.action},
})
if len(asked()) != 0 {
t.Errorf("GeoJS was asked about %v, want nothing", asked())
}
})
}
}
// startGeoJS starts a stand-in for GeoJS, which places fromDE and fromKP
// and no other address. It returns its URL, and what returns the
// addresses it has been asked about.