Add per-server Interface/KeyPair edit hardening and OPNsense config import

Redact the private key from the /servers/:id/keypair response body -
the UI never rendered it, but the raw key was still returned over the
wire (json:"private_key,omitempty" plus explicit clearing before the
JSON response).

Add a new import flow: an admin can upload an OPNsense config.xml,
preview the WireGuard servers/clients it defines (editable before
committing), and confirm to create the corresponding
Server/ServerSetting/Client records. Nothing is auto-applied - no
wg-quick/systemctl call happens, matching the existing manual "Apply"
step for regular server management.

Schema verified against OPNsense core (WireGuard has been in core
since 22.1, not a plugin) - see opnsense/parse.go for the confirmed
tag reference. Public keys are always re-derived from private keys
rather than trusted from the export; client public-key collisions
against existing store data are skipped and reported per-batch rather
than aborting the whole import.

Since OPNsense stores DNS/MTU per-server and keepalive per-client, but
this fork only had those app-wide (GlobalSetting), extended
ServerSetting with DNSServers/MTU and Client with PersistentKeepalive
as optional overrides that fall back to the global default when unset
- existing single-server behavior is unchanged when the override is
empty/zero. Manual UI editing of the per-client keepalive override
outside the import flow is left for a later pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VjwLYRA87o8m9a9zztgs3
This commit is contained in:
sysops
2026-07-24 00:05:08 +02:00
co-authored by Claude Sonnet 5
parent 0dbb916866
commit 388a8377cd
10 changed files with 943 additions and 31 deletions
+18 -5
View File
@@ -48,17 +48,27 @@ func resolveServerID(c echo.Context) string {
}
// buildEffectiveSettings merges the app-wide GlobalSetting (DNS/MTU/
// PersistentKeepalive) with a server's own EndpointAddress override (from
// ServerSetting) into a single model.GlobalSetting, so util.BuildClientConfig
// can keep its existing single-struct signature unchanged.
// PersistentKeepalive) with a server's own EndpointAddress/DNSServers/MTU
// overrides (from ServerSetting) into a single model.GlobalSetting, so
// util.BuildClientConfig can keep its existing single-struct signature
// unchanged. Empty/zero overrides fall back to the global default, so
// existing single-server installs (no override ever set) are unaffected.
func buildEffectiveSettings(db store.IStore, serverID string) (model.GlobalSetting, error) {
globalSettings, err := db.GetGlobalSettings()
if err != nil {
return globalSettings, err
}
serverSettings, err := db.GetServerSettings(serverID)
if err == nil && serverSettings.EndpointAddress != "" {
globalSettings.EndpointAddress = serverSettings.EndpointAddress
if err == nil {
if serverSettings.EndpointAddress != "" {
globalSettings.EndpointAddress = serverSettings.EndpointAddress
}
if len(serverSettings.DNSServers) > 0 {
globalSettings.DNSServers = serverSettings.DNSServers
}
if serverSettings.MTU > 0 {
globalSettings.MTU = serverSettings.MTU
}
}
return globalSettings, nil
}
@@ -1408,6 +1418,9 @@ func UpdateServerKeyPairHandler(db store.IStore) echo.HandlerFunc {
log.Infof("Updated wireguard server key pair for server %s", serverID)
// Never return the private key in the HTTP response body; the
// caller only needs the public key to update its view.
serverKeyPair.PrivateKey = ""
return c.JSON(http.StatusOK, serverKeyPair)
}
}