# Installation This document covers both generic Linux deployments and NixOS deployments. ## Prerequisites You need: - Linux. - Caddy access logs written to files in the default JSON format. - One readable log file per logical source you want to monitor. - A writable directory for the SQLite database. - Go 1.25 or newer if you build from source without Nix. - Optional: OPNsense API credentials if you want firewall alias synchronization. - Optional: outbound DNS and HTTPS access if you want IP investigation and bot verification. ## Deployment checklist Before starting the daemon, decide: - which Caddy log files you want to ingest - which heuristic profile each log file should use - whether the daemon should auto-block for each profile or only mark addresses for review - whether OPNsense integration is enabled - which address the web UI should listen on ## 1. Configure Caddy access logs The daemon reads files, not stdout and not journald. Each monitored source should write to its own file. An example Caddy configuration is available in [`../examples/Caddyfile`](../examples/Caddyfile). Important points: - Keep the default JSON log format. - Use one file per source. - Make sure the service account running `caddy-opnsense-blocker` can read those files. - If Caddy sits behind another proxy, make sure Caddy records the actual client IP in `request.client_ip`. ## 2. Prepare OPNsense credentials (optional) If you want the daemon to block and unblock IP addresses on OPNsense: 1. Create or reuse an API key and secret. 2. Decide which alias name should contain blocked IPs. 3. Keep the credentials in files readable only by the service account. If OPNsense integration is disabled, the daemon still ingests logs, records decisions, and supports manual review. ## 3. Build the binary Clone the repository and build the daemon: ```bash git clone https://git.dern.ovh/infrastructure/caddy-opnsense-blocker.git cd caddy-opnsense-blocker CGO_ENABLED=0 go test ./... CGO_ENABLED=0 go build -o caddy-opnsense-blocker ./cmd/caddy-opnsense-blocker ``` You can also use `nix-build` if you prefer the Nix package defined in this repository. ## 4. Create the runtime user and directories Example for a non-NixOS host: ```bash useradd --system --home-dir /var/lib/caddy-opnsense-blocker --create-home --shell /usr/sbin/nologin blocker install -d -o blocker -g blocker -m 0750 /var/lib/caddy-opnsense-blocker install -d -o root -g blocker -m 0750 /etc/caddy-opnsense-blocker install -d -o root -g blocker -m 0750 /var/log/caddy install -m 0755 caddy-opnsense-blocker /usr/local/bin/caddy-opnsense-blocker ``` If your Caddy logs are group-readable by a different group, either adjust permissions or grant the runtime user supplementary group access. ## 5. Create the configuration file Start from the repository example: ```bash cp config.example.yaml /etc/caddy-opnsense-blocker/config.yaml chmod 0640 /etc/caddy-opnsense-blocker/config.yaml ``` Then edit: - `server.listen_address` - `storage.path` - `profiles` - `sources` - `opnsense.*` if OPNsense integration is enabled For a field-by-field reference, see [`configuration.md`](configuration.md). Store OPNsense credentials in files such as: ```bash install -m 0400 -o root -g blocker /path/to/api-key /etc/caddy-opnsense-blocker/opnsense-api-key install -m 0400 -o root -g blocker /path/to/api-secret /etc/caddy-opnsense-blocker/opnsense-api-secret ``` Then reference those files from `config.yaml`. ## 6. Run the daemon manually once Before installing a service unit, validate that the daemon starts correctly: ```bash sudo -u blocker /usr/local/bin/caddy-opnsense-blocker -config /etc/caddy-opnsense-blocker/config.yaml ``` In another terminal: ```bash curl http://127.0.0.1:9080/healthz ``` If the daemon is bound to another address, adjust the URL accordingly. ## 7. Install the systemd service An example unit file is available in [`../examples/caddy-opnsense-blocker.service`](../examples/caddy-opnsense-blocker.service). Typical installation steps: ```bash cp examples/caddy-opnsense-blocker.service /etc/systemd/system/caddy-opnsense-blocker.service systemctl daemon-reload systemctl enable --now caddy-opnsense-blocker.service ``` After that: ```bash systemctl status caddy-opnsense-blocker.service journalctl -u caddy-opnsense-blocker.service -f ``` ## 8. Verify end-to-end behavior Recommended checks: - `GET /healthz` returns `{"status":"ok",...}` - the UI loads on the configured address - new suspicious requests appear in the dashboard - the IP detail page shows the full request history for one address - manual `Block` and `Unblock` actions produce backend actions if OPNsense is enabled ## 9. Log rotation and upgrades - The daemon stores inode and offset per source, so it can resume after restarts. - File truncation or inode changes are detected automatically, which makes the common rename-based log rotation pattern safe. - Upgrades are usually just “replace the binary, restart the service”. The SQLite database is kept separately in the configured state directory. ## Nix build Build the packaged binary directly from the repository root: ```bash nix-build ``` The result symlink contains the packaged daemon under `result/bin/caddy-opnsense-blocker`. ## NixOS module The repository includes a reusable NixOS module in [`../module.nix`](../module.nix). ### Import from a local checkout ```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 = { server.listen_address = "127.0.0.1:9080"; 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-access.json"; profile = "public-web"; } ]; }; }; } ``` ### Notes about the NixOS module - The module defaults the service user and group to `caddy`. - The generated configuration file is written from `services.caddy-opnsense-blocker.settings`. - OPNsense credentials can be injected through systemd credentials with `credentials.opnsenseApiKeyFile` and `credentials.opnsenseApiSecretFile`. - The default database path is `/var/lib/caddy-opnsense-blocker/caddy-opnsense-blocker.db`. ## Operating without OPNsense Set `opnsense.enabled: false` to run in review-only mode. In that mode the daemon still: - ingests logs - stores events and IP state - runs investigations - serves the UI and API - records manual override decisions locally It simply stops short of calling an external firewall API.