Add wireguard-ui-multi core: multi-server DB, WireGuard manager, REST API, UI, installers

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>
This commit is contained in:
sysops
2026-07-10 02:53:14 +02:00
co-authored by Claude Sonnet 5
parent 3d6608ef80
commit 3b3ffd8ebf
26 changed files with 3236 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
package database
import (
"database/sql"
"fmt"
_ "modernc.org/sqlite"
)
// DB wraps the sqlite connection used by the whole application.
type DB struct {
*sql.DB
}
const schema = `
CREATE TABLE IF NOT EXISTS servers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
interface_name TEXT NOT NULL UNIQUE,
listen_port INTEGER NOT NULL,
private_key TEXT NOT NULL,
public_key TEXT NOT NULL,
address_range TEXT NOT NULL,
dns TEXT DEFAULT '',
mtu INTEGER DEFAULT 1420,
enabled INTEGER NOT NULL DEFAULT 1,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS peers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
name TEXT NOT NULL,
email TEXT DEFAULT '',
public_key TEXT NOT NULL,
private_key TEXT DEFAULT '',
preshared_key TEXT DEFAULT '',
allowed_ips TEXT NOT NULL,
endpoint TEXT DEFAULT '',
persistent_keepalive INTEGER DEFAULT 25,
enabled INTEGER NOT NULL DEFAULT 1,
expires_at DATETIME,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
actor TEXT NOT NULL,
action TEXT NOT NULL,
target TEXT NOT NULL,
detail TEXT DEFAULT '',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_peers_server_id ON peers(server_id);
`
// Open opens (creating if needed) the sqlite database at path and applies schema.
func Open(path string) (*DB, error) {
sqlDB, err := sql.Open("sqlite", path+"?_pragma=foreign_keys(1)")
if err != nil {
return nil, fmt.Errorf("open sqlite: %w", err)
}
if _, err := sqlDB.Exec(schema); err != nil {
sqlDB.Close()
return nil, fmt.Errorf("apply schema: %w", err)
}
return &DB{sqlDB}, nil
}
// LogAudit records an entry in the audit log.
func (db *DB) LogAudit(actor, action, target, detail string) error {
_, err := db.Exec(`INSERT INTO audit_log (actor, action, target, detail) VALUES (?, ?, ?, ?)`,
actor, action, target, detail)
return err
}