2
Files
caddy-opnsense-blocker/README.md

3.0 KiB

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:
CGO_ENABLED=0 go run ./cmd/caddy-opnsense-blocker -config ./config.yaml
  1. 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:

CGO_ENABLED=0 go test ./...

Build the daemon:

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