163 lines
5.0 KiB
Markdown
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.
|