feat(backend): add OPNsense backend (#743)
Docker / Build and Push (push) Canceled after 0s
github-pages / deploy (push) Canceled after 0s
Test / make test (push) Canceled after 0s
Docker / release (push) Canceled after 0s

Manages interfaces and peers on OPNsense through the WireGuard API in
OPNsense core, so a stock appliance needs nothing installed. OPNsense
calls a tunnel a "server" and a peer on it a "client"; those map to
PhysicalInterface and PhysicalPeer.

Reads go through searchXxx because getXxx returns select fields as
{value, selected} maps that cannot be posted back to a write. Validation
failures arrive as HTTP 200 with a "result": "failed" body, so the body
is checked and not the status code.

Firewall rules are not managed, matching the pfSense backend: a new
tunnel handshakes but carries no traffic until a pass rule exists.

Alpha, and documented as such.

Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
This commit is contained in:
Jacopo Clark
2026-09-10 22:27:17 +02:00
committed by GitHub
parent 32ef6048fb
commit 7f5786f40f
14 changed files with 2855 additions and 14 deletions
+53 -1
View File
@@ -219,7 +219,7 @@ The current MikroTik backend is in **BETA** and may not support all features.
### `default`
- **Default:** `local`
- **Description:** The default backend to use for managing WireGuard interfaces.
Valid options are: `local`, or other backend id's configured in the `mikrotik` section.
Valid options are: `local`, or other backend id's configured in the `mikrotik`, `pfsense` or `opnsense` sections.
### `rekey_timeout_interval`
- **Default:** `180s`
@@ -293,6 +293,58 @@ Below are the properties for each entry inside `backend.mikrotik`:
For more details on configuring the MikroTik backend, see the [Backends](../usage/backends.md) documentation.
### OPNsense
The `opnsense` array contains a list of OPNsense backend definitions. Each entry describes how to connect to an OPNsense firewall that hosts WireGuard interfaces.
The WireGuard API used here is part of OPNsense core, so no add-on package needs to be installed on the firewall.
Below are the properties for each entry inside `backend.opnsense`:
#### `id`
- **Default:** *(empty)*
- **Description:** A unique identifier for this backend.
This value can be referenced by `backend.default` to use this backend as default.
The identifier must be unique across all backends and must not use the reserved keyword `local`.
#### `display_name`
- **Default:** *(empty)*
- **Description:** A human-friendly display name for this backend. If omitted, the `id` will be used as the display name.
#### `api_url`
- **Default:** *(empty)*
- **Description:** Base URL of the OPNsense appliance, including scheme, e.g., `https://opnsense.example.com`.
Do not append `/api`; the backend adds the API paths itself.
#### `api_key`
- **Default:** *(empty)*
- **Description:** API key, created under `System -> Access -> Users -> <user> -> API keys`.
#### `api_secret`
- **Default:** *(empty)*
- **Description:** The secret belonging to `api_key`. OPNsense authenticates the pair as HTTP Basic credentials.
The secret is shown only once, when the key is created.
#### `api_verify_tls`
- **Default:** `false`
- **Description:** Whether to verify the TLS certificate of the OPNsense API endpoint. Set to `false` to allow self-signed certificates (not recommended for production).
#### `api_timeout`
- **Default:** `30s`
- **Description:** Timeout for API requests to the OPNsense firewall. Uses Go duration format (e.g., `10s`, `1m`). If omitted, a default of 30 seconds is used.
#### `ignored_interfaces`
- **Default:** *(empty)*
- **Description:** A list of interface names to exclude during interface enumeration.
This is useful if you want to prevent specific interfaces from being imported from the OPNsense firewall.
#### `debug`
- **Default:** `false`
- **Description:** Enable verbose debug logging for the OPNsense backend.
> There is deliberately no `concurrency` option for this backend. The MikroTik and pfSense backends issue one request per interface when enumerating details; the OPNsense search endpoints return every record with its fields already populated, so enumeration costs a fixed number of requests regardless of how many tunnels exist.
For more details on configuring the OPNsense backend, see the [Backends](../usage/backends.md) documentation.
---
## Advanced
+46 -1
View File
@@ -9,9 +9,10 @@ A global default backend determines where newly created interfaces go (unless yo
- **Local** (default): Manages interfaces on the host running WireGuard Portal (Linux WireGuard via wgctrl). Use this when the portal should directly configure wg devices on the same server.
- **MikroTik** RouterOS (_beta_): Manages interfaces and peers on MikroTik devices via the RouterOS REST API. Use this to control WG interfaces on RouterOS v7+.
- **pfSense** (_alpha_): Manages interfaces and peers on pfSense firewalls via the pfSense REST API.
- **OPNsense** (_alpha_): Manages interfaces and peers on OPNsense firewalls via the WireGuard API built into OPNsense core. Unlike the pfSense backend, no add-on package is required on the firewall.
How backend selection works:
- The default backend is configured at `backend.default` (_local_ or the id of a defined MikroTik backend).
- The default backend is configured at `backend.default` (_local_ or the id of a defined MikroTik, pfSense or OPNsense backend).
New interfaces created in the UI will use this backend by default.
- Each interface stores its backend. You can select a different backend when creating a new interface.
@@ -89,3 +90,47 @@ backend:
### Known limitations:
- Alpha quality: behavior and API coverage may change.
- Statistics (rx/tx bytes, last handshake) are not available from the pfSense REST API today.
## Configuring OPNsense backends
> :warning: The OPNsense backend is currently **alpha**. Interface and peer CRUD are supported, as are **per-peer** traffic statistics (rx/tx bytes and last handshake), which the pfSense backend cannot provide. Interface-level byte counters are not reported, because OPNsense exposes counters per peer only. Interface hooks, DNS push and ping are not supported.
The OPNsense backend talks to the WireGuard API that is part of **OPNsense core**. Unlike the pfSense backend, no add-on package needs to be installed — a stock OPNsense install already answers `/api/wireguard/*`.
Point `api_url` at the appliance root (for example `https://opnsense.example.com`); the portal appends the API paths itself.
### Prerequisites on OPNsense:
- OPNsense 24.1 or newer, where WireGuard is part of the base system. Developed and tested against 26.7.
- An API key/secret pair, created under `System -> Access -> Users -> <user> -> API keys`. OPNsense shows the secret only once, at creation time.
- The user needs permission for the WireGuard and firewall pages it should manage.
- HTTPS recommended; set `api_verify_tls: false` only for lab/self-signed setups.
Example WireGuard Portal configuration:
```yaml
backend:
# default backend decides where new interfaces are created
default: opnsense1
opnsense:
- id: opnsense1 # unique id, not "local"
display_name: Edge firewall # optional nice name
api_url: https://opnsense.example.com # appliance root, no /api suffix
api_key: your-api-key
api_secret: your-api-secret
api_verify_tls: true
api_timeout: 30s
debug: false
```
### Behaviour worth knowing:
- **Interface naming.** An OPNsense tunnel has both an instance number and a free-text name. WireGuard Portal uses the resulting device name (`wg0`, `wg1`, ...) as the interface identifier, so an interface imported from OPNsense looks the same as one managed by the `local` backend. The free-text name becomes the display name.
- **Changes are staged.** OPNsense applies WireGuard configuration only when the service is reconfigured, which the backend does after every change. Reconfiguring does **not** drop or rekey sessions of peers that are already connected, so adding a peer is safe against a live VPN.
- **The service is enabled automatically.** Bringing an interface up switches on the global WireGuard service if it is off, because a tunnel configured while the service is disabled stays down without reporting an error anywhere. The backend never disables the service.
- **Firewall rules are not managed.** A newly created tunnel has no pass rule, so peers will complete a handshake but carry no traffic until you add a rule on the `WireGuard (Group)` interface. This matches the pfSense backend, which also leaves firewall rules to the administrator.
### Known limitations:
- Alpha quality: behavior and API coverage may change.
- Interface hooks (`PreUp`/`PostUp`/...) are not supported; OPNsense has no API equivalent.
- DNS settings pushed to clients are a property of the tunnel in OPNsense and are not managed by the portal.
- `PingAddresses` is not implemented.