64f4f3c5d421f3c6e7e89e122a4725ad85d04673
CrowdSec UI
A lightweight, dependency-free web dashboard for monitoring and managing CrowdSec.
Stack: Go 1.22 · html/template · Tailwind CSS (CDN) · Zero external Go packages
Features
| Feature | Transport | Requires |
|---|---|---|
| Decisions (list, add, delete) | LAPI REST | Machine credentials |
| Alerts (list, delete) | LAPI REST | Machine credentials |
| Live dashboard stats | LAPI REST (polled) | Machine credentials |
| Bouncers (list, add, delete) | cscli exec |
cscli binary |
| Machines (list, validate, delete) | cscli exec |
cscli binary |
| Hub (collections/parsers/scenarios) | cscli exec |
cscli binary |
| Metrics | cscli exec |
cscli binary |
All
cscli-backed features degrade gracefully with a banner if the binary is unavailable.
Quick Start
1. Register the UI as a CrowdSec machine
# On the host running CrowdSec:
sudo cscli machines add crowdsec-dashy --password your-password -a
# Or if CrowdSec is in Docker:
docker exec crowdsec cscli machines add crowdsec-dashy --password your-password -a
2. Configure environment variables
Copy or edit docker-compose.yml and set:
CROWDSEC_API_LOGIN: "crowdsec-dashy"
CROWDSEC_API_PASSWORD: "your-password" # must match step 1
UI_USERNAME: "admin"
UI_PASSWORD: "your-ui-password" # Basic Auth for the web UI
UI_SESSION_SECRET: "32-char-random-string"
3. Set up cscli bind-mount (for CLI features)
# If CrowdSec is in Docker, extract cscli:
docker cp crowdsec:/usr/local/bin/cscli /usr/local/bin/cscli
chmod +x /usr/local/bin/cscli
# The docker-compose.yml already bind-mounts it:
# - /usr/local/bin/cscli:/usr/local/bin/cscli:ro
4. Launch
docker compose up -d
Open http://localhost:8080 — browser will prompt for Basic Auth.
Running Directly (no Docker)
# Build
go build -o crowdsec-dashy ./cmd/server
# Set env vars
export CROWDSEC_API_URL="http://localhost:8080"
export CROWDSEC_API_LOGIN="crowdsec-dashy"
export CROWDSEC_API_PASSWORD="your-password"
export UI_USERNAME="admin"
export UI_PASSWORD="your-ui-password"
export UI_SESSION_SECRET="32-char-random-string"
# Run
./crowdsec-dashy
Environment Variables
| Variable | Default | Description |
|---|---|---|
PORT |
:8080 |
Listen address |
CROWDSEC_API_URL |
http://localhost:8080 |
CrowdSec LAPI base URL |
CROWDSEC_API_LOGIN |
(required) | Machine login (cscli machines add) |
CROWDSEC_API_PASSWORD |
(required) | Machine password |
CSCLI_PATH |
/usr/local/bin/cscli |
Path to cscli binary |
UI_USERNAME |
admin |
Basic Auth username |
UI_PASSWORD |
changeme |
Basic Auth password — change this |
UI_SESSION_SECRET |
(required, ≥32 chars) | HMAC signing key |
POLL_INTERVAL_SEC |
15 |
Dashboard live-poll interval |
Project Structure
crowdsec-dashy/
├── cmd/server/main.go entrypoint
├── internal/
│ ├── config/config.go env-var loading
│ ├── crowdsec/
│ │ ├── types.go all shared structs
│ │ ├── lapi.go REST client (decisions, alerts)
│ │ └── cli.go cscli exec wrapper
│ ├── handlers/
│ │ ├── renderer.go template engine + PageData
│ │ ├── dashboard.go
│ │ ├── decisions.go
│ │ ├── alerts.go
│ │ ├── bouncers.go
│ │ ├── machines.go
│ │ ├── hub.go
│ │ ├── metrics.go
│ │ └── api.go JSON API for dashboard polling
│ ├── middleware/middleware.go BasicAuth, Logger, SecureHeaders, Recovery
│ └── router/router.go route wiring
└── web/
├── templates/layouts/base.html sidebar shell
├── templates/pages/ one file per page
└── static/css/app.css all component styles
static/js/app.js global JS
static/js/dashboard.js stats polling
Adding a New Page
- Create
internal/handlers/mypage.gowith a handler struct - Create
web/templates/pages/mypage.htmlstarting with{{template "base" .}} - Add to
SidebarNavinrenderer.go - Add
mux.Handle("/mypage", handlers.NewMyPageHandler(deps))inrouter.go
Security Notes
- All routes protected by HTTP Basic Auth — put behind TLS (nginx/Caddy) in production
cscliargs passed as a slice (never through a shell); strict allow-list of subcommands- LAPI JWT stored in memory only, never logged
- Generated bouncer API keys shown exactly once, never stored by the UI
- Security headers on every response: CSP, X-Frame-Options, X-Content-Type-Options
- Server has explicit Read/Write/Idle timeouts
Dependencies
Go: zero external packages — stdlib only. Frontend: Tailwind CSS via CDN (CSS only, no JS framework), Google Fonts via CDN.
Languages
Go
56.6%
HTML
29.5%
CSS
8.9%
JavaScript
4.4%
Dockerfile
0.6%