Jul 22 - Update protect admin page with fail2ban
This commit is contained in:
@@ -179,3 +179,52 @@ Then visit `https://your-domain/admin`, sign in, and manage content.
|
||||
- `SECRET_KEY` must be stable and secret; changing it logs everyone out.
|
||||
- The admin routes live under `/admin`; the public page and `schema.sql` SQL
|
||||
workflow above still work unchanged.
|
||||
|
||||
---
|
||||
|
||||
## Brute-force protection (fail2ban)
|
||||
|
||||
Every login attempt is written to `logs/auth.log` (rotating, 1 MB × 5) with the
|
||||
real client IP:
|
||||
|
||||
```
|
||||
2026-07-22 12:00:00,123 jqc.auth WARNING FAILED LOGIN user=admin from 203.0.113.5
|
||||
2026-07-22 12:00:05,456 jqc.auth INFO LOGIN OK user=admin from 203.0.113.5
|
||||
```
|
||||
|
||||
The real IP comes from `ProxyFix` reading nginx's `X-Forwarded-For` (gunicorn
|
||||
binds 127.0.0.1, so the header can't be spoofed from outside). Config ships in
|
||||
`deploy/fail2ban/`.
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y fail2ban
|
||||
sudo cp deploy/fail2ban/filter.d/jqc-admin.conf /etc/fail2ban/filter.d/
|
||||
sudo cp deploy/fail2ban/jail.d/jqc-admin.local /etc/fail2ban/jail.d/
|
||||
# edit logpath in the jail file if your app dir differs from /home/jqc/jqc_features
|
||||
sudo systemctl enable --now fail2ban
|
||||
sudo systemctl restart fail2ban
|
||||
```
|
||||
|
||||
Default policy: **5 failures in 10 min → 1 h ban** (`maxretry`/`findtime`/`bantime`
|
||||
in the jail file).
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
# regex matches the log lines:
|
||||
sudo fail2ban-regex logs/auth.log deploy/fail2ban/filter.d/jqc-admin.conf
|
||||
# jail is live:
|
||||
sudo fail2ban-client status jqc-admin
|
||||
```
|
||||
|
||||
`fail2ban-regex` should report matches equal to the number of `FAILED LOGIN`
|
||||
lines. `status` shows currently banned IPs.
|
||||
|
||||
### Scope note
|
||||
|
||||
This jail bans credential-guessing that reaches the password check (a real
|
||||
browser session with a valid CSRF token). Dumb bots that POST without a CSRF
|
||||
token get an HTTP 400 and never reach the check — they can't guess a password
|
||||
anyway. To also throttle those, add an nginx `limit_req` on `/admin/login`.
|
||||
|
||||
Reference in New Issue
Block a user