Sentinel WAF
Rate limit, AuthShield, Anomaly.
Sentinel is Grit's in-process Web Application Firewall (WAF). It does rate limiting, AuthShield (brute-force lockouts), Anomaly detection, and Geo blocking ā all in middleware that runs in sub-millisecond time. This lesson covers what it ships and how to configure it.
The Sentinel dashboard
Open http://localhost:8080/sentinel/ui. Log in with SENTINEL_USERNAME / SENTINEL_PASSWORD from your .env. You'll see:
- Live request rate + p95 latency
- Blocked requests (by reason: WAF rule, rate limit, AuthShield)
- Recent suspicious IPs
- Per-route rate-limit counters
What it intercepts
sentinel.MountE(r, db, sentinel.Config{Dashboard: sentinel.DashboardConfig{Username: cfg.SentinelUsername,Password: cfg.SentinelPassword,SecretKey: cfg.SentinelSecretKey,AllowInsecureDefaults: cfg.AppEnv != "production",},WAF: sentinel.WAFConfig{Enabled: true,Mode: sentinel.ModeBlock, // or ModeLog},RateLimit: sentinel.RateLimitConfig{Enabled: true,ByIP: &sentinel.Limit{Requests: 100, Window: time.Minute},ByRoute: map[string]sentinel.Limit{"/api/auth/login": {Requests: 5, Window: 15 * time.Minute},},},AuthShield: sentinel.AuthShieldConfig{Enabled: true,LoginRoute: "/api/auth/login",},Anomaly: sentinel.AnomalyConfig{Enabled: true},Geo: sentinel.GeoConfig{Enabled: true},})
WAF ā basic injection / scanner blocks
OWASP CRS-lite ruleset embedded. Blocks:
- SQLi probes (
' OR 1=1--, UNION SELECT, time-based) - XSS payloads in query / body
- Path traversal (
../../etc/passwd) - Dotfile probes (
.git,.env,.bak)
Two modes: ModeBlock (return 403 + record) or ModeLog (record but pass through ā useful during rollout).
Rate limit ā per-IP + per-route
The default config:
- 100 req/min per IP across all routes
- 5 req/15min per IP on
/api/auth/login - Custom rates per route via the
ByRoutemap
Exceeded ā 429 Too Many Requests. Counters live in Redis (shared across API replicas).
AuthShield ā account-level brute-force
After N failed logins for an email, the account enters a cool-down regardless of source IP. This defends against distributed credential-stuffing where every attempt comes from a different IP.
6 failed login attempts for alex@example.comā AuthShield locks the account for 15 minutesā Even valid password retries return "invalid credentials"ā Audit log records the lock eventā Sentinel dashboard shows it
Anomaly + Geo
- Anomaly: behavioural rules ā rapid 4xx bursts, suspicious user-agent strings, scanner signatures. Bots get throttled before they exhaust your rate-limit budget.
- Geo: country-level allow/block list. "Block everything outside the US" or "flag high-fraud-rate countries for manual review" ā both are config flags.
SENTINEL_TRUSTED_PROXIES to your LB's CIDR. Without it, the WAF sees every request as coming from the LB's IP ā your rate limit immediately exhausts.The dashboard credential gate
Sentinel refuses to mount in production with the default password sentinel. v3.25.1 of Grit fixes this by generating a random password at scaffold time, but if you somehow kept the default, the API won't start in APP_ENV=production. Set strong credentials or set DEMO_MODE=true for the public-demo carve-out.
Quick check
Try it
Trip the rate limit:
- Run 6 login attempts in quick succession with curl:
$for i in 1 2 3 4 5 6; do$ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:8080/api/auth/login \$ -H 'Content-Type: application/json' \$ -d '{"email":"alex@example.com","password":"wrong"}'$done
- You should see five 401s, then a 429.
- Open the Sentinel dashboard and look at the rate-limit page.
- Paste the curl output and a dashboard screenshot in
notes.md.
What's next
Security on. Now to see what's actually happening ā Pulse's p50/p95/p99 dashboards, per-route metrics, slow-query visibility.
Spot a typo? Have an idea?
Help us improve this lesson. One click opens a GitHub issue with the lesson URL pre-filled ā suggest clearer wording, report a bug, or request more depth. The course keeps improving thanks to learners like you.
Suggest an improvement on GitHub