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>
86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
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
|
|
}
|