2
Files
caddy-opnsense-blocker/docs/install.md

6.9 KiB

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.

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:

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:

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:

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.

Store OPNsense credentials in files such as:

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:

sudo -u blocker /usr/local/bin/caddy-opnsense-blocker -config /etc/caddy-opnsense-blocker/config.yaml

In another terminal:

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.

Typical installation steps:

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:

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:

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.

Import from a local checkout

{
  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.