80 Commits
Author SHA1 Message Date
h44z 38c372e239 Go 1.27 (#755)
* update to go 1.27

* go fix changes
2026-09-12 17:52:01 +02:00
Jacopo Clark 7f5786f40f feat(backend): add OPNsense backend (#743)
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>
2026-09-10 22:27:17 +02:00
h44z 32ef6048fb feat: add link-based configuration emails (#725) (#754)
* feat: add link-based configuration emails (#725)

* fix tests
2026-09-10 22:04:20 +02:00
h44z 4337fb7809 feat(config): add startup_timeout option with context propagation (#739) (#750) 2026-09-02 22:28:51 +02:00
Jacopo Clark 64195a25c1 fix(ldap): do not disable every user when a sync returns no identifiers (#744)
disable_missing tested absence against the raw sync result without
checking that the search returned anything usable, so a search that
succeeds and yields nothing looked like "every user has been removed".

Connection and search errors were already safe, since synchronizeLdapUsers
returns before the disable phase. The gap is the successful-but-empty
case: a base_dn or sync_filter that stops matching, an unpopulated
replica, a field_map user_identifier naming an attribute the server does
not return, or a bind account that lost read access to the user subtree.
LDAP gives nothing to tell those apart from a directory that is genuinely
empty; they all answer success with zero entries.

Acting on it is not a database flag. TopicUserDisabled removes each
user's peers from the WireGuard device, the successful search means no
error is logged, and every message on the path was Debug while log_level
defaults to info, so the whole event was silent. It also repeats every
sync interval.

Refuse to disable anyone when no usable identifier came back, logging the
provider, entry count and identifier field. The guard counts identifiers,
not entries, so it covers the field_map case too. The per-user disable
line moves from Debug to Warn so a mass disable is audible even where the
guard does not fire.

The cost is that a directory intentionally emptied of users now disables
nobody. That is documented, along with the workaround: leave one account
matching sync_filter and everyone else is disabled as before.

Signed-off-by: clark-ja <37738506+clark-ja@users.noreply.github.com>
2026-09-01 22:38:31 +02:00
Christoph Haas de2f7c6835 doc: add section that describes how to configure OAuth2 callback URL 2026-06-05 20:34:25 +02:00
Aram Akhavan dea358c8cf fix: pfsense backend (#703)
* Return empty string instead of "<nil>" when a genericjsonobject key doesn't exist.

* Fix pfsense backend

* Fix API request parameter names and types
* Refactor interface and peer creation to send the necessary parameters
* Automatically call apply when interfaces or peers are changed

Signed-off-by: Aram Akhavan <1147328+kaysond@users.noreply.github.com>

---------

Signed-off-by: Aram Akhavan <1147328+kaysond@users.noreply.github.com>
2026-05-29 21:55:54 +02:00
h44z 72cfd1d8a9 feat: add support for PKCE (#686) (#702) 2026-05-28 20:49:13 +02:00
Mark Lawrence 8fd2721345 Document necessary systemd-networkd configuration (#694)
By default, the systemd-networkd.service(8) removes routing policy
created by other tools when it starts. This can cause wireguard tunnels
to stop working during a system upgrade or other administration
actions. Document the configuration necessary to prevent this occuring.

Signed-off-by: Mark Lawrence <mark@rekudos.net>
2026-05-23 20:33:14 +02:00
nesbyte 835f76bf58 feat(docs): how to troubleshoot admin_group_regex with oidc (#684)
Added instructions for identifying claims in OIDC user info payload for admin rights.
2026-05-18 22:31:46 +02:00
Mykhailo RoitandChristoph Haas 958dcb8fa9 feat: sanitize external identity provider user data (#681)
* feat: sanitize external user data

* remove config option to disable Sanitization: sanitize_external_user_data

* cleanup

---------

Co-authored-by: Christoph Haas <christoph.h@sprinternet.at>
2026-05-18 22:28:27 +02:00
h44z 1c133b6f6e Improved default peer handling (#674)
* create default peers for newly created interfaces (#666)

* allow to manually create default peers for an interface (#666)
2026-04-16 21:55:41 +02:00
Michael TupitsynandChristoph Haas 71806455dd OIDC - support IdP logout (#670)
* OIDC - support IdP logout

Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>

* Add support of logout_idp_session parameter

Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>

* Fix merge conflict issue

Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>

* Restore original package-lock.json

Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>

* Cleanup

---------

Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>
Co-authored-by: Christoph Haas <christoph.h@sprinternet.at>
2026-04-12 13:18:04 +02:00
Michael Tupitsyn 9b437205b1 Add support for auth.oidc.allowed_user_groups (#667) (#668)
Signed-off-by: Michael Tupitsyn <michael.tupitsyn@gmail.com>
2026-04-11 18:24:18 +02:00
402cc1b5f3 feat: Implement LDAP interface-specific provisioning filters (#642)
* Implement LDAP filter-based access control for interface provisioning

* test: add unit tests for LDAP interface filtering logic

* smaller improvements / cleanup

---------

Co-authored-by: jc <37738506+theguy147@users.noreply.github.com>
Co-authored-by: Christoph Haas <christoph.h@sprinternet.at>
2026-03-19 23:13:19 +01:00
h44z f70f60a3f5 fix: configurable handshake validity interval and improved defaults (#645)
* fix: support configurable rekey timeout interval for peer connectivity tracking (#641)

* change default check-time to 180s
2026-03-19 23:11:40 +01:00
TimandTim Aerdts 9c56e92443 Updated documentation (#640)
Include pass about systemd networkd managing foreign routes and deleting them on restart.

Signed-off-by: Tim <tim@tuimz.nl>
Co-authored-by: Tim Aerdts <tim@teaminova.nl>
2026-03-17 18:33:57 +01:00
Christoph Haas 85d407ff11 chore: update dependencies 2026-02-20 18:59:58 +01:00
h44z e0f6c1d04b feat: allow multiple auth sources per user (#500,#477) (#612)
* feat: allow multiple auth sources per user (#500,#477)

* only override isAdmin flag if it is provided by the authentication source
2026-01-21 22:22:22 +01:00
h44z 2200509bc0 feat: introduce "Create Default Peer" flag for interfaces (#513) (#605) 2026-01-13 23:11:22 +01:00
h44z 1b56acac87 Doc Update (#603)
* docs: enhance binary usage guide and systemd setup (#577)

* docs: remove invalid mail templates section from configuration overview
2026-01-05 23:25:37 +01:00
h44z 0a88fe745f allow setting a base-path for the web UI and API (#583) (#595) 2025-12-20 15:30:55 +01:00
h44z 8cc937b031 Custom templates (#594)
* allow custom mail templates (#533)

* allow to override embedded frontend (#533)
2025-12-10 23:10:43 +01:00
rwjack 54ca1d8aed Add Pfsense backend (ALPHA) (#585)
* Add pfSense backend domain types and configuration

This adds the necessary domain types and configuration structures
for the pfSense backend support. Includes PfsenseInterfaceExtras and
PfsensePeerExtras structs, and the BackendPfsense configuration
with API URL, key, and timeout settings.

* Add low-level pfSense REST API client

Implements the HTTP client for interacting with the pfSense REST API.
Handles authentication via X-API-Key header, request/response parsing,
and error handling. Uses the pfSense REST API v2 endpoints as documented
at https://pfrest.org/.

* Implement pfSense WireGuard controller

This implements the InterfaceController interface for pfSense firewalls.
Handles WireGuard tunnel and peer management through the pfSense REST API.
Includes proper filtering of peers by interface (since API filtering doesn't
work) and parsing of the allowedips array structure with address/mask fields.

* Register pfSense controllers and update configuration

Registers the pfSense backend controllers in the controller manager
and adds example configuration to config.yml.sample. Also updates
README to mention pfSense backend support.

* Fix peer filtering and allowedips parsing for pfSense backend

The pfSense REST API doesn't support filtering peers by interface
via query parameters, so all peers are returned regardless of the
filter. This caused peers from all interfaces to be randomly assigned
to a single interface in wg-portal.

Additionally, the API returns allowedips as an array of objects with
"address" and "mask" fields instead of a comma-separated string,
which caused parsing failures.

Changes:
- Remove API filter from GetPeers() since it doesn't work
- Add client-side filtering by checking the "tun" field in peer responses
- Update convertWireGuardPeer() to parse allowedips array structure
- Add parseAddressArray() helper for parsing address objects
- Attempt to fetch interface addresses from /tunnel/{id}/address endpoint
  (endpoint may not be available in all pfSense versions)
- Add debug logging for peer filtering and address loading operations

Note: Interface addresses may still be empty if the address endpoint
is not available. Public Endpoint and Default DNS Servers are typically
configured manually in wg-portal as the pfSense API doesn't provide
this information.

* Extract endpoint, DNS, and peer names from pfSense peer data

The pfSense API provides endpoint, port, and description (descr) fields
in peer responses that can be used to populate interface defaults and
peer display names.

Changes:
- Extract endpoint and port from peers and combine them properly
- Fix peer name/description extraction to check "descr" field first
  (pfSense API uses "descr" instead of "description" or "comment")
- Add extractPfsenseDefaultsFromPeers() helper to extract common
  endpoint and DNS from peers during interface import
- Set PeerDefEndpoint and PeerDefDnsStr from peer data for pfSense
  backends during interface import
- Use most common endpoint/DNS values when multiple peers are present

* Fix interface display name to use descr field from pfSense API

The pfSense API uses "descr" field for tunnel descriptions, not
"description" or "comment". Updated convertWireGuardInterface()
to check "descr" first so that tunnel descriptions (e.g., "HQ VPN")
are displayed in the UI instead of just the tunnel name (e.g., "tun_wg0").

* Remove calls to non-working tunnel and peer detail endpoints

The pfSense REST API endpoints /api/v2/vpn/wireguard/tunnel/{id}
and /api/v2/vpn/wireguard/tunnel/{id}/address don't work and were
causing log spam. Removed these calls and use only the data from
the tunnel/peer list responses.

Also removed the peer detail endpoint call that was added for
statistics collection, as it likely doesn't work either.

* Fix unused variable compilation error

Removed unused deviceId variable that was causing build failure.

* Optimize tunnel address fetching to use /tunnel?id endpoint

Instead of using the separate /tunnel/address endpoint, now query
the specific tunnel endpoint /tunnel?id={id} which includes the
addresses array in the response. This avoids unnecessary API calls
and simplifies the code.

- GetInterface() now queries /tunnel?id={id} after getting tunnel ID
- loadInterfaceData() queries /tunnel?id={id} as fallback if addresses missing
- extractAddresses() properly parses addresses array from tunnel response
- Removed /tunnel/address endpoint calls

Signed-off-by: rwjack <jack@foss.family>

* Fix URL encoding issue in tunnel endpoint queries

Use Filters in PfsenseRequestOptions instead of passing query strings
directly in the path. This prevents the ? character from being encoded
as %3F, which was causing 404 errors.

- GetInterface() now uses Filters map for id parameter
- loadInterfaceData() now uses Filters map for id parameter

Signed-off-by: rwjack <jack@foss.family>

* update backend docs for pfsense

---------

Signed-off-by: rwjack <jack@foss.family>
2025-12-09 22:33:12 +01:00
Christoph d759fc7dc7 allow to log raw LDAP user data (#571) 2025-11-19 16:00:11 +01:00
Christoph 67192170fc doc: fix incorrect config examples 2025-11-18 23:23:49 +01:00
Isak Wertwein 8f25bef050 feat: config by environment variables (#570)
* feat: config by environment variables without config file

Signed-off-by: Isak Wertwein <isak.wertwein@gmail.com>

* string slice by environment variable

Signed-off-by: Isak Wertwein <isak.wertwein@gmail.com>

---------

Signed-off-by: Isak Wertwein <isak.wertwein@gmail.com>
2025-11-16 18:33:25 +01:00
Christoph Haas 80dc7f290a correct enum for User-Source in api doc (#562) 2025-11-13 20:00:37 +01:00
Christoph Haas 139fb17f98 redo UI screenshots, fix the responsiveness of the image slider for wgportal.org 2025-10-12 15:48:08 +02:00
Christoph Haas f53d0b3d7f add the possibility to debug oauth or oidc login issues (#541) 2025-10-12 15:09:40 +02:00
h44z cdf3a49801 Cleanup route handling (#542)
* mikrotik: allow to set DNS, wip: handle routes in wg-controller

* replace old route handling for local controller

* cleanup route handling for local backend

* implement route handling for mikrotik controller
2025-10-12 14:31:19 +02:00
Christoph Haas 298c9405f6 add support for sending emails to peers without linked user accounts if their user-identifier is a valid email address 2025-10-12 14:31:01 +02:00
h44z 9e6ad98c4e Doc improvements (#538)
* add dark/light image to doc

* add dark/light image to doc

* add funding info, prepare release v2.1
2025-10-04 14:17:29 +02:00
h44z 765fb09770 Mikrotik improvements (#521)
* allow to specify ignored interfaces (#514)

* only set endpoint info for "responder" peers (#516)
2025-09-09 21:43:16 +02:00
Christoph Haas db357b82d0 update doc for disable_admin_user flag (#515) 2025-09-08 19:16:52 +02:00
h44z c79a6c83a8 allow setting the DisplayName property for newly provisioned peers (#507) (#511) 2025-09-03 19:34:58 +02:00
h44z 112f6bfb77 Mikrotik integration (#467)
Allow MikroTik routes as WireGuard backends
2025-08-10 14:42:02 +02:00
h44z edb88b5768 new webhook models (#444) (#471)
warning: existing webhook receivers need to be adapted to the new models
2025-06-29 19:49:01 +02:00
h44z dd28a8dddf allow to hide login form (#459) (#470)
use the `hide_login_form` parameter in the `auth` settings to configure this feature
2025-06-27 13:50:38 +02:00
h44z be29abd29a add webhook event for peer state change (#444) (#468)
* add webhook event for peer state change (#444)

new event types: connect and disconnect

example payload:

```json
{
  "event": "connect",
  "entity": "peer",
  "identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
  "payload": {
    "PeerId": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
    "IsConnected": true,
    "IsPingable": false,
    "LastPing": null,
    "BytesReceived": 1860,
    "BytesTransmitted": 10824,
    "LastHandshake": "2025-06-26T23:04:33.325216659+02:00",
    "Endpoint": "10.55.66.77:33874",
    "LastSessionStart": "2025-06-26T22:50:40.10221606+02:00"
  }
}
```

* add webhook docs (#444)
2025-06-27 12:37:10 +02:00
S.J. Louw f0be66aea4 Option to limit peer count that a normal user can create (#457) 2025-06-26 20:17:45 +02:00
Christoph Haas 75a5f3d815 add/improve documentation 2025-05-16 14:58:05 +02:00
Christoph Haas e9005b1b90 add minimum password length check 2025-05-16 09:55:35 +02:00
Christoph Haas 1394be2341 add webauthn (passkey) support 2025-05-12 22:53:43 +02:00
Christoph Haas 6a96925be7 add API endpoints to prepare fresh interfaces and peers (#432) 2025-05-09 16:19:36 +02:00
Christoph Haas c6253e7c15 clarify Docker image version tags, remove stable and legacy builds (#191) 2025-05-09 15:42:08 +02:00
Christoph Haas 99d6ce73ad update documentation for allowed_domains in oauth and oidc (#416) 2025-05-05 18:33:05 +02:00
Christoph Haas 432c627f9b further improve documentation and examples (#423) 2025-05-04 14:48:34 +02:00
Christoph Haas cd60761ea7 improve docs 2025-05-04 11:16:46 +02:00
Christoph Haas 2c8304417b prepare for v2 release 2025-05-04 11:00:12 +02:00