# 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 with summary cards, a sortable “Recent IPs” view for the last 24 hours, bot badges, and a full request history for each selected address. - On-demand IP investigation with persistent caching for bot verification, reverse DNS, RDAP, and Spamhaus lookups. - Background IP investigation workers so missing cached intelligence appears without blocking page loads. - Manual block, unblock, and clear-override actions with OPNsense-aware UI state. - 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 policy engine later. ## Architecture - `internal/caddylog`: parses default Caddy JSON access logs - `internal/engine`: evaluates requests against a profile - `internal/investigation`: performs bot verification and IP enrichment - `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 ## License This project is licensed under the MIT License. See `LICENSE`. ## 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 `investigation` section controls how long IP enrichment is cached and whether on-demand Spamhaus lookups are enabled. - The investigation worker fills missing cached intelligence in the background so the dashboard stays fast while bot badges and cached intel keep filling in. Opening an IP details page reuses the cache; the `Refresh investigation` button is the manual override when you explicitly want a new lookup. - 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/recent-ips?hours=24` - `GET /api/ips/{ip}` - `POST /api/ips/{ip}/investigate` - `POST /api/ips/{ip}/block` - `POST /api/ips/{ip}/unblock` - `POST /api/ips/{ip}/clear-override` The legacy `POST /api/ips/{ip}/reset` endpoint is still accepted as a backwards-compatible alias for `clear-override`. ## 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. ## Nix packaging The repository ships with first-class Nix files: - `package.nix`: reusable package definition - `default.nix`: convenience entry point for `nix-build` - `module.nix`: reusable NixOS module Build the package directly from the repository root: ```bash nix-build ``` Use the NixOS module from another configuration: ```nix { imports = [ /path/to/caddy-opnsense-blocker/module.nix ]; services.caddy-opnsense-blocker = { enable = true; credentials.opnsenseApiKeyFile = "/run/secrets/opnsense-api-key"; credentials.opnsenseApiSecretFile = "/run/secrets/opnsense-api-secret"; settings = { opnsense = { enabled = true; base_url = "https://router.example.test"; ensure_alias = true; alias.name = "blocked-ips"; }; profiles.public-web = { auto_block = true; block_unexpected_posts = true; block_php_paths = true; suspicious_path_prefixes = [ "/wp-admin" "/wp-login.php" "/.env" ]; }; sources = [ { name = "public-web"; path = "/var/log/caddy/public-web.json"; profile = "public-web"; } ]; }; }; } ``` ## Roadmap - richer decision engine - optional GeoIP and ASN providers beyond RDAP - richer review filters in the UI - alternative blocking backends besides OPNsense - direct streaming ingestion targets in addition to file polling