Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
271aaafccc |
@@ -1625,17 +1625,19 @@ defaults judge it by what it does to your service.
|
|||||||
names, `SWWAF_BLOCKLIST_REFRESH` after it last fetched it or tried to, 24 hours
|
names, `SWWAF_BLOCKLIST_REFRESH` after it last fetched it or tried to, 24 hours
|
||||||
by default and never less than one, one list after another. Since
|
by default and never less than one, one list after another. Since
|
||||||
`reputation.json` keeps when each list was last tried, the fetch failed or not,
|
`reputation.json` keeps when each list was last tried, the fetch failed or not,
|
||||||
at start `smallwebwaf` fetches at once only a list it has never tried, and one
|
even one cut off as `smallwebwaf` stopped, whose request the server may have
|
||||||
it last tried that long ago; any other waits its turn, so that restarts do not
|
had, at start `smallwebwaf` fetches at once only a list it has never tried, and
|
||||||
fetch a list more often. A fetch fails when the server answers other than `200`,
|
one it last tried that long ago; any other waits its turn, so that restarts do
|
||||||
when it does not finish within a minute, when the list is longer than 16 MiB, or
|
not fetch a list more often. A fetch fails when the server answers other than
|
||||||
when a line of it is not an address or a netblock, or for
|
`200`, when it does not finish within a minute, when the list is longer than 16
|
||||||
|
MiB, or when a line of it is not an address or a netblock, or for
|
||||||
`SWWAF_ASN_LIMIT_PERCENT_URL`, not an AS number, `:` and a percentage. The copy
|
`SWWAF_ASN_LIMIT_PERCENT_URL`, not an AS number, `:` and a percentage. The copy
|
||||||
fetched before then stays in use, and the failure is counted, logged and raised
|
fetched before then stays in use, and the failure is counted, logged and raised
|
||||||
as a `source_failure` alert. The last good copy of each list is kept whole,
|
as a `source_failure` alert; a fetch cut off as `smallwebwaf` stops is not a
|
||||||
comment lines included, in `reputation.json` (see "State files" above), so that
|
failure. The last good copy of each list is kept whole, comment lines included,
|
||||||
a restart keeps it in use too. Each list is named by its URL, in the request
|
in `reputation.json` (see "State files" above), so that a restart keeps it in
|
||||||
log, the alerts and the metrics, so keep a secret out of it.
|
use too. Each list is named by its URL, in the request log, the alerts and the
|
||||||
|
metrics, so keep a secret out of it.
|
||||||
|
|
||||||
A client in `SWWAF_ALLOW_NETS` is not checked. Any other is checked by its own
|
A client in `SWWAF_ALLOW_NETS` is not checked. Any other is checked by its own
|
||||||
address after the country lists, and `SWWAF_BLOCKLIST_ACTION` says what is done
|
address after the country lists, and `SWWAF_BLOCKLIST_ACTION` says what is done
|
||||||
|
|||||||
@@ -252,13 +252,14 @@ func (l *Lists) Load(lists []List) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetchDue fetches each list that is due, one after another, and returns
|
// fetchDue fetches each list that is due, one after another, and returns
|
||||||
// when the next is due.
|
// when the next is due. Once ctx has ended, it starts none, since a fetch
|
||||||
|
// cut off is noted as a try.
|
||||||
func (l *Lists) fetchDue(ctx context.Context) time.Time {
|
func (l *Lists) fetchDue(ctx context.Context) time.Time {
|
||||||
var next time.Time
|
var next time.Time
|
||||||
|
|
||||||
for _, listURL := range l.URLs() {
|
for _, listURL := range l.URLs() {
|
||||||
due := l.due(listURL)
|
due := l.due(listURL)
|
||||||
if !l.params.Now().Before(due) {
|
if ctx.Err() == nil && !l.params.Now().Before(due) {
|
||||||
l.fetch(ctx, listURL)
|
l.fetch(ctx, listURL)
|
||||||
due = l.due(listURL)
|
due = l.due(listURL)
|
||||||
}
|
}
|
||||||
@@ -287,10 +288,11 @@ func (l *Lists) due(listURL string) time.Time {
|
|||||||
return last.Add(l.params.Refresh)
|
return last.Add(l.params.Refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch fetches the list at listURL. A good copy takes the place of the
|
// fetch fetches the list at listURL, and notes the try. A good copy takes
|
||||||
// one held. A failure leaves that in use, and is counted, logged and
|
// the place of the one held. A failure leaves that in use, and is counted,
|
||||||
// raised as a source_failure alert; a fetch cut off as ctx ends, as
|
// logged and raised as a source_failure alert. A fetch cut off as ctx
|
||||||
// smallwebwaf stops, is none.
|
// ends, as smallwebwaf stops, is no failure, but is still noted as a try,
|
||||||
|
// so that a restart waits for it: the server may have had its request.
|
||||||
func (l *Lists) fetch(ctx context.Context, listURL string) {
|
func (l *Lists) fetch(ctx context.Context, listURL string) {
|
||||||
lines, err := l.get(ctx, listURL)
|
lines, err := l.get(ctx, listURL)
|
||||||
|
|
||||||
@@ -299,10 +301,7 @@ func (l *Lists) fetch(ctx context.Context, listURL string) {
|
|||||||
found, err = l.parse(listURL, lines)
|
found, err = l.parse(listURL, lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
cutOff := err != nil && ctx.Err() != nil
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
now := l.params.Now()
|
now := l.params.Now()
|
||||||
|
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
@@ -313,12 +312,16 @@ func (l *Lists) fetch(ctx context.Context, listURL string) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
held.kept.Fetched, held.kept.Lines = now, lines
|
held.kept.Fetched, held.kept.Lines = now, lines
|
||||||
held.entries = found
|
held.entries = found
|
||||||
} else {
|
} else if !cutOff {
|
||||||
held.failures++
|
held.failures++
|
||||||
}
|
}
|
||||||
|
|
||||||
l.mu.Unlock()
|
l.mu.Unlock()
|
||||||
|
|
||||||
|
if cutOff {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
const failed = "fetching a list failed"
|
const failed = "fetching a list failed"
|
||||||
|
|
||||||
|
|||||||
@@ -326,13 +326,14 @@ func TestRestartWaitsRefreshAfterTheLastTryEvenOneThatFailed(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFetchCutOffAsItStopsIsNoFailure(t *testing.T) {
|
func TestFetchCutOffAsItStopsIsNoFailureButARestartWaitsForIt(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
synctest.Test(t, func(t *testing.T) {
|
synctest.Test(t, func(t *testing.T) {
|
||||||
|
// Stopped 30 seconds into the fetch of drop.txt, before tor.txt's.
|
||||||
servers := &standIn{lists: map[string]string{}, hanging: true}
|
servers := &standIn{lists: map[string]string{}, hanging: true}
|
||||||
queue := newQueue()
|
queue := newQueue()
|
||||||
p := params(dropURL)
|
p := params(dropURL, torURL)
|
||||||
p.Alerts = queue
|
p.Alerts = queue
|
||||||
|
|
||||||
lists := reputation.New(p)
|
lists := reputation.New(p)
|
||||||
@@ -346,14 +347,43 @@ func TestFetchCutOffAsItStopsIsNoFailure(t *testing.T) {
|
|||||||
close(stopped)
|
close(stopped)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
synctest.Wait()
|
time.Sleep(30 * time.Second)
|
||||||
stop()
|
stop()
|
||||||
<-stopped
|
<-stopped
|
||||||
|
|
||||||
|
wantFetches(t, servers, 1)
|
||||||
|
|
||||||
if lists.Failures(dropURL) != 0 || len(waiting(queue)) != 0 {
|
if lists.Failures(dropURL) != 0 || len(waiting(queue)) != 0 {
|
||||||
t.Errorf("%d failures and alerts %+v, want none", lists.Failures(dropURL),
|
t.Errorf("%d failures and alerts %+v, want none", lists.Failures(dropURL),
|
||||||
waiting(queue))
|
waiting(queue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restarted an hour later with what reputation.json keeps, it fetches
|
||||||
|
// tor.txt, never tried, at once, and drop.txt a refresh after its
|
||||||
|
// cut-off try.
|
||||||
|
time.Sleep(time.Hour)
|
||||||
|
|
||||||
|
restarted := &standIn{lists: map[string]string{
|
||||||
|
dropURL: "203.0.113.7\n", torURL: "198.51.100.7\n",
|
||||||
|
}}
|
||||||
|
again := reputation.New(params(dropURL, torURL))
|
||||||
|
again.SetTransport(restarted)
|
||||||
|
|
||||||
|
err := again.Load(lists.Snapshot())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("load: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
run(t, again)
|
||||||
|
wantFetches(t, restarted, 1)
|
||||||
|
wantListedBy(t, again, "198.51.100.7", torURL)
|
||||||
|
|
||||||
|
time.Sleep(refresh - time.Hour - time.Nanosecond)
|
||||||
|
wantFetches(t, restarted, 1)
|
||||||
|
|
||||||
|
time.Sleep(time.Nanosecond)
|
||||||
|
wantFetches(t, restarted, 2)
|
||||||
|
wantListedBy(t, again, "203.0.113.7", dropURL)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user