What's New in v2.8

This release came out of one deployment's threat log: 18,405 rows, nearly all of them scanners asking for env files from a handful of addresses. Every request detected, every request recorded, every request answered — and nothing stopped. Full details are in CHANGELOG.md.

Every env-file probe is detected

/.env and /.env.production were detected. /sendgrid.env, /gemini.env and /laravel.env were not: they stopped matching in v2.4.0, when the LFI pattern was tightened to clear a report.env.pdf false positive. Prefixed env files are most of what a scanner asks for.

All three shapes match now, and the lookalikes still pass — a document called report.env.pdf is a PDF, /sendgrid.env is someone looking for your API keys. The detection corpus carries the probe list from that log, with the false-positive count unchanged.

An address that keeps attacking gets blocked

A scanner walking a list of paths trips detection thousands of times. Until now each of those requests was inspected, recorded and answered, one at a time, for hours. AutoBlock counts threats per address and blocks one that goes over the line:

1AutoBlock: sentinel.AutoBlockConfig{
2 Enabled: true,
3 Threats: 10, // within Window
4 Window: 10 * time.Minute,
5 Duration: 24 * time.Hour, // 0 blocks until someone lifts it
6 MinSeverity: sentinel.SeverityHigh,
7 ExemptIPs: []string{"203.0.113.0/24"},
8},
  • Behaviour, not reputation. IPReputation.AutoBlock acts on what a third party already knows about an address; this acts on what the address has just done to your application, which is the only thing that catches a scanner nobody has reported yet.
  • Counted across replicas. Counting runs through Config.Counters, so threats spread over instances add up to one total, and a block made anywhere is enforced everywhere.
  • Hard to turn against you. MinSeverity (default High) keeps a stream of low-severity oddities from accumulating into a block, ExemptIPs and the whitelist are never blocked, and each threat counts once by its own ID. Every automatic block is written to the audit log like a manual one.

It only sees what happens after you turn it on

Auto-block counts threats as they arrive. The addresses already in your threat log are a separate job — that is what the sweep below is for.

Blocking the backlog in one call

Going through a scan by hand is how a threat log with thousands of rows gets ignored instead of acted on. The IP Management page has a Block repeat offenders panel, and the same thing is an endpoint:

# Who would be blocked? Nothing is blocked by a dry run.
curl -X POST http://localhost:8080/sentinel/api/ip/block-offenders \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"min_threats": 5, "since": "168h", "dry_run": true}'
# Block them, for 24 hours.
curl -X POST http://localhost:8080/sentinel/api/ip/block-offenders \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"min_threats": 5, "since": "168h", "duration": "24h"}'

Whitelisted and already-blocked addresses are skipped, a run is capped at 1,000 addresses (truncated says when more remain), and the sweep is audited once with the addresses as evidence. The dashboard panel always previews before it blocks.

Stopping an env scan

If your threat log looks like the one above, this is the whole change:

1sentinel.Mount(r, nil, sentinel.Config{
2 // Refuse the probes rather than recording them. Run in ModeLog first
3 // and read the Threats page if you have not already.
4 WAF: sentinel.WAFConfig{Enabled: true, Mode: sentinel.ModeBlock},
5
6 // And stop answering the address at all once it is clearly scanning.
7 AutoBlock: sentinel.AutoBlockConfig{Enabled: true},
8})

Then clear the backlog once from the dashboard. New scanners are blocked about ten probes in, which for the log this came from is a few seconds rather than a few hours.

Upgrading

Nothing is required and no defaults changed: AutoBlock is off until you turn it on. The only behaviour change is that prefixed env probes are detected again, so if you serve a file whose name ends in .env — unusual, but /exports/data.env would qualify — exclude that route with WAF.ExcludeRoutes.


Built with by JB