Files

163 lines
5.0 KiB
Markdown

# CrowdSec UI
A lightweight, dependency-free web dashboard for monitoring and managing CrowdSec.
**Stack**: Go 1.26 · `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
```bash
# On the host running CrowdSec:
sudo cscli machines add crowdsec-dashy --password your-password -a -f /etc/crowdsec/dashy_credentials.yaml
# Or if CrowdSec is in Docker:
docker exec crowdsec cscli machines add crowdsec-dashy --password your-password -a -f /etc/crowdsec/dashy_credentials.yaml
```
### 2. Configure environment variables
Copy or edit `docker-compose.yml` and set:
```yaml
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)
```bash
# 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
```bash
docker compose up -d
```
Open `http://localhost:8080` — browser will prompt for Basic Auth.
---
## Running Directly (no Docker)
```bash
# 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
1. Create `internal/handlers/mypage.go` with a handler struct
2. Create `web/templates/pages/mypage.html` starting with `{{template "base" .}}`
3. Add to `SidebarNav` in `renderer.go`
4. Add `mux.Handle("/mypage", handlers.NewMyPageHandler(deps))` in `router.go`
---
## Security Notes
- All routes protected by HTTP Basic Auth — put behind TLS (nginx/Caddy) in production
- `cscli` args 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.