feat: initial scaffold of CrowdSec admin webapp
All checks were successful
Deploy / deploy (push) Successful in 39s
All checks were successful
Deploy / deploy (push) Successful in 39s
Flask + htmx mini-app to list and delete CrowdSec decisions from a browser, gated behind Authentik. Talks to host LAPI via host.docker.internal:8080 using machine JWT auth (bouncer X-Api-Key is read-only). Gitea Actions CI/CD on push to main: runner rebuilds image and brings the stack up via docker compose on the host (same pattern as flight-radar). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
74
README.md
Normal file
74
README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# crowdsec-admin
|
||||
|
||||
Tiny Flask + htmx webapp to list and delete CrowdSec decisions (bans) from a browser, gated behind Authentik.
|
||||
|
||||
Built so the admin can unban an IP without SSH when their own network routes change. Source of truth: this Gitea repo. Push to `main` → Gitea Actions runner rebuilds and redeploys via `docker compose` on the host.
|
||||
|
||||
## Routes
|
||||
|
||||
- `GET /` — UI
|
||||
- `GET /decisions` — list active decisions (htmx fragment), optional `?ip=`
|
||||
- `POST /unban` — delete by decision `id` or by `ip`
|
||||
- `POST /unban-me` — delete decisions for the caller's IP (uses `X-Forwarded-For`)
|
||||
- `GET /healthz` — JSON, returns 503 if LAPI unreachable
|
||||
|
||||
## How it talks to CrowdSec
|
||||
|
||||
LAPI lives on the host at `0.0.0.0:8080`. Container reaches it via `host.docker.internal:host-gateway`.
|
||||
|
||||
LAPI `DELETE /v1/decisions` requires **machine JWT** (bouncers are read-only). App registers as machine via `cscli machines add`, logs in to `/v1/watchers/login`, caches the JWT ~13 minutes, and refreshes on 401.
|
||||
|
||||
## One-time setup
|
||||
|
||||
1. **Register the machine on the host** (needs sudo, choose a strong password and save it in `secrets.yml`):
|
||||
|
||||
```bash
|
||||
sudo cscli machines add crowdsec-admin --password '<PW>'
|
||||
```
|
||||
|
||||
2. **Gitea repo secrets** (Settings → Secrets and variables → Actions):
|
||||
|
||||
- `LAPI_MACHINE_ID=crowdsec-admin`
|
||||
- `LAPI_MACHINE_PASSWORD=<PW from step 1>`
|
||||
|
||||
3. **DNS**: `crowdsec.domverse-berlin.eu` → host IP.
|
||||
|
||||
4. **Authentik**: nothing — the existing wildcard `forward_domain` provider covers `*.domverse-berlin.eu`, and the `authentik@docker` label in `docker-compose.yml` gates the router.
|
||||
|
||||
5. **First deploy**: push to `main` or trigger `workflow_dispatch`. Runner builds the image, brings the stack up, and reports health.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
crowdsec-admin/
|
||||
├── .gitea/workflows/deploy.yml # CI/CD on push to main
|
||||
├── docker-compose.yml # Traefik labels + Kuma + authentik@docker
|
||||
├── app/
|
||||
│ ├── Dockerfile # python:3.12-slim + gunicorn
|
||||
│ ├── requirements.txt
|
||||
│ ├── app.py # Flask, ~120 LOC
|
||||
│ └── templates/
|
||||
│ ├── index.html # full page
|
||||
│ ├── _decisions.html # htmx fragment
|
||||
│ └── _unban_me.html # htmx fragment
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Local dev
|
||||
|
||||
```bash
|
||||
docker compose up --build -d
|
||||
# or, on the host:
|
||||
LAPI_URL=http://localhost:8080 LAPI_MACHINE_ID=... LAPI_MACHINE_PASSWORD=... \
|
||||
python -m flask --app app/app.py run --port 8000
|
||||
```
|
||||
|
||||
## Security notes
|
||||
|
||||
- Authentik is the only auth gate. Admin compromise = unban anything.
|
||||
- App never `exec`s shell. Only LAPI HTTP. IP inputs validated with `ipaddress.ip_address`. Decision-id input validated `isdigit()`.
|
||||
- `TRUSTED_PROXY_HOPS=1` picks the IP Traefik saw. Increase if a second proxy sits in front.
|
||||
|
||||
## Pattern note
|
||||
|
||||
This stack is **not Portainer-managed** — it follows the same convention as the `flight-radar` repo: source in Gitea, runner brings the stack up on the host via `docker compose`. Other (non-CI) stacks are deployed via Portainer API per the infra rule.
|
||||
Reference in New Issue
Block a user