feat: complete WGDashboard config parity
Backend (server-core): - Extend firewall NetManager with AddForwardRule, AddInputRule via nft CLI - Sync FORWARD rules on peer create, update, delete, suspend/unsuspend - Sync INPUT rules on server create, update (port change), delete - Fix duplicate import and missing firewall cleanup calls Frontend (dashboard-ui): - Add Edit Node modal for MTU, DNS, ListenPort configuration - Add Advanced Peer Settings accordion (AllowedIPs, DNS, MTU, Keepalive, Notes) - Add Suspend/Unsuspend toggle with SUSPENDED badge - Update API client for new endpoints and fields
This commit is contained in:
@@ -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`
|
||||
+1
-1
Submodule apps/dashboard-ui updated: d78a95d462...3834f89f25
+1
-1
Submodule apps/server-core updated: fae1abf339...f00e007eb2
Reference in New Issue
Block a user