Files
crowdsec-dashy/CROWDSEC_DASHY_PLAN.md
2026-05-17 04:54:34 +00:00

11 KiB

CrowdSec Dashy — Project Plan

Pure Go · No NPM · Tailwind CDN · Zero third-party Go packages (except SQLite if needed)


1. Architecture Overview

Data Access — Two Layers

Layer What it covers Auth
CrowdSec LAPI (REST, net/http) Decisions CRUD, Alerts read/delete, health check Machine login + password → JWT
cscli exec (os/exec) Bouncers, Machines, Hub (collections/parsers/scenarios), Metrics Binary path (mounted volume in Docker)

Critical finding: cscli bouncers add/delete/list, machines add/delete/list, and all hub commands hit the database directly and are not exposed through the REST API. Any Docker deployment must bind-mount the cscli binary or the app must gracefully degrade those sections.

Deployment Modes

Mode A — Sidecar Docker (recommended)
  crowdsec container  ←→  crowdsec-dashy container
  - shared Docker network (LAPI at http://crowdsec:8080)
  - /usr/local/bin/cscli bind-mounted read-only from host
  - CROWDSEC_API_LOGIN / PASSWORD as machine credentials

Mode B — Host binary
  - Direct localhost:8080 LAPI
  - cscli already on PATH

No SQLite needed

All state lives in CrowdSec's own database. No local DB required.


2. Environment Variables

PORT=:8080                              # UI listen address
CROWDSEC_API_URL=http://localhost:8080  # LAPI base URL
CROWDSEC_API_LOGIN=crowdsec-dashy          # machine login (create with cscli machines add)
CROWDSEC_API_PASSWORD=changeme          # machine password
CSCLI_PATH=/usr/local/bin/cscli        # path to cscli binary
UI_USERNAME=admin                       # basic-auth for the UI itself
UI_PASSWORD=changeme                    # basic-auth for the UI itself
UI_SESSION_SECRET=random32chars         # HMAC session signing key
POLL_INTERVAL_SEC=15                    # dashboard live-poll interval

3. Project Structure

crowdsec-dashy/
├── go.mod                                (module crowdsec-dashy, go 1.22)
├── Dockerfile
├── docker-compose.yml
│
├── cmd/server/
│   └── main.go                           entrypoint, env config, server init
│
├── internal/
│   ├── config/
│   │   └── config.go                     env-var loading, validation
│   │
│   ├── crowdsec/
│   │   ├── types.go                      Decision, Alert, Bouncer, Machine, Hub*, Metrics structs
│   │   ├── lapi.go                       REST client: login, decisions CRUD, alerts, health
│   │   └── cli.go                        cscli wrapper: bouncers, machines, hub, metrics
│   │
│   ├── handlers/
│   │   ├── renderer.go                   template engine + PageData (sidebar nav)
│   │   ├── dashboard.go                  GET /
│   │   ├── decisions.go                  GET/POST /decisions, POST /decisions/delete
│   │   ├── alerts.go                     GET /alerts, POST /alerts/delete
│   │   ├── bouncers.go                   GET/POST /bouncers
│   │   ├── machines.go                   GET/POST /machines
│   │   ├── hub.go                        GET /hub, POST /hub/install, POST /hub/remove
│   │   ├── metrics.go                    GET /metrics-ui
│   │   └── api.go                        JSON API: /api/v1/stats, /api/v1/decisions, etc.
│   │
│   ├── middleware/
│   │   └── middleware.go                 BasicAuth, SecureHeaders, Logger, Recovery
│   │
│   └── router/
│       └── router.go                     route wiring
│
└── web/
    ├── templates/
    │   ├── layouts/
    │   │   └── base.html                 sidebar shell (not top-nav)
    │   └── pages/
    │       ├── dashboard.html
    │       ├── decisions.html
    │       ├── alerts.html
    │       ├── bouncers.html
    │       ├── machines.html
    │       ├── hub.html
    │       ├── metrics.html
    │       └── error.html
    └── static/
        ├── css/app.css                   component classes, sidebar, badges, tables
        └── js/
            ├── app.js                    global: sidebar toggle, flash dismiss
            ├── dashboard.js              SSE/polling for live stats + sparklines
            └── tables.js                client-side filter/sort, confirm-delete modal

4. Pages & Features

4.1 Dashboard (/)

  • Stat cards: Active Bans, Alerts (24h), Bouncers connected, Machines registered
  • Live-update: JS polls /api/v1/stats every N seconds, updates counters in-place
  • Recent activity: Last 10 alerts table (scenario, IP, date, origin)
  • Recent bans: Last 10 decisions table
  • CrowdSec version + health badge (green/red dot)

4.2 Decisions (/decisions)

  • Table: IP/Range, Type (ban/captcha), Scenario, Origin, Duration, Expires At
  • Filters: type, origin, scope (IP / Range / Country)
  • Actions: Delete individual (POST /decisions/delete/{id}), Bulk delete (checked rows)
  • Add Decision form: IP, scope, type, duration (PRG pattern)
  • Pagination (server-side, offset/limit passed to LAPI)

4.3 Alerts (/alerts)

  • Table: ID, Scenario, IP, Country, ASN, Date, Decisions count
  • Click row → detail drawer (all events in alert, full metadata)
  • Delete individual alert, bulk delete selected
  • Filter by scenario, IP, date range (client-side JS)

4.4 Bouncers (/bouncers)

  • Table: Name, IP, Last Seen, Version, Type, Valid
  • Add bouncer (runs cscli bouncers add <name>, shows generated API key once — modal)
  • Delete bouncer (POST with confirm)
  • Status badge: green (valid) / red (revoked)
  • Graceful degradation: if cscli unavailable, show inline warning banner

4.5 Machines (/machines)

  • Table: Name, IP, Last Update, Version, Auth Type, Status, Last Heartbeat
  • Add machine (cscli machines add)
  • Validate pending machine (cscli machines validate)
  • Delete machine
  • Graceful degradation: cscli required

4.6 Hub (/hub)

  • Tabbed view: Collections | Parsers | Scenarios | Postoverflows
  • Each tab: list installed (with version), list available to install
  • Install / Remove actions → POST → runs cscli hub install/remove
  • Update all button (cscli hub update && cscli hub upgrade)
  • Graceful degradation: cscli required

4.7 Metrics (/metrics-ui)

  • Parsed output of cscli metrics (acquisition, parsers, scenarios, LAPI)
  • Tables grouped by category: Acquisition Sources, Parser Stats, Scenario Stats, LAPI Stats
  • Auto-refresh toggle (polls every 30s)
  • Graceful degradation: cscli required

5. Internal API (/api/v1/...)

Used by frontend JS — all return JSON, all protected by same BasicAuth.

Method Path Description
GET /api/v1/stats Dashboard summary counts
GET /api/v1/decisions Paginated decisions (query: page, limit, type, scope)
DELETE /api/v1/decisions/{id} Delete one decision
GET /api/v1/alerts Paginated alerts
DELETE /api/v1/alerts/{id} Delete one alert
GET /api/v1/health LAPI health + cscli availability

6. Authentication

The UI sits behind HTTP Basic Auth (middleware.BasicAuth) — simple and works in Docker behind a reverse proxy with TLS.

The app itself authenticates to CrowdSec LAPI as a machine (not a bouncer) to get full read+write access to decisions and alerts.

Flow:

  1. On startup, lapi.Login() POSTs credentials to /v1/watchers/login → JWT
  2. JWT stored in memory, refreshed on 401 response
  3. All LAPI requests carry Authorization: Bearer <jwt>

7. UI Design

Aesthetic: Industrial/utilitarian dark — think terminal output meets ops dashboard. Not a generic admin panel.

  • Layout: Fixed left sidebar (240px), collapsible on mobile. Content area with header bar.
  • Palette: Near-black surface (#080b10), blue-grey panels (#0f1520), electric cyan accent (#00d4ff), threat-red (#ff3b3b), safe-green (#00e676)
  • Typography: JetBrains Mono for data/numbers/badges, IBM Plex Sans for labels/headings
  • Tables: Monospaced IP addresses, color-coded type badges (ban=red, captcha=amber, custom=blue)
  • Sidebar nav: Icon + label, active state left-border accent, subtle hover
  • Stat cards: Large mono number, label, delta indicator, thin accent top-border
  • No external chart libs: Sparklines drawn with inline SVG <polyline> from JS

8. Build Phases

Phase 1 — Foundation

  • go.mod, config, folder structure
  • crowdsec/types.go — all structs
  • crowdsec/lapi.go — HTTP client, login, decisions, alerts
  • crowdsec/cli.go — exec wrapper, JSON parsing for bouncers/machines/hub/metrics
  • middleware/middleware.go — BasicAuth + existing middleware
  • router/router.go skeleton
  • base.html — sidebar shell + Tailwind config

Phase 2 — Dashboard + API

  • handlers/api.go/api/v1/stats, /api/v1/health
  • handlers/dashboard.go + dashboard.html
  • static/js/dashboard.js — polling, counter animation, sparklines

Phase 3 — Decisions & Alerts

  • handlers/decisions.go + decisions.html
  • handlers/alerts.go + alerts.html
  • static/js/tables.js — filter, sort, bulk-select, confirm modal

Phase 4 — Bouncers & Machines

  • handlers/bouncers.go + bouncers.html
  • handlers/machines.go + machines.html
  • Graceful degradation UI pattern

Phase 5 — Hub & Metrics

  • handlers/hub.go + hub.html
  • handlers/metrics.go + metrics.html

Phase 6 — Docker + Polish

  • Dockerfile (multi-stage: build → scratch/alpine)
  • docker-compose.yml with bind-mount examples
  • Error page handler
  • README with setup instructions

9. Docker Compose Example

version: "3.9"
services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    volumes:
      - crowdsec-data:/var/lib/crowdsec/data
      - crowdsec-config:/etc/crowdsec
    networks:
      - cs-net

  crowdsec-dashy:
    build: .
    ports:
      - "8080:8080"
    environment:
      CROWDSEC_API_URL: http://crowdsec:8080
      CROWDSEC_API_LOGIN: crowdsec-dashy
      CROWDSEC_API_PASSWORD: changeme
      CSCLI_PATH: /usr/local/bin/cscli
      UI_USERNAME: admin
      UI_PASSWORD: changeme
    volumes:
      # Bind-mount cscli from the host for CLI features
      - /usr/local/bin/cscli:/usr/local/bin/cscli:ro
    networks:
      - cs-net
    depends_on:
      - crowdsec

networks:
  cs-net:

volumes:
  crowdsec-data:
  crowdsec-config:

10. Security Checklist

  • All routes behind BasicAuth middleware
  • LAPI credentials in env only, never in templates or logs
  • cscli exec: args passed as slice (no shell injection), strict allow-list of commands
  • All POST forms validate inputs server-side before exec/API call
  • PRG pattern on all state-changing POSTs
  • http.MaxBytesReader on all POST handlers
  • Security headers middleware (CSP, X-Frame-Options, etc.)
  • Server timeouts (Read/Write/Idle)
  • Template buffer pattern (render to bytes.Buffer first)
  • JWT token never logged
  • Generated bouncer API keys shown exactly once, never stored by the UI

11. go.mod

module crowdsec-dashy

go 1.22

Zero external Go dependencies. All CrowdSec communication via stdlib net/http + os/exec.