# caddy-opnsense-blocker `caddy-opnsense-blocker` is a local-first daemon that ingests Caddy access logs in their default JSON format, evaluates suspicious requests, keeps persistent local state in SQLite, provides a lightweight web UI for review, and blocks or unblocks IP addresses through an OPNsense alias. ## Features - Real-time ingestion of multiple Caddy JSON log files. - One heuristic profile per log source. - Persistent local state in SQLite. - Local-only web UI for reviewing events and IPs. - Manual block, unblock, and override reset actions. - OPNsense alias backend with automatic alias creation. - Concurrent polling across multiple log files. ## Current scope This first version is intentionally strong on ingestion, persistence, UI, and OPNsense integration. The decision engine is deliberately simple and deterministic for now: - suspicious path prefixes - unexpected `POST` requests - `.php` path detection - explicit known-agent allow/deny rules - excluded CIDR ranges - manual overrides This keeps the application usable immediately while leaving room for a more advanced network-intelligence engine later. ## Architecture - `internal/caddylog`: parses default Caddy JSON access logs - `internal/engine`: evaluates requests against a profile - `internal/store`: persists events, IP state, manual decisions, backend actions, and source offsets - `internal/opnsense`: manages the target OPNsense alias through its API - `internal/service`: runs concurrent log followers and applies automatic decisions - `internal/web`: serves the local review UI and JSON API ## Quick start 1. Generate or provision OPNsense API credentials. 2. Copy `config.example.yaml` to `config.yaml` and adapt it. 3. Start the daemon: ```bash CGO_ENABLED=0 go run ./cmd/caddy-opnsense-blocker -config ./config.yaml ``` 4. Open the local UI on the configured address, for example `http://127.0.0.1:9080`. ## Example configuration See `config.example.yaml`. Important points: - Each source points to one Caddy log file. - Each source references exactly one profile. - `initial_position: end` means “start following new lines only” on first boot. - The web UI should stay bound to a local address such as `127.0.0.1:9080`. ## Web UI and API The web UI is intentionally small and server-rendered. It refreshes through lightweight JSON polling and exposes these endpoints: - `GET /healthz` - `GET /api/overview` - `GET /api/events` - `GET /api/ips` - `GET /api/ips/{ip}` - `POST /api/ips/{ip}/block` - `POST /api/ips/{ip}/unblock` - `POST /api/ips/{ip}/reset` ## Development Run the test suite: ```bash CGO_ENABLED=0 go test ./... ``` Build the daemon: ```bash CGO_ENABLED=0 go build ./cmd/caddy-opnsense-blocker ``` `CGO_ENABLED=0` is useful on systems without a C toolchain. The application itself only relies on pure-Go dependencies. ## Roadmap - richer decision engine - asynchronous DNS / RDAP / ASN enrichment - richer review filters in the UI - alternative blocking backends besides OPNsense - direct streaming ingestion targets in addition to file polling