Country allow and deny lists, looked up through GeoJS (closes #44)
check / check (push) Failing after 3s
check / check (push) Failing after 3s
SWWAF_DENIED_COUNTRIES and SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES refuse a request with 403 before its body is read or rate-limited, logged as country_denied. internal/lookup asks GeoJS only while a list is set, 200 clients per request, one at a time, keeping answers 7 days. Failures, a redirect or an answer leaving an address out included, are logged without addresses; GeoJS is then left alone a second, doubling to five minutes. Private, loopback and link-local clients are never sent. Deviation, per the issue: no SWWAF_LOOKUP_SOURCE or SWWAF_LOOKUP_TIMEOUT; 403, not SWWAF_BAN_RESPONSE. Deviation: GeoJS's country endpoint, not geo.json. Judgement call: an IPv6 /64 is asked about by its first address; at most 10,000 clients wait. Judgement call: config.go lists the ISO 3166-1 codes; no widely used library holds them. Model: opus-5-5
This commit was merged in pull request #54.
This commit is contained in:
@@ -28,6 +28,8 @@ const (
|
||||
rateLimitPerMinute = "SWWAF_RATE_LIMIT_PER_MINUTE"
|
||||
rateLimitPerHour = "SWWAF_RATE_LIMIT_PER_HOUR"
|
||||
rateLimitPerDay = "SWWAF_RATE_LIMIT_PER_DAY"
|
||||
deniedCountries = "SWWAF_DENIED_COUNTRIES"
|
||||
allowedCountries = "SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES"
|
||||
)
|
||||
|
||||
// off switches a timeout, a size limit or a rate limit off.
|
||||
@@ -79,6 +81,8 @@ func TestDefaults(t *testing.T) {
|
||||
|
||||
wantNetblocks(t, cfg.TrustedProxies,
|
||||
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")
|
||||
wantCountries(t, deniedCountries, cfg.DeniedCountries)
|
||||
wantCountries(t, allowedCountries, cfg.ExclusivelyAllowedCountries)
|
||||
}
|
||||
|
||||
func TestValuesAsSet(t *testing.T) {
|
||||
@@ -97,6 +101,8 @@ func TestValuesAsSet(t *testing.T) {
|
||||
rateLimitPerMinute: "60",
|
||||
rateLimitPerHour: "600",
|
||||
rateLimitPerDay: "6000",
|
||||
deniedCountries: "cn, RU,kp,Xk",
|
||||
allowedCountries: "de",
|
||||
})
|
||||
|
||||
wantSettings(t, cfg, config.Config{
|
||||
@@ -117,6 +123,25 @@ func TestValuesAsSet(t *testing.T) {
|
||||
}
|
||||
|
||||
wantNetblocks(t, cfg.TrustedProxies, "192.0.2.1/32", "10.0.0.0/8", "2001:db8::/32")
|
||||
wantCountries(t, deniedCountries, cfg.DeniedCountries, "CN", "RU", "KP", "XK")
|
||||
wantCountries(t, allowedCountries, cfg.ExclusivelyAllowedCountries, "DE")
|
||||
}
|
||||
|
||||
func TestCodeOnBothCountryListsStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
deniedCountries: "cn,ru",
|
||||
allowedCountries: "de,RU",
|
||||
}.lookupEnv)
|
||||
if err == nil {
|
||||
t.Fatal("ru on both country lists was accepted")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(err.Error(), allowedCountries+": ") ||
|
||||
!strings.Contains(err.Error(), `"RU"`) {
|
||||
t.Errorf("error %q does not name %s and RU", err, allowedCountries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizesAndOff(t *testing.T) {
|
||||
@@ -198,6 +223,18 @@ func TestInvalidValueStopsTheStart(t *testing.T) {
|
||||
{rateLimitPerHour, "1.5"},
|
||||
{rateLimitPerDay, "-1"},
|
||||
{rateLimitPerDay, "lots"},
|
||||
{deniedCountries, "nk"},
|
||||
{deniedCountries, "kp,,ir"},
|
||||
{deniedCountries, "prk"},
|
||||
{deniedCountries, "408"},
|
||||
{deniedCountries, "k"},
|
||||
{deniedCountries, "eu"},
|
||||
{deniedCountries, "un"},
|
||||
{deniedCountries, "su"},
|
||||
{allowedCountries, "ac"},
|
||||
{allowedCountries, "uk"},
|
||||
{allowedCountries, "zz"},
|
||||
{allowedCountries, "de,germany"},
|
||||
} {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -245,6 +282,8 @@ func TestLogsEachSettingWithItsValue(t *testing.T) {
|
||||
rateLimitPerMinute: "1000",
|
||||
rateLimitPerHour: "10000",
|
||||
rateLimitPerDay: "50000",
|
||||
deniedCountries: "",
|
||||
allowedCountries: "",
|
||||
}
|
||||
if !maps.Equal(line.Settings, want) {
|
||||
t.Errorf("logged settings\n%v\nwant\n%v", line.Settings, want)
|
||||
@@ -282,3 +321,12 @@ func wantNetblocks(t *testing.T, got []netip.Prefix, want ...string) {
|
||||
t.Errorf("netblocks %v, want %v", gotText, want)
|
||||
}
|
||||
}
|
||||
|
||||
// wantCountries checks the list of countries the setting name gave.
|
||||
func wantCountries(t *testing.T, name string, got []string, want ...string) {
|
||||
t.Helper()
|
||||
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("%s gave %v, want %v", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user