Implements the from-scratch multi-server WireGuard management fork per CLAUDE.md spec: sqlite schema (servers/peers/audit_log/users), Curve25519 key generation, per-interface config rendering + wg-quick/systemd control, nftables hook scaffolding, session+CSRF-protected REST API with QR code and config download endpoints, a minimal vanilla-JS web UI, legacy wg0.conf migration, and both a native installer and a Proxmox LXC provisioning script (with auto-detected latest Debian template). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
2.5 KiB
Go
87 lines
2.5 KiB
Go
package wireguard
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gitea.perlbach24.de/scripte/wireguard-ui-multi/internal/server"
|
|
)
|
|
|
|
// ConfigDir is where per-interface wgX.conf files are written, e.g. /etc/wireguard.
|
|
var ConfigDir = "/etc/wireguard"
|
|
|
|
// RenderConfig builds the wg-quick compatible config text for a server and its peers.
|
|
func RenderConfig(srv *server.Server, peers []*server.Peer) string {
|
|
var b strings.Builder
|
|
|
|
fmt.Fprintf(&b, "[Interface]\n")
|
|
fmt.Fprintf(&b, "PrivateKey = %s\n", srv.PrivateKey)
|
|
fmt.Fprintf(&b, "Address = %s\n", srv.AddressRange)
|
|
fmt.Fprintf(&b, "ListenPort = %d\n", srv.ListenPort)
|
|
if srv.MTU > 0 {
|
|
fmt.Fprintf(&b, "MTU = %d\n", srv.MTU)
|
|
}
|
|
if srv.DNS != "" {
|
|
fmt.Fprintf(&b, "DNS = %s\n", srv.DNS)
|
|
}
|
|
|
|
for _, p := range peers {
|
|
if !p.Enabled {
|
|
continue
|
|
}
|
|
b.WriteString("\n[Peer]\n")
|
|
fmt.Fprintf(&b, "# %s\n", p.Name)
|
|
fmt.Fprintf(&b, "PublicKey = %s\n", p.PublicKey)
|
|
if p.PresharedKey != "" {
|
|
fmt.Fprintf(&b, "PresharedKey = %s\n", p.PresharedKey)
|
|
}
|
|
fmt.Fprintf(&b, "AllowedIPs = %s\n", p.AllowedIPs)
|
|
if p.PersistentKeepalive > 0 {
|
|
fmt.Fprintf(&b, "PersistentKeepalive = %d\n", p.PersistentKeepalive)
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// RenderClientConfig builds the config a peer/client would use to connect to srv.
|
|
func RenderClientConfig(srv *server.Server, p *server.Peer, endpointHost string) string {
|
|
var b strings.Builder
|
|
|
|
b.WriteString("[Interface]\n")
|
|
fmt.Fprintf(&b, "PrivateKey = %s\n", p.PrivateKey)
|
|
fmt.Fprintf(&b, "Address = %s\n", p.AllowedIPs)
|
|
if srv.DNS != "" {
|
|
fmt.Fprintf(&b, "DNS = %s\n", srv.DNS)
|
|
}
|
|
|
|
b.WriteString("\n[Peer]\n")
|
|
fmt.Fprintf(&b, "PublicKey = %s\n", srv.PublicKey)
|
|
if p.PresharedKey != "" {
|
|
fmt.Fprintf(&b, "PresharedKey = %s\n", p.PresharedKey)
|
|
}
|
|
fmt.Fprintf(&b, "Endpoint = %s:%d\n", endpointHost, srv.ListenPort)
|
|
fmt.Fprintf(&b, "AllowedIPs = 0.0.0.0/0, ::/0\n")
|
|
if p.PersistentKeepalive > 0 {
|
|
fmt.Fprintf(&b, "PersistentKeepalive = %d\n", p.PersistentKeepalive)
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// WriteConfig writes the rendered server config to ConfigDir/<interface>.conf with 0600 perms.
|
|
func WriteConfig(srv *server.Server, peers []*server.Peer) error {
|
|
if err := os.MkdirAll(ConfigDir, 0700); err != nil {
|
|
return err
|
|
}
|
|
path := filepath.Join(ConfigDir, srv.InterfaceName+".conf")
|
|
return os.WriteFile(path, []byte(RenderConfig(srv, peers)), 0600)
|
|
}
|
|
|
|
// ConfigPath returns the on-disk path for a server's config file.
|
|
func ConfigPath(srv *server.Server) string {
|
|
return filepath.Join(ConfigDir, srv.InterfaceName+".conf")
|
|
}
|