diff --git a/.sisyphus/plans/nxg-wgdashboard-config-parity.md b/.sisyphus/plans/nxg-wgdashboard-config-parity.md new file mode 100644 index 0000000..eb87e48 --- /dev/null +++ b/.sisyphus/plans/nxg-wgdashboard-config-parity.md @@ -0,0 +1,206 @@ +# Plan: WGDashboard Config Parity (Phase 5.5) + +## 1. Goal +Achieve feature parity with WGDashboard for WireGuard configuration editing and peer settings, adapting WGDashboard's local-file management approach to NexusGuard's multi-node database-driven SD-WAN architecture. + +--- + +## 2. Current Status (May 22, 2026) + +### ✅ COMPLETED + +#### Phase 1: Database Model Updates (Backend) +- [x] **Update `WgServer` Model**: Added MTU (default 1420), DNS (default "1.1.1.1"). +- [x] **Update `Device` Model**: Added EndpointAllowedIPs, DNS, MTU, PersistentKeepalive (default 25), Notes, IsSuspended, RxBytes, TxBytes. +- [x] **Database Migration**: Auto-migrate via GORM AutoMigrate. + +#### Phase 2: API Updates (Backend) +- [x] **Server API (`servers.go`)**: `PUT /api/v1/servers/:id` — pointer fields for partial updates, firewall sync on port change. +- [x] **Device API (`devices.go`)**: `PUT /api/v1/devices/:id` — pointer fields for EndpointAllowedIPs, DNS, MTU, PersistentKeepalive, Notes. +- [x] **Config Generation (`peers.go`)**: `getDeviceConfig` — DNS override (device > server > "1.1.1.1"), MTU override, PersistentKeepalive, EndpointAllowedIPs. +- [x] **Peer Suspension API**: `POST /api/v1/devices/:id/suspend` and `/unsuspend` with `IsSuspended` toggle + firewall rule add/remove. + +#### Phase 3: Firewall Enforcement Sync (nftables) — MOSTLY DONE +- [x] **Firewall Manager Interface**: Extended with `AddForwardRule`, `RemoveForwardRule`, `AddInputRule`, `RemoveInputRule`. +- [x] **Linux Implementation**: `nftables_linux.go` via `nft` CLI (AddForwardRule, RemoveForwardRule, AddInputRule, RemoveInputRule). +- [x] **Stub Implementation**: `nftables_stub.go` for non-Linux builds. +- [x] **Server Create**: `AddInputRule(server.Name, server.ListenPort)` in `servers.go`. +- [x] **Server Update**: Firewall re-sync when ListenPort changes in `servers.go`. +- [x] **Server Delete**: `RemoveInputRule(server.Name)` in `servers.go`. +- [x] **Peer Suspend**: `RemoveForwardRule` / `AddForwardRule` in `devices.go` toggleSuspension. +- [x] **Device Delete (partial)**: `RemoveRangeRule` called but **missing `RemoveForwardRule`**. + +### ❌ REMAINING (to be completed now) + +#### Phase 3: Backend Firewall Fixes +- [x] **Fix duplicate `"net"` import** in `devices.go` (lines 4-5, two imports of same package). +- [x] **Add `AddForwardRule` in `peers.go` CreatePeer** — after device creation, add FORWARD rule for device AllowedIPs. +- [x] **Add firewall sync in `devices.go` Update** — when EndpointAllowedIPs or AllowInternet changes, re-sync FORWARD rule. +- [x] **Add `RemoveForwardRule` in `devices.go` Delete** — clean up FORWARD rule when device is deleted. + +#### Phase 4: Dashboard UI (Frontend) +- [x] **Node Edit Modal** in `Servers.vue` — Edit Node button + modal to configure MTU, DNS, ListenPort via `PUT /api/v1/servers/:id`. +- [x] **Advanced Peer Settings** in `DeviceDetail.vue` — Accordion section with fields for AllowedIPs, DNS, MTU, Keepalive, Notes, Suspend toggle, using new API fields. +- [x] **Update API client** (`servers.ts`, `devices.ts`) — Add `updateServer()`, `suspendDevice()`, `unsuspendDevice()` and extend `updateDevice()` with new fields. + +--- + +## 3. Implementation Tasks + +### Task A: Backend Fixes (4 edits across 2 files) + +#### A.1 devices.go — Duplicate import +- Remove second `"net"` import on line 5. +- File: `apps/server-core/api/devices.go` +- Verify: `go build ./...` passes. + +#### A.2 peers.go — Add ForwardRule on CreatePeer +- After line ~131 (configText generation), add: + ```go + // Sync firewall FORWARD rule for this peer + h.fw.AddForwardRule(device.Name, ip, allowedIPs) + ``` +- Also handle case where EndpointAllowedIPs is empty — derive from AllowInternet. +- File: `apps/server-core/api/peers.go` + +#### A.3 devices.go — Firewall sync on Update +- In `Update()` handler, after device is updated, re-sync FORWARD rule if: + - `EndpointAllowedIPs` changed (req.EndpointAllowedIPs != nil) + - `AllowInternet` changed (req.AllowInternet != nil) +- Calculate destCIDR from device's EndpointAllowedIPs > AllowInternet > InternalIP +- Call `h.fw.RemoveForwardRule(device.Name)` then `h.fw.AddForwardRule(...)` with new CIDR. + +#### A.4 devices.go — ForwardRule cleanup on Delete +- In `Delete()` handler, add `h.fw.RemoveForwardRule(device.Name)` before or after the existing `h.db.Delete(&device)`. +- File: `apps/server-core/api/devices.go` + +### Task B: Frontend API Client Updates + +#### B.1 servers.ts — Add updateServer() +```typescript +export const updateServer = async (id: string, data: { + name?: string; + public_endpoint?: string; + listen_address?: string; + listen_port?: number; + mtu?: number; + dns?: string; +}) => { + await api.put(`/servers/${id}`, data) +} +``` + +#### B.2 devices.ts — Extend updateDevice() + add suspend/unsuspend +```typescript +export const updateDevice = async (id: string, data: { + name?: string; + allow_internet?: boolean; + endpoint_allowed_ips?: string; + dns?: string; + mtu?: number; + persistent_keepalive?: number; + notes?: string; +}) => { + await api.put(`/devices/${id}`, data) +} + +export const suspendDevice = async (id: string) => { + const { data } = await api.post(`/devices/${id}/suspend`) + return data +} + +export const unsuspendDevice = async (id: string) => { + const { data } = await api.post(`/devices/${id}/unsuspend`) + return data +} +``` + +#### B.3 Device interface — Add new fields +```typescript +export interface Device { + // ... existing fields ... + EndpointAllowedIPs?: string + DNS?: string + MTU?: number + PersistentKeepalive?: number + Notes?: string + IsSuspended?: boolean +} +``` + +### Task C: Frontend UI Components + +#### C.1 Servers.vue — Edit Node Modal +Add to the template: +- An "Edit" button next to "Delete" in each server row +- A modal dialog (glassmorphism style matching current design) with fields: + - ListenPort (number input) + - MTU (number input) + - DNS (text input) +- Form submits to `updateServer(id, payload)` API +- Modal uses `v-model="showEditModal"` to open/close +- Refresh list after update + +Implementation approach: +- Add `editingServer` ref to track which server is being edited +- Add modal template with glassmorphism card (bg-gray-900/90 rounded-2xl border border-white/10 shadow-lg backdrop-blur-xl) +- Pre-populate form with current server values +- On submit: call API, close modal, reload list + +#### C.2 DeviceDetail.vue — Advanced Peer Settings +Add after the AllowInternet toggle section: +- A collapsible "Advanced Settings" accordion +- Inside: form fields for: + - AllowedIPs (text input - comma-separated CIDRs) + - DNS (text input) + - MTU (number input) + - PersistentKeepalive (number input) + - Notes (textarea) + - Suspend (toggle switch with API call) +- "Save" button calls `updateDevice()` with all fields +- Suspend toggle calls `suspendDevice()` / `unsuspendDevice()` separately +- Show "Suspended" badge when device is suspended + +--- + +## 4. Execution Strategy + +``` +Wave 1 (parallel — backend fixes + frontend API): +├── Task A: Backend fixes (devices.go, peers.go) +├── Task B: Frontend API client updates (servers.ts, devices.ts, peers.ts) + +Wave 2 (parallel — frontend UI): +├── Task C.1: Servers.vue Edit Node modal +├── Task C.2: DeviceDetail.vue Advanced Peer Settings accordion + +Wave 3 (final — verification): +├── Verify go build ./... passes +├── Verify npm build passes (or at least no TS errors) +├── Update plan file with final status +├── Commit all changes +``` + +--- + +## 5. Verification Strategy + +### Backend Verification +```bash +cd apps/server-core +go build ./... # Expected: PASS (no errors) +go vet ./... # Expected: PASS (no warnings) +``` + +### Frontend Verification +```bash +cd apps/dashboard-ui +npx vue-tsc --noEmit # Expected: PASS (no type errors) +npm run build # Expected: PASS (build succeeds) +``` + +### Manual QA (after deploy to wglab) +- Add Node → Edit Node (change MTU, DNS, ListenPort) → Verify +- Create Peer → Edit Advanced Settings (AllowedIPs, DNS, MTU, Keepalive, Notes) → Verify +- Toggle Suspend → Verify peer status changes → Verify firewall rules update +- Verify nftables rules: `nft list chain ip nexusguard forward`, `nft list chain ip nexusguard input` diff --git a/apps/dashboard-ui b/apps/dashboard-ui index d78a95d..3834f89 160000 --- a/apps/dashboard-ui +++ b/apps/dashboard-ui @@ -1 +1 @@ -Subproject commit d78a95d4624b3101bba8661b9bf10066a8bc8abc +Subproject commit 3834f89f258df3eaab1b6e0f8c6d28b276129729 diff --git a/apps/server-core b/apps/server-core index fae1abf..f00e007 160000 --- a/apps/server-core +++ b/apps/server-core @@ -1 +1 @@ -Subproject commit fae1abf339f93e89f53397f513e047c325df170a +Subproject commit f00e007eb24d7e3a8b7c527bc888cbf4beee1557