feat(wg): Push-Routes (Client-Routes) für WireGuard-Server-Interfaces

Neues Feld 'client_routes' auf wireguard_interfaces: der Operator
trägt dort kommagetrennte Netzwerke ein (z. B. 10.0.10.0/24 für ein
LAN hinter der Box). Der Peer-Config-Download fügt diese automatisch
als zusätzliche AllowedIPs in den [Peer]-Block der Client-Config ein.

Bisher wurde nur das Server-Tunnel-Subnetz (ifc.address_cidr) als
AllowedIPs exportiert — Peers konnten so keine anderen Netze über
den Tunnel erreichen ohne die Config manuell anzupassen.

Migration: 0026_wg_client_routes.sql

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-21 13:09:51 +02:00
parent 1f0d05019e
commit bc5d81d966
9 changed files with 46 additions and 20 deletions

View File

@@ -173,6 +173,7 @@ type ifaceCreateReq struct {
AllowedIPs *string `json:"allowed_ips,omitempty"`
PersistentKeepalive *int `json:"persistent_keepalive,omitempty"`
MTU *int `json:"mtu,omitempty"`
ClientRoutes *string `json:"client_routes,omitempty"`
Role string `json:"role"`
Active bool `json:"active"`
Description *string `json:"description,omitempty"`
@@ -222,6 +223,7 @@ func (h *WireguardHandler) CreateIface(c *gin.Context) {
PeerEndpoint: req.PeerEndpoint, PeerPublicKey: req.PeerPublicKey,
PeerPSKEnc: encPSK, AllowedIPs: req.AllowedIPs,
PersistentKeepalive: req.PersistentKeepalive, MTU: req.MTU,
ClientRoutes: req.ClientRoutes,
Role: req.Role, Active: req.Active, Description: req.Description,
}
if ifc.Role == "" {
@@ -311,6 +313,7 @@ func (h *WireguardHandler) UpdateIface(c *gin.Context) {
PeerEndpoint: req.PeerEndpoint, PeerPublicKey: req.PeerPublicKey,
PeerPSKEnc: encPSK, AllowedIPs: req.AllowedIPs,
PersistentKeepalive: req.PersistentKeepalive, MTU: req.MTU,
ClientRoutes: req.ClientRoutes,
Role: req.Role, Active: req.Active, Description: req.Description,
}
if ifc.Role == "" {
@@ -645,12 +648,15 @@ func (h *WireguardHandler) peerConfigText(ctx context.Context, peerID int64) (st
}
fmt.Fprintf(&b, "PresharedKey = %s\n", string(psk))
}
// AllowedIPs on the client side is "everything that should go
// through the tunnel". For a server hosting an internal LAN
// this is typically 10.x/8 or the server's address range. We
// default to the iface address (so the client can at least
// reach the gateway) — operator can edit downloaded conf.
fmt.Fprintf(&b, "AllowedIPs = %s\n", ifc.AddressCIDR)
// AllowedIPs on the client side: the server's tunnel subnet
// (so peers can reach each other + the gateway) plus any
// additional "push routes" the operator configured on the
// server interface (e.g. 10.0.10.0/24 for a LAN behind the box).
clientAllowedIPs := ifc.AddressCIDR
if ifc.ClientRoutes != nil && strings.TrimSpace(*ifc.ClientRoutes) != "" {
clientAllowedIPs += ", " + strings.TrimSpace(*ifc.ClientRoutes)
}
fmt.Fprintf(&b, "AllowedIPs = %s\n", clientAllowedIPs)
// Endpoint — the operator's public host:port that peers dial.
// We don't know this here (could be a CNAME or behind a load
// balancer); leave a placeholder the operator must fill in.