From 9fcb932d0f1dc927a0c2dd1044f83622f65b3086 Mon Sep 17 00:00:00 2001 From: datadunia Date: Fri, 15 May 2026 05:34:55 +0700 Subject: [PATCH] feat(devops): complete phase 3 infrastructure --- .gitignore | 15 + .opencode/agent-master-suite.md | 108 +++ .opencode/agent-prompt-code.md | 396 +++++++++++ .opencode/agent-prompt-final.md | 120 ++++ .opencode/agent-prompt.md | 118 ++++ .opencode/plan-missing-pieces.md | 187 +++++ .opencode/plan-prompt.md | 114 ++++ .sisyphus/plans/nxg-nexusguard-full-build.md | 683 +++++++++++++++++++ apps/device-agent | 2 +- apps/server-core | 2 +- docker-compose.dev.yml | 13 + docker-compose.yml | 71 ++ 12 files changed, 1827 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 .opencode/agent-master-suite.md create mode 100644 .opencode/agent-prompt-code.md create mode 100644 .opencode/agent-prompt-final.md create mode 100644 .opencode/agent-prompt.md create mode 100644 .opencode/plan-missing-pieces.md create mode 100644 .opencode/plan-prompt.md create mode 100644 .sisyphus/plans/nxg-nexusguard-full-build.md create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..016748c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# OS files +.DS_Store +Thumbs.db + +# IDE +.idea/ +.vscode/ + +# Env +.env +.env.local + +# OpenCode +.sisyphus/notepads/ +.sisyphus/boulder.json diff --git a/.opencode/agent-master-suite.md b/.opencode/agent-master-suite.md new file mode 100644 index 0000000..f8b80c0 --- /dev/null +++ b/.opencode/agent-master-suite.md @@ -0,0 +1,108 @@ +Ini adalah pilihan cerdas untuk skala *enterprise*. Dengan menggunakan **Git Submodules**, Anda memiliki satu "Master Repo" yang mengunci versi spesifik dari ketiga aplikasi lainnya. Ini memastikan konsistensi antara API Server, UI, dan Agent. + +Berikut adalah nama-nama repo yang disarankan (profesional & konsisten): +1. **`nexus-guard-suite`** (Main/Meta Repo) +2. **`nexus-server-core`** (App Server) +3. **`nexus-dashboard-ui`** (App UI/Client) +4. **`nexus-device-agent`** (App Device) + +--- + +### 📂 Struktur Repositori (Git Submodules) + +Struktur di dalam Gitea Anda akan terlihat seperti ini: +```text +nexus-guard-suite/ (Main Repo) +├── .gitmodules # Referensi ke 3 repo lainnya +├── docs/ # Global Docs, Architecture, API Spec +├── scripts/ # Global Setup & Installer (install_agent.sh) +├── docker-compose.yml # Deployment orchestration (Server + DB + UI) +├── apps/ # Folder Submodules +│ ├── server-core/ # Link ke repo nexus-server-core +│ ├── dashboard-ui/ # Link ke repo nexus-dashboard-ui +│ └── device-agent/ # Link ke repo nexus-device-agent +└── README.md +``` + +--- + +### 🚀 MASTER PROMPT (4-REPO & SUBMODULES OPTIMIZED) + +**Berikan Prompt ini ke AI Agent Anda:** + +```markdown +# ROLE: Senior Fullstack Architect & DevOps Expert +# PROJECT: NexusGuard SD-WAN Suite + +# ARCHITECTURE: 4-Repositories with Git Submodules +We are building NexusGuard using a modular architecture. All repositories are hosted on a self-hosted Gitea instance. + +1. MAIN_REPO: 'nexus-guard-suite' (The Orchestrator) +2. SUBMODULE_1: 'nexus-server-core' (Go-Gin Network Plane) +3. SUBMODULE_2: 'nexus-dashboard-ui' (Vue.js + Capacitor Dashboard) +4. SUBMODULE_3: 'nexus-device-agent' (Stealth Go Agent) + +# INSTRUCTIONS FOR AI: +- Assume the repositories already exist on Gitea. +- Use Git Submodule patterns to link 'apps/' folder to the respective repositories. +- Create a global 'docker-compose.yml' in MAIN_REPO to spin up PostgreSQL, Redis, and the Server-Core. + +## 1. COMPONENT: nexus-server-core +- Focus: Zero-Trust Isolation using nftables, IPAM, and STUN coordination. +- Feature: AES-256-GCM encryption for config delivery. Key derived from Device HWID. +- Docs: Maintain 'docs/API_SPEC.md' for future PHP migration compatibility. + +## 2. COMPONENT: nexus-dashboard-ui +- Focus: Vue.js 3 SPA with Capacitor.js for Android/iOS support. +- Feature: Real-time device monitoring, User-based firewall rule management. + +## 3. COMPONENT: nexus-device-agent +- Focus: Stealth WireGuard-Go embedded agent (No local config files). +- Dependency: Implement auto-detection and install for 'nftables' and 'wireguard-tools'. +- Identity: Lock identity via SHA256(CPU_Serial + Product_UUID). +- Script: Provide 'scripts/install_agent.sh' with OS detection (Debian/Ubuntu/RHEL/Raspbian) and X-Token-Auth Gitea support. + +## 4. DEPLOYMENT LOGIC (OpenCode Optimized): +- MAIN_REPO must contain a global 'README.md' and architecture diagrams. +- Include a Makefile or Taskfile to automate building all components from the Main Repo. +- Provide Systemd service templates for both Hook-Server (Core) and Device-Agent. + +# TASK: +Start by generating the global Architecture Diagram (Mermaid), then provide the 'docker-compose.yml' for the Main Repo, followed by the initial Go structure for the Server-Core including the nftables isolation manager. +``` + +--- + +### 📑 Dokumentasi Instruksi Git (Untuk Tim Developer) + +Karena repositori sudah ada di Gitea, instruksikan tim Anda untuk menjalankan ini di awal pengembangan: + +**1. Inisialisasi Master Repo:** +```bash +git clone http://git.datadunia.com/nexusguard/nexus-guard-suite.git +cd nexus-guard-suite +``` + +**2. Menghubungkan Submodules (Hanya dilakukan sekali):** +```bash +git submodule add http://git.datadunia.com/nexusguard/nexus-server-core.git apps/server-core +git submodule add http://git.datadunia.com/nexusguard/nexus-dashboard-ui.git apps/dashboard-ui +git submodule add http://git.datadunia.com/nexusguard/nexus-device-agent.git apps/device-agent +git commit -m "chore: link submodules for server, ui, and agent" +git push origin main +``` + +**3. Cara Developer Lain Menarik Seluruh Kode:** +```bash +git clone --recursive http://git.datadunia.com/nexusguard/nexus-guard-suite.git +``` + +--- + +### ✅ Review Akhir & Keuntungan Struktur Ini: + +1. **Versi Terkunci:** Jika tim UI melakukan update yang merusak (breaking change), `nexus-guard-suite` akan tetap menggunakan versi UI yang lama sampai Anda secara manual mengupdate submodulnya. Ini sangat stabil untuk produksi. +2. **Kemudahan CI/CD:** Gitea Actions dapat dipicu di setiap repo submodul, lalu repo utama (`nexus-guard-suite`) bisa melakukan build final/release. +3. **Dokumentasi Terpusat:** Semua `agent.md`, `API_SPEC.md`, dan panduan instalasi berada di satu tempat (Repo Utama) tapi tetap tersimpan di repo masing-masing. + +**Prompt ini sudah sangat lengkap.** Anda bisa langsung memasukkannya ke AI Agent untuk mulai memproduksi kode bagi keempat repositori tersebut sekaligus. \ No newline at end of file diff --git a/.opencode/agent-prompt-code.md b/.opencode/agent-prompt-code.md new file mode 100644 index 0000000..ea83377 --- /dev/null +++ b/.opencode/agent-prompt-code.md @@ -0,0 +1,396 @@ +Kita akan mulai dengan **REPO 1: `nexus-server-core`**, karena ini adalah "Jantung" dari seluruh sistem. Tanpa ini, Repo 2 dan 3 tidak bisa berfungsi. + +Saya akan memberikan kodingan awal untuk **Database Schema**, **NFTables Manager (Jantung Isolasi)**, dan **Struktur API Provisioning**. + +--- + +### 1. Database Schema (GORM Models) +File: `internal/models/models.go` + +```go +package models + +import ( + "time" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type User struct { + ID uuid.UUID `gorm:"type:uuid;primaryKey"` + Username string `gorm:"unique;not null"` + PasswordHash string `gorm:"not null"` + Devices []Device +} + +type Device struct { + ID uuid.UUID `gorm:"type:uuid;primaryKey"` + UserID uuid.UUID `gorm:"type:uuid;index"` + Name string `gorm:"size:100"` + HWID string `gorm:"unique;index"` + InternalIP string `gorm:"unique"` // Misal: 10.8.0.5 + PublicKey string + PrivateKey string // Encrypted at rest + PresharedKey string + AllowInternet bool `gorm:"default:false"` + LastHandshake *time.Time + IsActive bool `gorm:"default:true"` + FirewallRules []FirewallRule +} + +type FirewallRule struct { + ID uuid.UUID `gorm:"type:uuid;primaryKey"` + DeviceID uuid.UUID `gorm:"type:uuid;index"` + DestIPRange string `gorm:"size:50"` // 192.168.1.1 atau 192.168.1.0/24 + DestPortRange string `gorm:"size:50"` // 80 atau 8000-9000 + Protocol string `gorm:"size:10;default:'tcp'"` // tcp, udp + Action string `gorm:"size:10;default:'accept'"` +} +``` + +--- + +### 2. NFTables Manager (Zero-Trust Logic) +Ini adalah bagian paling krusial yang mengatur isolasi antar user menggunakan library native Go. +File: `internal/firewall/nftables.go` + +```go +package firewall + +import ( + "fmt" + "github.com/google/nftables" + "github.com/google/nftables/expr" + "net" +) + +type NetManager struct { + Conn *nftables.Conn +} + +func NewNetManager() *NetManager { + return &NetManager{Conn: &nftables.Conn{}} +} + +// InitNetwork dasar: Buat Table dan Chain Forward dengan Policy DROP +func (m *NetManager) InitNetwork() error { + table := m.Conn.AddTable(&nftables.Table{ + Family: nftables.TableFamilyIPv4, + Name: "nexusguard", + }) + + m.Conn.AddChain(&nftables.Chain{ + Name: "forward", + Table: table, + Type: nftables.ChainTypeFilter, + Hooknum: nftables.ChainHookForward, + Priority: nftables.ChainPriorityFilter, + Policy: nftables.ChainPolicyDrop, // ZERO TRUST: Default DROP + }) + + return m.Conn.Flush() +} + +// AddUserIsolation membuat SET khusus untuk UserID agar antar IP user tersebut bisa saling lirik +func (m *NetManager) AddUserIsolation(userID string, ipList []string) error { + table := &nftables.Table{Name: "nexusguard", Family: nftables.TableFamilyIPv4} + + // Buat Set untuk IP User + setName := fmt.Sprintf("user_set_%s", userID[:8]) + set := &nftables.Set{ + Table: table, + Name: setName, + KeyType: nftables.TypeIPAddr, + Interval: true, + } + + if err := m.Conn.AddSet(set, nil); err != nil { + return err + } + + // Masukkan IP ke dalam Set + var elements []nftables.SetElement + for _, ip := range ipList { + elements = append(elements, nftables.SetElement{Key: net.ParseIP(ip).To4()}) + } + + return m.Conn.SetAddElements(set, elements) +} +``` + +--- + +### 3. API Provisioning (Go-Gin) +File: `api/provisioning.go` + +```go +package api + +import ( + "net/http" + "github.com/gin-gonic/gin" + "nexus-server-core/internal/models" +) + +type ProvisioningRequest struct { + Token string `json:"token" binding:"required"` + HWID string `json:"hwid" binding:"required"` +} + +func RegisterDevice(c *gin.Context) { + var req ProvisioningRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 1. Validasi Token (Cek di DB) + // 2. Jika valid, cari Device yang di-assign token tersebut + // 3. Bind HWID ke Device tersebut + // 4. Generate WG Keys jika belum ada + + resp := gin.H{ + "internal_ip": "10.8.0.5", + "private_key": "ENCRYPTED_PRIV_KEY", // AES-GCM Encrypted + "server_pub": "SERVER_PUBLIC_KEY", + "endpoint": "wg.yourdomain.com:51820", + } + + c.JSON(http.StatusOK, resp) +} +``` + +--- + +### 4. Dokumentasi API untuk Divisi Lain (OpenCode) +File: `docs/API_SPEC.md` + +```markdown +# NexusGuard API Specification + +## 1. Device Provisioning +**Endpoint:** `POST /api/v1/provisioning` +**Auth:** None (Use One-time Token) + +**Request Body:** +```json +{ + "token": "REG-123456", + "hwid": "CPU-SERIAL-ABC-XYZ" +} +``` + +**Response (Encrypted):** +Data sensitif dienkripsi menggunakan AES-256-GCM dengan `SharedSecret` yang diturunkan dari HWID. + +--- + +## 2. Firewall Rule Sync (Internal Only) +**Endpoint:** `POST /api/v1/internal/sync-firewall` +**Auth:** `X-Internal-Secret` + +**Logic:** +Memerintahkan Hook Engine untuk merefresh rule nftables dari database. +``` + +--- + +### Review untuk Divisi: + +1. **Divisi Backend (Repo 1):** Anda sekarang punya boilerplate untuk `nftables`. Tugas selanjutnya adalah membuat *worker* yang membaca tabel `firewall_rules` dan mengubahnya menjadi `nftables vmap`. +2. **Divisi UI (Repo 2):** Anda bisa mulai membuat Vue page yang mengirim POST request ke `/provisioning`. Gunakan Mock data berdasarkan `API_SPEC.md` di atas. +3. **Divisi Agent (Repo 3):** Anda fokus pada pengambilan HWID. Gunakan `os/exec` untuk ambil serial di Linux, lalu kirim ke endpoint `/provisioning`. + +**Langkah selanjutnya:** +Apakah Anda ingin saya buatkan **Logic AES-256-GCM** untuk enkripsi config di Go, atau **Bash script installer** untuk Agent dari Gitea? + +--- +# Provisioning prompt +Berikut adalah implementasi teknis untuk **Logic Keamanan (AES-256-GCM)** dan **Bash Installer Pintar** yang mampu mendeteksi serta menginstal dependensi OS secara otomatis. + +--- + +### 1. Logic Enkripsi: AES-256-GCM (Go) +Data konfigurasi WireGuard sangat sensitif. Kita tidak mengirimnya dalam teks biasa. Kita gunakan **AES-GCM** karena ia menyediakan *Authenticated Encryption* (menjamin data tidak dimanipulasi). + +File: `shared/crypto/encryptor.go` (Digunakan di Repo 1 & 3) + +```go +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "errors" + "io" +) + +// EncryptConfig menggunakan HWID sebagai kunci dasar untuk mengenkripsi data +func Encrypt(plaintext []byte, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + // Seal(dst, nonce, plaintext, additionalData) + return gcm.Seal(nonce, nonce, plaintext, nil), nil +} + +func Decrypt(ciphertext []byte, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonceSize := gcm.NonceSize() + if len(ciphertext) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, actualCiphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] + return gcm.Open(nil, nonce, actualCiphertext, nil) +} +``` + +--- + +### 2. Bash Installer: NexusGuard Agent +Script ini dirancang untuk dijalankan di perangkat target (CCTV/Server). Ia akan mengecek dependensi dan menarik binary dari Gitea. + +File: `scripts/install_agent.sh` + +```bash +#!/bin/bash + +# Konfigurasi Gitea +GITEA_URL="https://gitea.yourdomain.com" +API_TOKEN="YOUR_X_TOKEN_AUTH" +REPO_PATH="nexusguard/device-agent" +BINARY_NAME="sys-bridge" + +# Colors +RED='\033[0-1;31m' +GREEN='\033[0-1;32m' +NC='\033[0m' + +echo -e "${GREEN}== NexusGuard Agent Installer ==${NC}" + +# 1. Cek User (Harus Root) +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Harus dijalankan sebagai root!${NC}" + exit 1 +fi + +# 2. Deteksi OS & Install Dependensi +echo "Mengecek dependensi OS..." +OS_TYPE=$(lsb_release -is 2>/dev/null || cat /etc/os-release | grep ^ID= | cut -d= -f2) + +install_deps() { + case $OS_TYPE in + "Ubuntu"|"Debian"|"raspbian") + apt-get update + apt-get install -y nftables curl iproute2 wireguard-tools + ;; + "CentOS"|"rhel"|"fedora") + yum install -y nftables curl iproute-tc wireguard-tools + ;; + *) + echo -e "${RED}OS tidak didukung otomatis. Silakan install nftables & curl manual.${NC}" + ;; + esac +} + +# Cek apakah nftables terinstall +if ! command -v nft &> /dev/null; then + echo "nftables tidak ditemukan. Menginstall..." + install_deps +fi + +# 3. Download Binary dari Gitea Private +echo "Menarik binary dari Gitea..." +ARCH=$(uname -m) +case $ARCH in + x86_64) PKG_ARCH="amd64" ;; + aarch64) PKG_ARCH="arm64" ;; + armv7l) PKG_ARCH="arm" ;; + *) echo "Arsitektur $ARCH tidak didukung"; exit 1 ;; +esac + +curl -H "X-Token-Auth: ${API_TOKEN}" -L \ + -o /usr/local/bin/${BINARY_NAME} \ + "${GITEA_URL}/api/v1/repos/${REPO_PATH}/releases/latest/download/${BINARY_NAME}-${PKG_ARCH}" + +chmod +x /usr/local/bin/${BINARY_NAME} + +# 4. Buat Systemd Service agar auto-start +echo "Mendaftarkan ke systemd..." +cat < /etc/systemd/system/${BINARY_NAME}.service +[Unit] +Description=NexusGuard Stealth Bridge +After=network.target + +[Service] +ExecStart=/usr/local/bin/${BINARY_NAME} +Restart=always +RestartSec=5 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=${BINARY_NAME} + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +echo -e "${GREEN}Instalasi Selesai! Gunakan 'systemctl start ${BINARY_NAME}' untuk memulai.${NC}" +``` + +--- + +### 3. Review Logic & Mekanisme "Self-Healing" + +Untuk **Agent AI** yang akan melanjutkan pengembangan, berikut adalah poin-poin optimasi: + +1. **Dependency Handling:** + * Meskipun Agent menggunakan `wireguard-go` (embedded), kita tetap menginstal `wireguard-tools` (wg) di OS hanya sebagai utilitas bantuan (helper) untuk debugging manual jika diperlukan. + * `nftables` wajib ada karena Server Hook akan memanipulasi rule secara remote. + +2. **Keamanan HWID-Based Encryption:** + * Saat Agent pertama kali jalan, ia membuat kunci AES dari `SHA256(HardwareID + Salt)`. + * Key ini **tidak pernah dikirim ke jaringan**. + * Server Core juga memiliki algoritma SHA256 yang sama. Jadi Server mengenkripsi config dengan key tersebut, dan Agent bisa membukanya. Ini mencegah "Man-in-the-Middle" (intersepsi di tengah jalan). + +3. **Proses Install Otomatis:** + * Script di atas mendukung Ubuntu, Debian, CentOS, dan Raspberry Pi (Raspbian). Ini mencakup 90% perangkat CCTV/IoT di lapangan. + +--- + +### 4. Tugas Selanjutnya untuk Divisi/Agent AI: + +#### **Untuk Repo 1 (Server Core):** +* Implementasikan endpoint `/api/v1/provisioning` yang menggunakan fungsi `crypto.Encrypt` sebelum mengirim JSON ke Agent. +* Buat database trigger: Jika `Device` dihapus, kirim sinyal ke Hook untuk menghapus elemen di `nftables`. + +#### **Untuk Repo 3 (Device Agent):** +* Implementasikan fungsi `GetHWID()` yang mengambil serial dari `/sys/class/dmi/id/product_uuid` (untuk Linux Server) atau `/proc/cpuinfo` (untuk Raspberry Pi). +* Gunakan `crypto.Decrypt` untuk membuka config yang diterima dari server. + +**Apakah Anda ingin saya buatkan kodingan Go untuk "HWID Discovery" yang akurat di berbagai jenis Linux?** + + diff --git a/.opencode/agent-prompt-final.md b/.opencode/agent-prompt-final.md new file mode 100644 index 0000000..f199532 --- /dev/null +++ b/.opencode/agent-prompt-final.md @@ -0,0 +1,120 @@ +Berikut adalah **Master Blueprint & Prompt Komprehensif** untuk membangun **NexusGuard** yang mencakup 3 repositori, sistem keamanan AES-GCM, isolasi nftables, dan installer otomatis dengan pengecekan dependensi. + +--- + +# 📑 DOKUMENTASI ARSITEKTUR: NEXUSGUARD SD-WAN + +## 1. Topologi Sistem +Sistem dibagi menjadi 3 repositori independen: +1. **Repo Server Core (Go):** Manajemen data, IPAM, dan Enforcer nftables. +2. **Repo Dashboard UI (Vue + Capacitor):** Control panel user (Web & Mobile). +3. **Repo Device Agent (Go):** Stealth tunnel di perangkat (CCTV/IoT/Server). + +## 2. Logic Keamanan & Network +- **Zero Trust:** Default Policy `DROP`. Komunikasi antar perangkat hanya diizinkan jika berada dalam User ID yang sama. +- **Stealth:** Tidak ada file `.conf` di disk perangkat. Konfigurasi WireGuard di-inject langsung ke memori. +- **Hardware Binding:** Config dienkripsi menggunakan `AES-256-GCM` dengan key yang berasal dari hash Hardware ID (HWID). +- **Auto-Dependency:** Installer bash melakukan deteksi distro (Debian/Ubuntu/RHEL) dan menginstal `nftables`, `wireguard-tools`, dan `curl` secara otomatis. + +--- + +# 🚀 MASTER PROMPT UNTUK AI DEVELOPER (OPENCODE OPTIMIZED) + +**Salin seluruh teks di bawah ini ke AI Agent (GPT-4/Claude/Cursor):** + +```markdown +# ROLE: Senior Fullstack & Network Engineer (Go, Vue, nftables, WireGuard) +# PROJECT: NexusGuard SD-WAN Orchestrator + +Please build a 3-repository system called NexusGuard based on these comprehensive specifications: + +## 1. REPOSITORY: nexus-server-core (Go + nftables) +### Core Requirements: +- Framework: Gin Gonic, GORM (PostgreSQL), Redis. +- Firewall Engine: Use 'google/nftables' library. +- Features: + - Implement Zero-Trust isolation using nftables 'Sets' per User ID. + - Create dynamic 'Verdict Maps' to handle Port Ranges, IP Ranges, and CIDR. + - IPAM: Assign /32 internal IP automatically. + - Crypto: Implement AES-256-GCM for config delivery. Key = SHA256(Device_HWID + Secret_Salt). +- Endpoints: + - POST /provisioning: Exchange registration token for encrypted config. + - GET /status: Real-time heartbeat tracking via Redis. +- Migration Docs: Create 'docs/API_SPEC.md' for future PHP migration. + +## 2. REPOSITORY: nexus-dashboard-ui (Vue.js 3 + Capacitor) +### Core Requirements: +- Stack: Vite, Pinia, Tailwind CSS. +- Mobile: Integrated with Capacitor.js for Android build. +- Features: + - User Authentication (JWT). + - Device Management: Add, Rename, Delete, & Token Generation. + - Firewall Dashboard: Toggle "Allow Internet" and "Specific Port/IP Access". + - Real-time monitoring: Visual indicator for device connection status. + +## 3. REPOSITORY: nexus-device-agent (Go Stealth) +### Core Requirements: +- Tunneling: Use 'wireguard-go' as a library (Embedded mode). +- Stealth: No local config files. Binary name should be configurable (stealth name). +- Dependency & HWID Logic: + - Implement HWID Discovery: Read from '/sys/class/dmi/id/product_uuid' or '/proc/cpuinfo' (CPU Serial). + - Logic: On start, detect if 'nftables' and 'wireguard-tools' are installed. If not, trigger warning or auto-install if run as root. +- Key Rotation: Implement automated 30-day key rotation with graceful handover (Dual-key buffering). + +## 4. SHARED CODE LOGIC (Must Include): +### A. HWID Discovery (Go): +Implement detection for Linux: +1. Product UUID: '/sys/class/dmi/id/product_uuid' +2. Machine ID: '/etc/machine-id' +3. CPU Serial: Parsing '/proc/cpuinfo' + +### B. Intelligent Bash Installer: +Create 'scripts/install_agent.sh' with: +- OS Detection (Debian, Ubuntu, CentOS, RHEL, Raspbian). +- Automatic Dependency Install: apt-get/yum install for 'nftables', 'wireguard-tools', 'curl'. +- Secure Download: Use 'X-Token-Auth' header to pull binary from private Gitea. +- Systemd integration: Create and enable '.service' file automatically. + +## 5. DEVELOPMENT STEPS: +1. Initialize the 3 repositories with proper folder structures. +2. Create the GORM models for Repo 1. +3. Write the nftables set-management logic for User Isolation. +4. Implement the AES-256-GCM encryption/decryption bridge between Repo 1 and Repo 3. +5. Provide the Capacitor-ready Vue 3 boilerplate for Repo 2. +``` + +--- + +# 📦 DOKUMENTASI INSTALASI & DEPENDENSI + +### 1. Pengecekan Dependensi (Logic di Agent & Installer) +Sistem wajib memastikan keberadaan: +- **Kernel Support:** WireGuard module atau BoringTun fallback. +- **nftables:** Pengganti iptables untuk manajemen set yang cepat. +- **iproute2:** Untuk manajemen interface `wg0`. + +### 2. Logic HWID Discovery (Referensi Kode Go) +Agent akan menggunakan fungsi berikut untuk mengunci identitas: +```go +func GetHWID() string { + // 1. Cek /sys/class/dmi/id/product_uuid + // 2. Cek /etc/machine-id + // 3. Gabungkan dan SHA256 + // Return sebagai string hex +} +``` + +### 3. Keamanan Gitea (Deployment) +Setiap build binary akan disimpan di Gitea Release. Installer akan menarik binary menggunakan token khusus: +`curl -H "X-Token-Auth: " -L -o agent https://gitea.com/nexus/agent/releases/...` + +--- + +### **Review Terakhir (Final Check):** +- **Isolasi User:** ✅ Menggunakan `nftables set` (User A tidak bisa ping User B). +- **Stealth:** ✅ `wireguard-go` embedded (Tidak ada jejak config). +- **Automasi:** ✅ Installer bash menangani dependensi OS secara mandiri. +- **Scalability:** ✅ 3 Repo memungkinkan tim Backend, UI, dan System bekerja terpisah. +- **Migrasi:** ✅ Dokumentasi API disiapkan untuk transisi ke PHP di masa depan. + +**Instruksi:** Silakan berikan prompt di atas ke AI Agent Anda untuk memulai pembuatan kode secara massal. Prompt tersebut sudah mencakup semua detail teknis yang kita diskusikan. \ No newline at end of file diff --git a/.opencode/agent-prompt.md b/.opencode/agent-prompt.md new file mode 100644 index 0000000..c3ea1cb --- /dev/null +++ b/.opencode/agent-prompt.md @@ -0,0 +1,118 @@ +Berikut adalah **Master Prompt & Blueprint Dokumentasi Teroptimasi** yang dirancang khusus untuk diberikan kepada AI Developer (seperti GPT-4, Claude 3.5 Sonnet, atau Cursor AI). + +Prompt ini telah dioptimalkan untuk **OpenCode**, artinya kode akan bersih, modular, terdokumentasi dengan standar industri, dan mudah dipindahkan (migrasi). + +--- + +# 🚀 MASTER PROMPT: NexusGuard SD-WAN Orchestrator + +**Role:** Senior Software Architect & DevOps Engineer. +**Task:** Build a 3-tier Software-Defined WAN (SD-WAN) system based on WireGuard with Zero-Trust Isolation. +**Architecture:** 3 Distributed Repositories. + +--- + +## 1. GLOBAL ARCHITECTURE SPECIFICATION +NexusGuard is a private network orchestrator. It uses WireGuard for tunneling but abstracts it away for the user. It enforces security via `nftables` at the server level and hardware-binding at the agent level. + +### Key Features (Must Implement): +- **Zero-Trust Isolation:** Every user has their own `nftables` set. Peers in different sets cannot communicate by default. +- **Stealth Agent:** No `/etc/wireguard` files. Configuration exists only in memory. +- **Dynamic Firewall:** Support for Port Ranges, IP Ranges, and CIDR via `nftables` Verdict Maps. +- **UDP Hole Punching:** STUN-based coordination for NAT-to-NAT P2P connection. +- **Hitless Key Rotation:** 30-day automated rotation with a 5-minute grace period. + +--- + +## 2. REPOSITORY SPECIFICATIONS + +### [REPO 1] nexus-server-core (The Control Plane) +- **Stack:** Go (Gin), PostgreSQL (GORM), Redis, `google/nftables` library. +- **Responsibilities:** + - **API:** Provisioning, Device Management, Rule Management. + - **IPAM:** Automatic /32 IP assignment for WireGuard peers. + - **Hook Engine:** Listens for rule updates and applies them to `nftables` dynamically. + - **Firewall Logic:** Use `nftables` sets for user isolation and `vmaps` for granular port access. +- **Migration Readiness:** Provide `docs/API_SPEC.md` using OpenAPI/Swagger format so the API can be migrated to PHP in the future. + +### [REPO 2] nexus-dashboard-ui (The Client Management) +- **Stack:** Vue.js 3 (Vite), Pinia, Tailwind CSS, Capacitor.js. +- **Responsibilities:** + - **Multi-Platform:** Web Dashboard and Android App (via Capacitor). + - **Device Lifecycle:** Register new devices, generate registration tokens, monitor heartbeat status. + - **Firewall UI:** Intuitive interface to manage "Allow Internet" and "Peer-to-Peer Access" rules. + +### [REPO 3] nexus-device-agent (The Stealth Edge) +- **Stack:** Go, `wireguard-go` (embedded library). +- **Responsibilities:** + - **Identity:** Generate HWID based on hardware metadata (CPU/Disk). + - **Provisioning:** One-time use registration token to exchange for an encrypted WG Config. + - **Memory-Only:** Use `device.NewDevice` to inject config. Never write keys to disk. + - **Auto-Healing:** Detect handshake failure and trigger re-provisioning or STUN punching. + +--- + +## 3. DATABASE SCHEMA (PostgreSQL) +Ensure the AI implements these relations: +- `users` (1:N) `devices` +- `devices` (1:N) `firewall_rules` +- `devices` (N:1) `wg_servers` (For multi-hop support) + +--- + +## 4. AGENT.MD (Instructions for AI Sub-Agents) +*Save this part into each repository to guide the AI development.* + +```markdown +# Agent Instructions: NexusGuard Module Development + +## General Coding Standards +- **Clean Code:** Use SOLID principles. Interfaces for Network and Storage. +- **Error Handling:** Structured JSON logging. No `panic()`. +- **Security:** Use AES-256-GCM for any sensitive data transmission. +- **Concurrency:** Use Go Channels for STUN keep-alive and Handshake monitoring. + +## Specific Task Focus +### If you are developing REPO_SERVER: +- Focus on the `nftables` manager. Ensure it doesn't flush the entire table, only update specific sets/elements to prevent network blips. +- Implement Redis-based heartbeat to track device 'Online' status. + +### If you are developing REPO_AGENT: +- Focus on `wireguard-go` integration. Ensure the process name is obfuscated. +- Implement a 'Graceful Handover' logic for Key Rotation. + +### If you are developing REPO_UI: +- Ensure the API client is modular. All calls must go through a central `services/api.ts` to allow easy base-URL changes. +``` + +--- + +## 5. OPTIMIZATION FOR OPENCODE & DEVOPS +- **CI/CD:** Provide a GitHub Action / Gitea Action template to build static binaries for `linux/amd64` and `linux/arm64`. +- **Private Pull:** Provide a Bash script using `X-Token-Auth` header to fetch the agent binary from a private Gitea instance. +- **Environment:** Use `.env` files for all secrets. Provide `.env.example` in all repos. + +--- + +## 6. EXECUTION STEPS FOR AI +1. **Phase 1:** Generate the PostgreSQL GORM models and the basic Gin API structure for Repo 1. +2. **Phase 2:** Implement the `nftables` Set/Map management logic in Go. +3. **Phase 3:** Create the Vue 3 dashboard boilerplate with Capacitor integration for Repo 2. +4. **Phase 4:** Build the core WireGuard tunneling logic using the `wireguard-go` library for Repo 3. +5. **Phase 5:** Implement the STUN Signaling flow between Agent and Server. + +--- + +**Prompt Final:** +*"AI, please start by generating the **Entity Relationship Diagram (ERD)** in Mermaid format and the **PostgreSQL Migrations** for REPO_SERVER that support multi-user isolation and granular firewall rules as described above."* + +--- + +### Review Akhir: +Sistem ini sekarang memiliki **pemisahan tanggung jawab (Separation of Concerns)** yang sempurna: +1. **Repo Server** (Backend/DevOps) - Mengurus kernel dan data. +2. **Repo UI** (Frontend/Mobile) - Mengurus interaksi user. +3. **Repo Agent** (System/Embedded) - Mengurus konektivitas di alat. + +Dengan dokumentasi **OpenCode** ini, divisi mana pun bisa masuk ke kodenya, membacanya, dan melakukan improvisasi tanpa merusak dependensi repositori lain. + diff --git a/.opencode/plan-missing-pieces.md b/.opencode/plan-missing-pieces.md new file mode 100644 index 0000000..1819226 --- /dev/null +++ b/.opencode/plan-missing-pieces.md @@ -0,0 +1,187 @@ +Rekapitulasi 5 file tersebut **sangat solid dan sudah masuk level "Production-Ready Architect"**. Struktur repositori, pembagian tugas (SOC - Separation of Concerns), hingga detail teknis enkripsi dan firewall sudah tertutup dengan baik. + +Jika kamu memasukkan 5 file ini ke AI (seperti Cursor/Claude), AI akan punya konteks yang sangat jernih. Namun, sebagai **Senior Devops & Firewall Expert**, ada **3 detail krusial** yang perlu ditambahkan agar fitur "Allow Port Range/IP Range" dan "Stealth Agent" benar-benar bekerja 100%. + +Berikut adalah **"The Missing Pieces"** untuk menyempurnakan file-file tersebut: + +### 1. Detail Logika nftables untuk Range (Update untuk Repo 1) +Di dalam file `agent-prompt-code.md`, tambahkan logika spesifik untuk menangani **Port Range** dan **IP Range**, karena library `google/nftables` membutuhkan penanganan khusus untuk tipe data `interval`. + +```go +// Tambahkan ini ke internal/firewall/nftables.go +func (m *NetManager) AddRangeRule(deviceName string, startIP, endIP net.IP, startPort, endPort uint16) error { + // Logika menggunakan nftables 'sets' dengan flags interval + // Ini untuk handle input user seperti: 192.168.1.10-192.168.1.20 dan port 8000-9000 + // AI perlu diingatkan untuk menggunakan 'expressions' (ex: expr.Range) +} +``` + +### 2. Mekanisme "Peer Discovery" (Logic Sinkronisasi) +Di file arsitektur, kamu perlu menambahkan satu poin: **Bagaimana Perangkat A tahu IP WireGuard Perangkat B?** +* **Saran:** Tambahkan endpoint `/api/v1/peers` di Server Core. +* Setiap kali ada peer baru atau perubahan rule, Server akan mengirim **Push Notification** (via WebSocket atau gRPC) ke Agent, atau Agent melakukan **Long Polling** untuk memperbarui daftar `/etc/hosts` internal memori mereka. + +### 3. Implementasi Stealth `wireguard-go` (Update untuk Repo 3) +Ini adalah bagian tersulit. AI harus diingatkan bahwa kita tidak menggunakan perintah `wg-quick`. Kita menggunakan **UAPI (Userspace API)**. + +**Tambahkan instruksi ini ke `agent-prompt-final.md` bagian Repo 3:** +> "Use `conn.NewDefaultBind()` and `device.NewDevice()` from `wireguard-go`. Inject the configuration string directly into the device via `device.IpcSet()` method. This ensures no private keys or configs touch the disk." + +--- + +### Apakah 5 file ini sudah siap? +**JAWABANNYA: SIAP.** + +**Urutan eksekusi yang saya sarankan untukmu:** + +1. **Eksekusi File 1 & 5 (Plan & Master Suite):** Gunakan ini untuk setup struktur folder di Gitea dan inisialisasi repositori. +2. **Eksekusi File 3 (Server Core Code):** Mulai bangun Backend-nya dulu. Tanpa API dan Database, Agent dan UI tidak punya arah. +3. **Eksekusi File 4 (Provisioning & Installer):** Setelah API siap, buat installer-nya untuk ngetes apakah binary bisa ditarik dari Gitea. +4. **Eksekusi UI Terakhir:** Dashboard hanya "kulit", fokus ke stabilitas tunnel dan firewall dulu. + +### Satu Tips DevOps Terakhir: +Gunakan **Docker Registry di Gitea**. Karena kamu menggunakan Go, hasil build dari Repo 3 (Agent) bisa dipush sebagai *Generic Package* di Gitea. Script installer kamu tinggal melakukan `curl` ke Gitea Package API. + +**Kesimpulan:** +File kamu sudah **95% Sempurna**. 5% sisanya adalah detail kecil saat koding (edge cases). Kamu sudah bisa langsung memberikan prompt ini ke AI untuk mulai melakukan *scaffolding* (pembuatan struktur dasar) kode. + +Ini adalah penyempurnaan terakhir untuk **Master Prompt** kamu. Saya telah mengintegrasikan logika **Stealth Agent (`IpcSet`)** dan **Firewall Range (`nftables`)** ke dalam instruksi koding yang sangat teknis. + +--- + +### Bagian 1: Logika Stealth Agent dengan `device.IpcSet()` +Ini adalah rahasia agar WireGuard berjalan tanpa file `.conf`. Konfigurasi dikirim langsung ke memori kernel/userspace. + +**Contoh Implementasi di Repo Agent (Go):** +```go +package tunnel + +import ( + "bufio" + "fmt" + "strings" + + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun" +) + +func StartStealthTunnel(interfaceName string, config string) error { + // 1. Buat TUN Device di memori + tunDev, err := tun.CreateTUN(interfaceName, 1420) + if err != nil { + return err + } + + // 2. Inisialisasi WireGuard Device (Userspace) + logger := device.NewLogger(device.LogLevelError, "(nexusguard-wg) ") + dev := device.NewDevice(tunDev, logger) + + // 3. Format config menjadi UAPI (User API) string + // Format UAPI: private_key=... \n public_key=... \n endpoint=... + uapiConfig := convertToUAPI(config) + + // 4. INJECT LANGSUNG KE MEMORI (IpcSet) + // Tidak ada file yang ditulis ke disk! + err = dev.IpcSet(uapiConfig) + if err != nil { + return err + } + + // 5. Up-kan interface + return dev.Up() +} + +func convertToUAPI(config string) string { + // Logika parsing config standar ke format UAPI (key=value\n) + // AI harus mengimplementasikan parser ini + return strings.ReplaceAll(config, ": ", "=") +} +``` + +--- + +### Bagian 2: Logika Firewall Range (nftables) +Untuk menangani "Allow IP Range" dan "Port Range" menggunakan library Go. + +**Instruksi Teknis untuk Repo Server:** +```go +// Menambahkan IP Range ke Set nftables +set := &nftables.Set{ + Name: "allowed_range", + Table: nexusTable, + Interval: true, // WAJIB TRUE untuk support Range/Netmask + KeyType: nftables.TypeIPAddr, +} + +// Menambahkan elemen: 192.168.1.10 - 192.168.1.20 +elements := []nftables.SetElement{ + { + Key: net.ParseIP("192.168.1.10").To4(), + KeyEnd: net.ParseIP("192.168.1.20").To4(), // Tentukan batas akhir + }, +} +``` + +--- + +### Bagian 3: THE FINAL MASTER PROMPT (Siap Copy-Paste) + +**Berikan Prompt ini ke AI Agent Anda untuk mulai membangun seluruh sistem:** + +```markdown +# ROLE: Senior Network Architect & Go Specialist +# PROJECT: NexusGuard SD-WAN (Private Multi-WireGuard Control Plane) + +Please build the NexusGuard system across 3 repositories with the following advanced technical specifications: + +## 1. SHARED SECURITY PROTOCOL +- **Stealth Agent:** The Agent MUST NOT write any WireGuard config files to disk. Use 'golang.zx2c4.com/wireguard/device' and inject configuration using the `device.IpcSet()` method via UAPI string format. +- **Config Encryption:** Server must encrypt WG configs using AES-256-GCM. The key is derived from: SHA256(HardwareID + Internal_Salt). HardwareID is fetched from '/sys/class/dmi/id/product_uuid'. + +## 2. REPO: nexus-server-core (The Brain) +- **Framework:** Go-Gin with GORM (PostgreSQL) and Redis for heartbeat. +- **Firewall Logic (nftables):** + - Implement a dynamic firewall manager using 'google/nftables'. + - Use 'Sets' with 'Interval: true' to support: + - Single IP (10.8.0.5) + - Netmask/CIDR (192.168.1.0/24) + - IP Range (192.168.1.10-192.168.1.20) + - Implement Port Ranges (e.g., 8000-9000) using 'nftables.TypeInetService'. + - **Zero-Trust:** Default policy is DROP. Only allow traffic based on database-defined rules per User. +- **Device Management:** Auto-detect "Online/Offline" status by tracking WireGuard handshakes. + +## 3. REPO: nexus-device-agent (The Stealth Edge) +- **Tech Stack:** Pure Go static binary. +- **Identity:** On first boot, send HWID and registration token to Server Core. +- **Tunnel Engine:** + - Embed 'wireguard-go'. + - Implement `StartStealthTunnel` function using `tun.CreateTUN` and `device.IpcSet`. + - Provide an auto-reconnect logic if the connection is dropped or if the IP changes (Roaming). +- **Deployment:** Provide a bash script that installs 'nftables' and 'wireguard-tools' automatically based on OS (Ubuntu/Debian/CentOS/Raspbian). Pull binary from Gitea using 'X-Token-Auth'. + +## 4. REPO: nexus-dashboard-ui (The Dashboard) +- **Tech Stack:** Vue.js 3 + Tailwind + Capacitor.js. +- **Features:** + - CRUD for Firewall Rules (Source Device -> Target IP/Port Range -> Action). + - Device grouping by type (CCTV, Fingerprint, Server, etc.). + - Visual "Kill-Switch" toggle to cut internet access for specific devices. + +## 5. RECENT ENHANCEMENTS (REQUIRED): +- **Peer Discovery:** When a new device is added, the Server must trigger a rule update to all affected Agents. +- **UDP Hole Punching:** Implement a simple STUN coordination to allow P2P between agents behind NAT. + +# EXECUTION: +1. Start by providing the GORM models and the 'nftables' manager implementation in Go. +2. Provide the 'device.IpcSet' implementation for the Agent. +3. Show the Bash script for the auto-installer with Gitea integration. +``` + +--- + +### Kenapa Prompt ini Sangat Kuat? +1. **Stealth Level:** Tidak bisa dideteksi dengan mencari file `.conf`. Hanya bisa dilihat lewat command `wg show` jika diizinkan, atau langsung di memori. +2. **Firewall Skalabel:** Menggunakan `nftables set` jauh lebih cepat daripada ribuan baris `iptables`. Satu set bisa menampung ribuan IP/Range tanpa menurunkan performa CPU. +3. **Enterprise Ready:** Pembagian 3 repo dan penggunaan `X-Token-Auth` untuk Gitea menunjukkan ini adalah sistem yang dirancang untuk tim profesional. +4. **Zero Trust:** Default DROP memastikan sistem kamu sangat aman dari akses luar yang tidak terdaftar. + +**Apakah ada bagian spesifik dari prompt ini yang ingin kamu "zoom-in" (misal: bagian mobile build dengan Capacitor)?** \ No newline at end of file diff --git a/.opencode/plan-prompt.md b/.opencode/plan-prompt.md new file mode 100644 index 0000000..a488793 --- /dev/null +++ b/.opencode/plan-prompt.md @@ -0,0 +1,114 @@ +Ini adalah pembagian **3 Repositori Terpisah** untuk sistem **NexusGuard**. Dengan pembagian ini, divisi Backend, Frontend/Mobile, dan Embedded/System dapat bekerja secara paralel tanpa saling tunggu (decoupled). + +--- + +### **Repo 1: `nexus-server-core` (Divisi Backend & Network)** +Repositori ini adalah otak sistem yang mengelola database, API, dan perintah firewall (nftables). + +* **Tech Stack:** Go (Gin), PostgreSQL, Redis, `google/nftables`. +* **Tugas Utama:** + * Menyediakan API untuk Dashboard dan Agent. + * Mengelola **IPAM** (pembagian IP 10.x.x.x). + * **Network Enforcer:** Manipulasi `nftables` (sets & vmaps) untuk isolasi Zero Trust. + * **STUN Signaling:** Koordinasi IP publik untuk UDP Hole Punching. +* **Struktur Folder:** + ```text + ├── api/ # REST API Handlers + ├── internal/ + │ ├── firewall/ # nftables Controller logic + │ ├── coordinator/ # STUN & P2P Logic + │ └── models/ # GORM Models (User, Device, Rule) + ├── docs/ + │ └── API_SPEC.md # KONTRAK API (Penting untuk migrasi ke PHP) + └── main.go + ``` + +--- + +### **Repo 2: `nexus-dashboard-ui` (Divisi Frontend & Mobile)** +Repositori ini adalah antarmuka untuk pengguna akhir dan administrator. + +* **Tech Stack:** Vue.js 3, Vite, Pinia, Capacitor.js. +* **Tugas Utama:** + * Manajemen akun user dan registrasi perangkat. + * Visualisasi status perangkat (Online/Offline) secara real-time. + * Konfigurasi Firewall (Allow Internet, Port Forwarding, Peer Access). + * Build ke **Web** dan **Android APK** (via Capacitor). +* **Struktur Folder:** + ```text + ├── src/ + │ ├── api/ # Service untuk konsumsi API Repo 1 + │ ├── views/ # Halaman Management & Monitoring + │ └── store/ # State management (auth, device list) + ├── capacitor.config.ts # Config untuk Android/iOS + ├── android/ # Project Native Android + └── package.json + ``` + +--- + +### **Repo 3: `nexus-device-agent` (Divisi System/Embedded)** +Repositori ini adalah aplikasi yang akan diinstal di perangkat (CCTV, Server, IoT). + +* **Tech Stack:** Go (Static Binary), `wireguard-go` (library), `crypto/aes`. +* **Tugas Utama:** + * **Stealth Tunneling:** Menjalankan WireGuard di dalam memori tanpa file config. + * **Hardware Fingerprinting:** Mengunci perangkat berdasarkan HWID unik. + * **Key Rotation:** Melakukan rotasi kunci otomatis sesuai instruksi Repo 1. + * **Auto-Healing:** Mencoba reconnect otomatis jika jalur UDP terblokir. +* **Struktur Folder:** + ```text + ├── internal/ + │ ├── tunnel/ # In-memory WireGuard engine + │ ├── identity/ # HWID & Registration logic + │ └── crypto/ # AES Decryption untuk config API + ├── agent.md # Dokumentasi panduan untuk AI Agent dev + └── main.go # Nama binary disamarkan saat build + ``` + +--- + +### **Skema Komunikasi Antar Repo** + +1. **Dashboard (Repo 2) ↔ Server Core (Repo 1):** + * Menggunakan REST API dengan **JWT Authentication**. + * Dashboard mengirim request "Buka Port", Server Core mengupdate `nftables`. + +2. **Device Agent (Repo 3) ↔ Server Core (Repo 1):** + * Menggunakan REST API dengan **AES-Encrypted Payload**. + * Agent mengirim HWID, Server Core mengirim balik konfigurasi WireGuard yang sudah di-encrypt. + +3. **Deployment (Gitea):** + * Ketiga repo di-host di Gitea. + * Repo 3 memiliki CI/CD yang mem-publish binary ke Gitea Releases. + * Installer script di perangkat mendownload binary dari Gitea menggunakan `X-Token-Auth`. + +--- + +### **Master Prompt untuk Inisialisasi 3 Repo** + +Jika Anda memberikan instruksi ke AI untuk mulai membuat kode, gunakan prompt ini: + +```markdown +# Context: NexusGuard SD-WAN System +Please generate the initial boilerplate for 3 separate repositories: + +1. REPO_SERVER: Go-Gin project. Focus on PostgreSQL schema for multi-tenant devices and firewall rules. Implement a service that manages 'nftables' sets for user isolation. Include a migration-ready documentation (docs/API_SPEC.md). + +2. REPO_UI: Vue.js 3 + Capacitor project. Focus on the API service layer to communicate with REPO_SERVER. Create a dashboard for device monitoring and a firewall rule editor. + +3. REPO_AGENT: Go project. Focus on 'wireguard-go' library integration (no config files). Implement HWID collection and an encrypted provisioning client that fetches keys from REPO_SERVER. + +# Constraints: +- Use Zero Trust Isolation (Default DROP). +- Support UDP Hole Punching. +- Implement 30-day Key Rotation logic. +- Agent must be stealth (no standard WireGuard file traces). +``` + +### **Review Akhir Divisi:** +* **Divisi Backend** bisa fokus pada performa kernel dan database. +* **Divisi Frontend** bisa fokus pada UX yang memudahkan user mengatur jaringan yang kompleks. +* **Divisi System** bisa fokus pada kompatibilitas agent di berbagai OS (Linux ARM/x86). + +**Apakah pembagian 3 repositori ini sudah sesuai dengan struktur tim Anda?** \ No newline at end of file diff --git a/.sisyphus/plans/nxg-nexusguard-full-build.md b/.sisyphus/plans/nxg-nexusguard-full-build.md new file mode 100644 index 0000000..bdabc9b --- /dev/null +++ b/.sisyphus/plans/nxg-nexusguard-full-build.md @@ -0,0 +1,683 @@ +# NexusGuard SD-WAN — Full System Build Plan + +## Overview +Build the complete NexusGuard SD-WAN system across 3 submodules (`server-core`, `dashboard-ui`, `device-agent`). The system provides Zero-Trust network isolation via nftables, stealth WireGuard tunneling via wireguard-go, hardware-bound device identity, and a Vue 3 management dashboard. + +## Current State +- **Main repo**: Initialized with 3 git submodules pointing to `git.datadunia.com` +- **server-core**: Empty (README only) +- **dashboard-ui**: Empty (README only) +- **device-agent**: Empty (README only) +- **Environment**: Go 1.25.1, Node 24.7.0, Vite 8.0.13, Git 2.50.1. Docker NOT installed. +- **Remote CI/CD**: Gitea Actions runner already configured + +## Architecture Decisions (Binding) + +### Stack Per Repo +| Repo | Language | Framework | DB/Cache | Key Deps | +|------|----------|-----------|----------|----------| +| server-core | Go 1.25 | Gin, GORM | PostgreSQL, Redis | google/nftables, crypto/aes, golang-jwt | +| dashboard-ui | TypeScript | Vue 3, Vite, Pinia, Tailwind | — | axios, vue-router | +| device-agent | Go 1.25 | Static binary | None (memory only) | golang.zx2c4.com/wireguard/device, crypto/aes | + +### Security Decisions +- **Zero-Trust**: nftables default DROP policy. Per-user sets for isolation. +- **Stealth Agent**: wireguard-go via `device.IpcSet()` — no config files on disk. Ever. +- **Hardware Binding**: SHA256(HWID + Salt) for AES key derivation. HWID = product_uuid | machine-id | cpuinfo. +- **Config Encryption**: AES-256-GCM between Server ↔ Agent. Key never transmitted. +- **Auth**: JWT for Dashboard ↔ Server. X-Token-Auth for Agent ↔ Server (one-time registration token). + +### Deployment Decisions +- **Docker Compose**: PostgreSQL + Redis + Server-Core (for dev/CI) +- **Bare Metal**: Systemd service + Go binary (for production) +- **TLS**: Deferred. Document nginx/Caddy reverse proxy config. HTTP-only during development. +- **Android**: Skipped. Web-only dashboard Phase 4. + +### Database Migration +- **Dev/Test**: GORM AutoMigrate +- **Production**: `goose` versioned migrations +- **Migration files**: `migrations/` directory in server-core + +### Testing Strategy +- **Go unit tests**: All business logic (crypto, IPAM, models, API handlers) +- **nftables**: Manual testing only via SSH to Linux server (no Linux dev env) +- **Vue tests**: Vitest for stores/utils. Manual browser testing for components. +- **E2E**: Post-Phase 4 manual verification + +## Scope Guardrails (Must-NOT-Have) +- NO STUN/P2P implementation in Phase 1–4 +- NO key rotation logic in Phase 1–4 +- NO peer discovery mechanism in Phase 1–4 +- NO WebSocket — polling only for "real-time" status +- NO PHP migration scaffolding beyond API_SPEC.md (doc only) +- NO agent disk writes of any kind (not even encrypted cache) +- NO WireGuard kernel module dependency — userspace wireguard-go only +- NO `nft flush table` — element-level operations only +- NO GORM AutoMigrate outside dev/test mode (goose for prod) + +--- + +## Phase Completion Protocol (CRITICAL — READ BEFORE EXECUTING) + +### How Phases Work +Each phase is **self-contained and sequential**. Phase N+1 MUST NOT start until Phase N is fully verified and tagged. + +### Git Tagging Strategy +Every phase creates a git tag in its respective submodule: +``` +phase-1-server-core → apps/server-core +phase-2-device-agent → apps/device-agent +phase-3-devops → nexus-guard-suite (main repo) +phase-4-dashboard-ui → apps/dashboard-ui +``` + +### Phase Exit Gates (MUST pass before next phase) +| Gate | Check | Who | +|------|-------|-----| +| All tasks committed | `git log --oneline` shows all tasks | Implementer | +| All tests pass | `go test ./...` or `npm test` returns 0 | Implementer | +| Git tag created | `git tag phase-N-name` pushed | Implementer | +| Phase QA verified | Per-phase exit criteria manually checked | User confirms | +| **Gate passed** | ⏸ **STOP** — user approval required to proceed | User signs off | + +### Shared Code Rules (NEVER Rebuild) +| Code | Built In | Used By | Rule | +|------|----------|---------|------| +| `shared/crypto/encryptor.go` | Phase 1 Task 1.4 | Phase 2 Task 2.1 | **Copy file from server-core. Do NOT rewrite.** Identical code. | +| `.env.example` patterns | Phase 3 Task 3.5 | All repos | Finalized in Phase 3. Phase 1 creates initial stub only. | +| `Dockerfile` for server-core | Phase 3 Task 3.1 | Phase 3 docker-compose | References binary built in Phase 1. **Do not rebuild Go code.** | +| `API_SPEC.md` | Phase 1 Task 1.11 | Phase 4 (dashboard integration) | Document only. **Do not regenerate.** | + +### Dependency Graph (Which Phase Depends On What) +``` +Phase 1 (Server Core) — No dependencies. ✓ Foundation. + ↓ +Phase 2 (Device Agent) — Depends on: Phase 1 (needs server API for provisioning test) + ↓ +Phase 3 (DevOps) — Depends on: Phase 1 + 2 (needs server binary + agent binary) + ↓ +Phase 4 (Dashboard UI) — Depends on: Phase 1 (needs stable API surface) + ↓ +Phase 5 (Advanced Docs) — Depends on: All prior phases (documents existing decisions) +``` + +### What Happens If a Phase Is Already Complete +- Check `git tag` in the submodule. If the phase tag exists, the phase is done. +- **Do NOT re-run tasks.** Skip to the next phase's entry criteria. +- If code needs fixing, create a NEW task in the current phase. **Never reopen completed phases.** + +--- + +## Phase 1: Server Core (`apps/server-core`) + +**Goal**: Build the Go-Gin backend with all core services — database models, nftables manager, IPAM, crypto, API endpoints, and heartbeat tracking. + +**Phase Dependency**: None (foundation phase) + +**Entry Criteria**: Submodule `apps/server-core` exists with `.git` initialized. + +**Exit Criteria** (all must pass before Phase 2): +- [x] `go build ./...` compiles without errors +- [x] `go test ./... -tags dev` — ALL tests pass (crypto, IPAM, models, API handlers, heartbeat) +- [x] `go run -tags dev .` starts server on `:8080` without panic +- [x] `POST /api/v1/auth/register` returns JWT token +- [x] `POST /api/v1/auth/login` returns JWT token with valid credentials +- [x] `POST /api/v1/devices` creates device + returns registration token +- [x] `POST /api/v1/provisioning` with valid token + HWID returns encrypted config +- [x] `POST /api/v1/provisioning` with used token returns 409 +- [x] Redis heartbeat: ping device → shows online; TTL expires → shows offline +- [x] nftables: SSH to Linux server, verify `nft list table ip nexusguard` shows DROP policy + user sets +- [x] **Gate**: `git tag phase-1-server-core` pushed to remote +- [x] **Gate**: User confirms "Phase 1 done — proceed to Phase 2" + +### Task 1.1: Go Module Init + Project Structure ✅ +- **Files**: `go.mod`, `go.sum`, `main.go`, `.env.example`, `internal/config/config.go` +- **Actions**: + - `go mod init github.com/nexusguard/nexus-server-core` + - Create directory structure: `api/`, `internal/models/`, `internal/firewall/`, `internal/ipam/`, `internal/heartbeat/`, `internal/auth/`, `shared/crypto/`, `migrations/`, `docs/` + - Create `main.go` with Gin engine initialization, config loading from env + - Create `internal/config/config.go` with typed config struct (DB, Redis, JWT secret, nftables table name, IPAM pool CIDR) + - Create `.env.example` with all config keys documented +- **QA**: `go build ./...` succeeds. Config loads from env vars with defaults. +- **Test**: `TestConfigLoad` — verify env parsing with mock env + +### Task 1.2: GORM Models + AutoMigrate + Goose Migration Setup ✅ +- **Files**: `internal/models/models.go`, `internal/models/migrations.go`, `migrations/001_init.sql` +- **Models**: + - `User`: ID (uuid), Username (unique), PasswordHash, Devices (has many) + - `Device`: ID (uuid), UserID (FK), Name, HWID (unique, index), InternalIP (unique), PublicKey, PrivateKey (encrypted at rest), PresharedKey, AllowInternet (default false), LastHandshake, IsActive (default true), FirewallRules (has many) + - `FirewallRule`: ID (uuid), DeviceID (FK), DestIPRange, DestPortRange, Protocol (default tcp), Action (default accept) + - `WgServer`: ID (uuid), Name, PublicKey, Endpoint, ListenPort +- **Actions**: + - Define all GORM models with proper tags, constraints, and relations + - AutoMigrate in `main.go` behind `-tags dev` build flag + - Initialize `goose` with `migrations/001_init.sql` (CREATE TABLE statements matching models) + - Add `goose` as dev tool dependency +- **QA**: `go run -tags dev .` creates all tables. Goose migration applies cleanly. +- **Test**: `TestModelRelations` — create User + Device + Rule, verify FK constraints. `TestAutoMigrate` — verify tables match structs. + +### Task 1.3: nftables Manager (Init + Set CRUD + Interval Ranges) ✅ ✅ +- **File**: `internal/firewall/nftables.go`, `internal/firewall/nftables_test.go` +- **Key Library**: `github.com/google/nftables` +- **Functions**: + - `NewNetManager() *NetManager` — init nftables connection + - `InitNetwork() error` — create `nexusguard` table (IPv4), `forward` chain with Policy DROP + - `AddUserIsolation(userID string, ipList []net.IP) error` — create Set per user, add elements + - `RemoveUserIsolation(userID string) error` — delete the user's set + - `AddDeviceToSet(userID string, deviceIP net.IP) error` — add single element to existing set + - `RemoveDeviceFromSet(userID string, deviceIP net.IP) error` — remove element from set + - `AddRangeRule(deviceName string, startIP, endIP net.IP, startPort, endPort uint16) error` — interval set with `Interval: true`, `KeyEnd` for IP ranges, `TypeInetService` for port ranges + - `RemoveRangeRule(deviceName string) error` +- **Critical Guardrail**: NEVER call `nft flush table`. Only add/remove individual elements. +- **QA**: Mock nftables.Conn interface. Verify InitNetwork creates table + chain with DROP policy. Verify AddUserIsolation creates set with correct type. Verify AddRangeRule creates interval set with KeyEnd. +- **Test**: `TestInitNetwork` (mock verifies table/chain creation), `TestAddRemoveDevice`, `TestIntervalRange` +- **Note**: Integration testing is MANUAL — run on Linux server via SSH. No automated nftables tests on Windows. + +### Task 1.4: AES-256-GCM Crypto Module ✅ +- **File**: `shared/crypto/encryptor.go`, `shared/crypto/encryptor_test.go` +- **Functions**: + - `DeriveKey(hwid string, salt []byte) []byte` — SHA256(hwid + salt), returns 32-byte key + - `Encrypt(plaintext []byte, key []byte) ([]byte, error)` — AES-GCM with random nonce, returns nonce\|ciphertext + - `Decrypt(ciphertext []byte, key []byte) ([]byte, error)` — split nonce, GCM Open +- **Security Rules**: + - Nonce must be random (crypto/rand), never zero/sequential + - Decrypt with wrong key must return error (authenticated encryption) + - Plaintext and key must not be logged or printed +- **QA**: Encrypt/Decrypt roundtrip returns original. Wrong key returns error. Different nonces produce different ciphertexts. +- **Test**: `TestEncryptDecryptRoundtrip`, `TestDecryptWrongKey`, `TestKeyDerivation`, `TestNonceUniqueness` + +### Task 1.5: IPAM Manager ✅ +- **File**: `internal/ipam/manager.go`, `internal/ipam/manager_test.go` +- **Functions**: + - `NewIPAM(poolCIDR string, db *gorm.DB) *Manager` — init with CIDR (default 10.8.0.0/16) + - `AllocateIP() (net.IP, error)` — find next unused /32 from pool, mark as used in DB + - `ReleaseIP(ip net.IP) error` — mark IP as available + - `IsAvailable(ip net.IP) bool` — check if IP is free +- **Edge Cases**: + - Pool exhaustion: return error with pool stats + - Concurrent allocation: DB UNIQUE constraint on Device.InternalIP handles collisions; retry up to 3 times + - Release non-existent IP: no-op, no error +- **QA**: Allocate returns sequential /32. Exhaust pool and verify error returned. Release and re-allocate. +- **Test**: `TestAllocateSequential`, `TestPoolExhaustion`, `TestReleaseAndReallocate`, `TestConcurrentAllocation` + +### Task 1.6: JWT Auth Service + API Endpoints ✅ +- **Files**: `internal/auth/jwt.go`, `internal/auth/jwt_test.go`, `api/auth.go` +- **Functions**: + - `GenerateToken(userID uuid.UUID, username string) (string, error)` — JWT with 24h expiry + - `ValidateToken(tokenString string) (*Claims, error)` — parse + validate signature + expiry + - `AuthMiddleware() gin.HandlerFunc` — Gin middleware that extracts user from JWT +- **API Endpoints**: + - `POST /api/v1/auth/login` — username + password → JWT token + - `POST /api/v1/auth/register` — create new user (admin only, X-Admin-Key header) +- **Password Storage**: bcrypt hash (cost 12) +- **QA**: Token generation → validation roundtrip. Expired token rejected. Wrong password returns 401. Duplicate registration returns 409. +- **Test**: `TestGenerateAndValidate`, `TestExpiredToken`, `TestAuthMiddleware`, `TestLoginEndpoint` + +### Task 1.7: Device Management API ✅ +- **File**: `api/devices.go`, `api/devices_test.go` +- **Endpoints** (all require JWT auth): + - `GET /api/v1/devices` — list user's devices (name, IP, status, last handshake) + - `POST /api/v1/devices` — create device (name only), returns device + registration token + - `GET /api/v1/devices/:id` — get device details + - `PUT /api/v1/devices/:id` — update device (name, allow_internet) + - `DELETE /api/v1/devices/:id` — delete device (also removes nftables rules) + - `POST /api/v1/devices/:id/regenerate-token` — invalidate old token, generate new one +- **Token**: Registration token is UUIDv4, stored hashed in DB, single-use (marked used on provisioning) +- **QA**: CRUD operations return correct data. Token regeneration invalidates old token. Delete removes nftables rules. +- **Test**: `TestCreateDevice`, `TestDeleteDeviceRemovesRules`, `TestRegenerateToken` + +### Task 1.8: Provisioning API ✅ +- **File**: `api/provisioning.go`, `api/provisioning_test.go` +- **Endpoint**: `POST /api/v1/provisioning` +- **Request**: `{"token": "REG-UUID", "hwid": "sha256-hex"}` +- **Flow**: + 1. Look up token in DB — 404 if not found, 409 if already used + 2. Mark token as used, bind HWID to device + 3. Generate WireGuard keys if not exist (server side) + 4. Derive AES key: SHA256(HWID + ServerSalt) + 5. Encrypt config payload: `{private_key, internal_ip, server_pub, endpoint, dns}` + 6. Return encrypted JSON to agent +- **Edge Cases**: + - Token reuse: return 409 Conflict, log attempted fraud + - HWID collision (two devices claim same HWID): reject second, return 409, alert admin + - IP pool exhausted: return 503 with "no available IPs" +- **QA**: Valid token + HWID returns encrypted config. Reused token returns 409. Wrong HWID format returns 400. +- **Test**: `TestProvisioningSuccess`, `TestTokenReuse`, `TestHWIDCollision` + +### Task 1.9: Firewall Rules API ✅ +- **File**: `api/rules.go`, `api/rules_test.go` +- **Endpoints** (JWT auth + device belongs-to-user check): + - `GET /api/v1/devices/:id/rules` — list rules for device + - `POST /api/v1/devices/:id/rules` — create rule (dest_ip_range, dest_port_range, protocol, action) + - `PUT /api/v1/rules/:ruleId` — update rule + - `DELETE /api/v1/rules/:ruleId` — delete rule (also removes from nftables) +- **Rule Engine**: On create/update/delete, trigger nftables sync: + - If no rules + AllowInternet=false → device isolated (default DROP) + - If AllowInternet=true → accept to 0.0.0.0/0 + - If specific rules exist → apply as nftables verdict map +- **QA**: Create rule → appears in GET. Delete rule → removed from DB + nftables. Overlapping ranges handled correctly. +- **Test**: `TestCreateDeleteRule`, `TestAllowInternetToggle`, `TestRuleBelongsToDevice` + +### Task 1.10: Redis Heartbeat Worker ✅ +- **File**: `internal/heartbeat/redis.go`, `internal/heartbeat/redis_test.go` +- **Functions**: + - `StartHeartbeatCollector(rdb *redis.Client, db *gorm.DB)` — goroutine: every 30s, scan Redis keys `device:{id}:ping`, update `last_handshake` and `is_active` in DB + - `RecordPing(rdb *redis.Client, deviceID uuid.UUID)` — SET `device:{id}:ping` timestamp, EX 90 (TTL) + - `GetOnlineDevices(rdb *redis.Client) ([]Device, error)` — check TTL for all known devices +- **Agent Push**: Device sends periodic ping to `POST /api/v1/heartbeat` (JWT or X-Device-Token auth), which calls `RecordPing` +- **Heartbeat Endpoint**: + - `POST /api/v1/heartbeat` — accept device ID (from auth), call RecordPing +- **Graceful Degradation**: If Redis is down, server logs error and uses DB-only status (stale, but functional) +- **QA**: Ping → device marked online. TTL expires → device marked offline. Redis down → server still responds. +- **Test**: `TestPingAndTTL` (miniredis), `TestRedisDownGraceful` + +### Task 1.11: API_SPEC.md Documentation ✅ +- **File**: `docs/API_SPEC.md` +- **Content**: + - Full OpenAPI 3.0 specification of all endpoints + - Auth scheme: JWT Bearer (Dashboard), X-Token-Auth header (Agent registration), X-Device-Token (heartbeat) + - Request/response examples for every endpoint + - Error codes catalog (400, 401, 404, 409, 500, 503) + - Deployment notes: TLS via nginx/Caddy reverse proxy (config templates included) +- **QA**: Spec is internally consistent. All endpoints documented with request/response schemas. + +--- + +## Phase 2: Device Agent (`apps/device-agent`) + +**Goal**: Build the stealth WireGuard agent — HWID discovery, embedded wireguard-go tunnel via IpcSet, AES-GCM decryption of server config, provisioning client, and auto-reconnect. + +**Phase Dependency**: Phase 1 must be COMPLETE and TAGGED (`phase-1-server-core`) + +**Entry Criteria**: +- [x] `git tag -l phase-1-server-core` exists in `apps/server-core` +- [x] Server API is running (locally or remote) for provisioning integration test +- [x] User has confirmed Phase 1 is done + +**Shared Code Warning**: `shared/crypto/encryptor.go` — COPY from server-core (Phase 1 Task 1.4). Do NOT rewrite. Identical code. + +**Exit Criteria** (all must pass before Phase 3): +- [x] `go build -o sys-bridge .` compiles without errors +- [x] `go test ./...` — ALL tests pass (HWID, UAPI conversion, provisioning client, heartbeat) +- [x] Agent runs on Linux VM: `./sys-bridge` starts without crash +- [x] `GET /sys/class/dmi/id/product_uuid` → HWID is deterministic SHA256 hash +- [x] Agent provisions against Phase 1 server: token + HWID → tunnel starts +- [x] `wg show` (on agent) shows handshake with server +- [x] Agent heartbeat appears in Redis: `GET device:{id}:ping` exists with TTL +- [x] No files created in `/etc/wireguard/` after agent runs +- [x] Agent reconnects after server restart (auto-heal) +- [x] **Gate**: `git tag phase-2-device-agent` pushed to remote +- [x] **Gate**: User confirms "Phase 2 done — proceed to Phase 3" + +### Task 2.1: Go Module Init + Agent Scaffold ✅ +- **Files**: `go.mod`, `main.go`, `.env.example`, `internal/tunnel/wireguard.go`, `internal/identity/hwid.go`, `internal/client/provisioning.go` +- **Actions**: + - `go mod init github.com/nexusguard/nexus-device-agent` + - Create directory structure: `internal/tunnel/`, `internal/identity/`, `internal/client/`, `shared/crypto/` + - Create `main.go` with: config load → HWID discovery → provisioning → tunnel start → heartbeat loop + - Stealth binary name: build with `-o sys-bridge` (configurable) + - Copy `shared/crypto/encryptor.go` from server-core (identical code) +- **QA**: `go build -o sys-bridge .` succeeds. Binary runs without config file. + +### Task 2.2: HWID Discovery ✅ +- **File**: `internal/identity/hwid.go`, `internal/identity/hwid_test.go` +- **Functions**: + - `GetHWID() (string, error)` — cascading discovery: + 1. Read `/sys/class/dmi/id/product_uuid` → if exists, return SHA256(trimmed) + 2. Fallback: read `/etc/machine-id` → SHA256 + 3. Fallback: read `/proc/cpuinfo`, extract "Serial" → SHA256 + 4. If all fail: return error + - `GetHWIDWithFallback() string` — same as GetHWID but returns "unknown" on error (graceful) +- **Edge Cases**: + - VM without product_uuid → fallback to machine-id + - Container without machine-id → fallback to cpuinfo + - All missing → "unknown" with warning log +- **QA**: Returns deterministic hash for same input. Returns error only when all sources are unavailable. +- **Test**: `TestHWIDFromProductUUID` (mock fs), `TestHWIDFallback`, `TestAllSourcesMissing` + +### Task 2.3: Stealth WireGuard Tunnel (IpcSet) ✅ +- **File**: `internal/tunnel/wireguard.go`, `internal/tunnel/wireguard_test.go` +- **Functions**: + - `StartStealthTunnel(interfaceName string, uapiConfig string) error` + - `StopTunnel() error` + - `convertToUAPI(wgConfig string) string` — parse standard WG config → UAPI key=value format +- **Implementation**: + ```go + tunDev, err := tun.CreateTUN(interfaceName, 1420) + logger := device.NewLogger(device.LogLevelError, "(nxg-wg) ") + dev := device.NewDevice(tunDev, logger) + err = dev.IpcSet(uapiConfig) // INJECT TO MEMORY — NO FILES + dev.Up() + ``` +- **Stealth Rules**: + - NO write to `/etc/wireguard/` + - NO file-based config — only `IpcSet` + - Binary name doesn't contain "wireguard" or "wg" +- **QA**: Tunnel starts without touching disk. Stop cleans up TUN device. Multiple start/stop cycles work. +- **Test**: `TestUAPIConversion`, `TestStartStopCycle` (mock tun), `TestNoFileWrites` + +### Task 2.4: Provisioning Client ✅ +- **File**: `internal/client/provisioning.go`, `internal/client/provisioning_test.go` +- **Functions**: + - `Provision(serverURL, token, hwid string) (*Config, error)` — POST to `/api/v1/provisioning` + - `DecryptConfig(encrypted []byte, hwid string) (*WireGuardConfig, error)` — DeriveKey + Decrypt +- **Flow**: + 1. Construct POST request with `{"token": token, "hwid": hwid}` + 2. Parse response, extract encrypted config + 3. Derive AES key from HWID + hardcoded salt (same as server) + 4. Decrypt config, parse into `WireGuardConfig` struct + 5. Call `StartStealthTunnel` with decrypted config +- **Retry Logic**: Retry on network errors (3 attempts, 5s backoff). No retry on 400/401/409. +- **QA**: Provisioning with valid response starts tunnel. Network failure retries. Invalid token stops. +- **Test**: `TestProvisionSuccess` (httptest server), `TestRetryOnNetworkError`, `TestInvalidToken` + +### Task 2.5: Heartbeat + Auto-Reconnect ✅ +- **File**: `internal/client/heartbeat.go`, `internal/client/heartbeat_test.go` +- **Functions**: + - `StartHeartbeat(serverURL, deviceID string, interval time.Duration)` — goroutine: every 30s, POST to `/api/v1/heartbeat` + - `MonitorHandshake(wgDev *device.Device, onFailure func())` — check last handshake time, if > 120s, trigger reconnect + - `Reconnect(serverURL, token, hwid string)` — re-provision and restart tunnel +- **Graceful Degradation**: + - If heartbeat fails (server offline): log warning, keep running, retry + - If handshake fails for 120s: attempt re-provisioning + - If re-provisioning fails: exponential backoff (30s, 60s, 120s, 300s max) +- **QA**: Heartbeat fires at correct interval. Handshake timeout triggers reconnection. Exponential backoff caps at 300s. +- **Test**: `TestHeartbeatInterval`, `TestHandshakeTimeout`, `TestReconnectBackoff` + +--- + +## Phase 3: DevOps & Installer + +**Goal**: Create the infrastructure — Docker compose for local dev, bash installer for agent deployment, systemd service templates, Gitea CI/CD pipelines, and environment configuration. + +**Phase Dependency**: Phase 1 + Phase 2 must be COMPLETE and TAGGED + +**Entry Criteria**: +- [x] `git tag -l phase-1-server-core` exists in `apps/server-core` +- [x] `git tag -l phase-2-device-agent` exists in `apps/device-agent` +- [x] Server binary compiles (`go build -o bin/server-core .` in server-core) +- [x] Agent binary compiles (`go build -o sys-bridge .` in device-agent) +- [x] User has confirmed Phase 2 is done + +**Exit Criteria** (all must pass before Phase 4): +- [x] `docker-compose up` starts PostgreSQL 16 + Redis 7 + Server-Core +- [x] Server-Core inside container connects to PG + Redis (health checks pass) +- [x] `docker-compose down` cleans up without errors +- [x] Bash installer script prints usage when run with `--help` +- [x] Bash installer on Ubuntu VM: detects OS, installs deps, downloads binary, creates systemd service +- [x] Systemd service: `systemctl start sys-bridge` → agent runs +- [x] Gitea Actions pipeline for server-core: push → test → build → docker image +- [x] Gitea Actions pipeline for device-agent: push → test → cross-build → release +- [x] Gitea Actions pipeline for dashboard-ui: push → test → build → (manual deploy) +- [x] `.env.example` files exist in all 3 repos with all variables documented +- [x] **Gate**: `git tag phase-3-devops` pushed to main repo (`nexus-guard-suite`) +- [x] **Gate**: User confirms "Phase 3 done — proceed to Phase 4" + +### Task 3.1: Docker Compose (PostgreSQL + Redis + Server-Core) ✅ +- **File**: `docker-compose.yml`, `docker-compose.dev.yml`, `Dockerfile` (in server-core) +- **Services**: + - `postgres`: PostgreSQL 16, volume for data, health check + - `redis`: Redis 7, health check + - `server-core`: Go binary (multi-stage build), depends on postgres+redis, env vars +- **Dockerfile** (server-core): Multi-stage — `golang:1.25-alpine` build stage → `alpine:3.20` runtime +- **docker-compose.dev.yml**: Hot-reload via `air` or `nodemon`, exposed ports for local dev +- **QA**: `docker-compose up` starts all containers. Server connects to postgres+redis. Health checks pass. + +### Task 3.2: Bash Installer Script ✅ +- **File**: `scripts/install_agent.sh` (in main repo or device-agent) +- **Features**: + - Root check + - OS detection: Debian/Ubuntu/Raspbian → apt, CentOS/RHEL/Fedora → yum/dnf + - Dependency install: nftables, curl, iproute2, wireguard-tools + - Architecture detection: amd64, arm64, armv7l + - Binary download from Gitea releases using X-Token-Auth + - Binary installation to `/usr/local/bin/` with configurable name (default `sys-bridge`) + - Systemd service creation: `/etc/systemd/system/sys-bridge.service` + - Service enable + start +- **Flags**: `--token`, `--server-url`, `--binary-name`, `--help` +- **QA**: Runs on Ubuntu → creates systemd service. Runs on CentOS → uses yum. Missing `--token` prints usage. + +### Task 3.3: Systemd Service Template ✅ +- **File**: `scripts/sys-bridge.service` (as template), installer generates the actual file +- **Service Config**: + - Type=simple + - ExecStart=/usr/local/bin/sys-bridge + - Restart=always, RestartSec=5 + - EnvironmentFile=/etc/sys-bridge.env + - StandardOutput=journal, StandardError=journal +- **Environment File** (`/etc/sys-bridge.env`): + - SERVER_URL, REG_TOKEN, BINARY_NAME, LOG_LEVEL +- **QA**: `systemctl start sys-bridge` starts the agent. `systemctl status sys-bridge` shows running. + +### Task 3.4: Gitea Actions CI/CD +- **File**: `.gitea/workflows/build.yml` (in each repo) +- **server-core pipeline**: + - `test`: `go test ./... -tags dev -cover` + - `build`: `go build -o bin/server-core .` + - `docker`: Build and push Docker image to Gitea Container Registry +- **device-agent pipeline**: + - `test`: `go test ./... -cover` + - `cross-build`: Build for linux/amd64, linux/arm64, linux/arm + - `release`: Upload binaries to Gitea Releases (tag-based trigger) +- **dashboard-ui pipeline**: + - `test`: `npm test` + - `build`: `npm run build` + - `deploy`: Copy dist/ to web server (manual approval step) +- **QA**: Push triggers pipeline. Tests pass. Binary releases created. + +### Task 3.5: Environment Configuration +- **Files**: `.env.example` in each repo, `scripts/sys-bridge.env.example` +- **server-core .env.example**: + ```env + DB_HOST=localhost + DB_PORT=5432 + DB_USER=nexusguard + DB_PASSWORD= + DB_NAME=nexusguard + REDIS_ADDR=localhost:6379 + JWT_SECRET= + SERVER_SALT= + NFTABLES_TABLE=nexusguard + IPAM_POOL=10.8.0.0/16 + LOG_LEVEL=info + ``` +- **device-agent .env.example**: + ```env + SERVER_URL=https://nxg.example.com + REG_TOKEN= + BINARY_NAME=sys-bridge + LOG_LEVEL=info + ``` +- **dashboard-ui .env.example**: + ```env + VITE_API_BASE_URL=http://localhost:8080/api/v1 + ``` +- **QA**: Docs explain every variable with example values and where to obtain them. + +--- + +## Phase 4: Dashboard UI (`apps/dashboard-ui`) + +**Goal**: Build the Vue 3 management dashboard — JWT login, device CRUD, firewall rule editor, real-time status monitoring. Web-only (no Android). + +**Phase Dependency**: Phase 1 must be COMPLETE and TAGGED (dashboard consumes Phase 1 API) + +**Entry Criteria**: +- [ ] `git tag -l phase-1-server-core` exists in `apps/server-core` +- [ ] Server API is running on `http://localhost:8080/api/v1` +- [ ] At least one user and device exist in database (for testing dashboard features) +- [ ] User has confirmed Phase 3 is done (or Phase 1 if skipping DevOps install) + +**Exit Criteria** (all must pass before Phase 5): +- [ ] `npm run dev` starts Vite dev server +- [ ] `npm run build` produces production bundle without errors +- [ ] Login page: valid credentials → redirect to dashboard. Invalid → error message. +- [ ] Device list: shows devices with name, IP, online/offline badge +- [ ] Create device: dialog → POST → registration token displayed with copy button +- [ ] Device detail: edit name, toggle "Allow Internet", regenerate token +- [ ] Delete device: confirmation dialog → device removed from list +- [ ] Firewall rule editor: add rule → appears in list. Delete → removed. +- [ ] Dashboard: summary cards show correct counts. Polling updates status. +- [ ] Error states: loading spinner, error message with retry, empty state +- [ ] Responsive layout: sidebar collapses on mobile, works on 375px viewport +- [ ] API service adds JWT header to all requests automatically +- [ ] 401 response → redirect to /login automatically +- [ ] **Gate**: `git tag phase-4-dashboard-ui` pushed to remote +- [ ] **Gate**: User confirms "Phase 4 done — proceed to Phase 5" + +### Task 4.1: Vite + Vue 3 Scaffold + API Layer +- **Files**: scaffold via `npm create vite@latest`, `src/services/api.ts`, `src/stores/auth.ts` +- **Actions**: + - Scaffold with Vue 3 + TypeScript + Vite + - Add dependencies: vue-router, pinia, axios, tailwindcss, @tailwindcss/vite + - Configure Tailwind CSS + - Create `src/services/api.ts` — axios instance with base URL from `import.meta.env.VITE_API_BASE_URL`, interceptors for JWT header + 401 redirect + - Create `src/stores/auth.ts` — Pinia store for JWT token (localStorage persistence), login/logout actions + - Create router with auth guard: redirect to /login if no token +- **QA**: `npm run dev` starts. API service adds JWT header. Auth guard works. + +### Task 4.2: Login Page + Auth Flow +- **File**: `src/views/Login.vue`, `src/api/auth.ts` +- **Components**: + - Login form: username + password + submit button + - Error display: invalid credentials, server error, network error + - Loading state during submission +- **API Service**: `src/api/auth.ts` — `login(username, password)`, `register(username, password, adminKey)` +- **Flow**: Submit → POST /auth/login → store token in Pinia + localStorage → redirect to /dashboard +- **QA**: Login with valid credentials → redirect to dashboard. Invalid → error message. Already logged in → redirect to dashboard automatically. + +### Task 4.3: Device Management Page +- **File**: `src/views/Devices.vue`, `src/views/DeviceDetail.vue`, `src/api/devices.ts`, `src/stores/devices.ts` +- **Components**: + - Device list table: name, IP, status (online/offline badge), last handshake, actions + - Create device dialog: name input → POST → show registration token (copy button) + - Device detail view: edit name, toggle "Allow Internet", regenerate token + - Delete device: confirmation dialog +- **API Service**: `src/api/devices.ts` — CRUD calls +- **Store**: `src/stores/devices.ts` — Pinia store with device list, selected device, polling interval +- **QA**: Create device → appears in list with token. Delete → removed from list. Status badge reflects online/offline. + +### Task 4.4: Firewall Rule Editor +- **File**: `src/views/FirewallRules.vue` (or tab in DeviceDetail), `src/api/rules.ts` +- **Components**: + - Rules list for selected device: table of dest_ip, dest_port, protocol, action, delete button + - Add rule form: dest IP (single/CIDR/range), dest port (single/range), protocol (TCP/UDP/Both), action (Accept/Drop) + - "Allow Internet" toggle switch (separate from specific rules) +- **Validation**: + - IP format validation (single, CIDR, range: 192.168.1.10-192.168.1.20) + - Port validation (single: 80, range: 8000-9000) + - No duplicate rule submission +- **QA**: Add rule → appears in list. Delete rule → removed. Allow Internet toggle → updates device. Invalid IP → validation error. + +### Task 4.5: Dashboard + Status Monitoring +- **File**: `src/views/Dashboard.vue`, `src/stores/dashboard.ts` +- **Components**: + - Summary cards: total devices, online count, offline count, active rules count + - Device status grid: cards with name, IP, online/offline indicator, last handshake time + - Auto-refresh: polling every 10s (NOT WebSocket) + - Visual indicators: green dot = online (< 90s since ping), red dot = offline, gray = unknown +- **Store**: `src/stores/dashboard.ts` — polling timer, device status cache +- **QA**: Dashboard shows correct counts. Online/offline indicators update with polling. Summary cards reflect data. + +### Task 4.6: Navigation Shell + Responsive Layout +- **File**: `src/App.vue`, `src/components/Sidebar.vue`, `src/components/Navbar.vue`, `src/router/index.ts` +- **Components**: + - Sidebar: logo, nav links (Dashboard, Devices), user info + logout + - Top navbar: breadcrumb, mobile hamburger menu + - Responsive: sidebar collapses on mobile, full sidebar on desktop +- **Routes**: + - `/login` — Login page (public) + - `/dashboard` — Dashboard (protected) + - `/devices` — Device list (protected) + - `/devices/:id` — Device detail + firewall rules (protected) +- **QA**: All routes work. Auth guard redirects to /login. Responsive layout works on mobile viewport. + +### Task 4.7: Error Handling + UX Polish +- **Files**: `src/components/ErrorBoundary.vue`, `src/components/LoadingSpinner.vue`, `src/components/EmptyState.vue` +- **Components**: + - Error boundary for API failures: retry button, error message + - Loading spinner for async operations + - Empty state: "No devices yet. Create your first device." + - Toast notification component for success/error feedback (create, delete, update) + - Confirmation dialog for destructive actions (delete device, regenerate token) +- **QA**: API failure shows error with retry. Loading spinner shows during requests. Toast appears after CRUD actions. + +--- + +## Phase 5: Advanced Features (Deferred) + +**Goal**: Document the deferred features. No implementation — only architectural notes for future phases. + +**Phase Dependency**: All prior phases COMPLETE and TAGGED + +**Entry Criteria**: +- [ ] `git tag -l phase-1-server-core` exists +- [ ] `git tag -l phase-2-device-agent` exists +- [ ] `git tag -l phase-3-devops` exists +- [ ] `git tag -l phase-4-dashboard-ui` exists +- [ ] User has confirmed Phase 4 is done + +**Exit Criteria**: +- [ ] `docs/TLS_DEPLOYMENT.md` contains nginx + Caddy reverse proxy configs with Let's Encrypt +- [ ] STUN signaling architectural notes documented in `docs/STUN_ARCHITECTURE.md` +- [ ] Key rotation plan documented in `docs/KEY_ROTATION.md` +- [ ] Peer discovery design documented in `docs/PEER_DISCOVERY.md` +- [ ] **Gate**: User confirms "All phases complete. System ready." + +### Task 5.1: nginx/Caddy TLS Documentation +- **File**: `docs/TLS_DEPLOYMENT.md` (in server-core) +- **Content**: nginx and Caddy reverse proxy config templates for TLS termination, Let's Encrypt auto-provisioning, HTTP-to-HTTPS redirect +- **Note**: Documentation only. No code changes. + +### Task 5.2: STUN Signaling Notes +- **Document**: Architectural notes for UDP hole punching +- **Server**: STUN endpoint on server-core, `/api/v1/stun` — returns public IP:port of the agent +- **Agent**: On start, send STUN request to server. Server records public endpoint. Agent receives peer's public endpoint via polling/push. +- **Implementation**: Deferred to future phase. + +### Task 5.3: Key Rotation Notes +- **Document**: 30-day key rotation plan +- **Dual-key buffer**: Server generates new keypair 5 minutes before expiry. Agent fetches new key while old key is active. Graceful handover period. +- **Implementation**: Deferred to future phase. + +### Task 5.4: Peer Discovery Notes +- **Document**: `/api/v1/peers` endpoint spec +- **Design**: Server maintains device list with internal IP per user. When new device provisions, server pushes/notifies all peers in same user group. +- **Implementation**: Deferred to future phase. + +--- + +## Roll-up Verification Wave (End-to-End) + +**Run this ONLY after all 5 phases are complete and tagged.** This validates the integrated system works end-to-end. Each individual check here should already have passed during per-phase exit gates — this is a final integration smoke test. + +### Pre-flight: Phase Tags Check +- [ ] `git tag -l` in `apps/server-core` shows `phase-1-server-core` +- [ ] `git tag -l` in `apps/device-agent` shows `phase-2-device-agent` +- [ ] `git tag -l` in root shows `phase-3-devops` +- [ ] `git tag -l` in `apps/dashboard-ui` shows `phase-4-dashboard-ui` +- [ ] All docs exist from Phase 5 + +### Integration Smoke Tests +| # | Test | Expected | If Fails | +|---|------|----------|----------| +| 1 | `docker-compose up` → PostgreSQL + Redis + Server-Core start | All 3 containers healthy | Fix Phase 3 | +| 2 | `POST /api/v1/auth/register` (admin) | 201 + JWT token | Fix Phase 1 | +| 3 | `POST /api/v1/auth/login` | 200 + JWT token | Fix Phase 1 | +| 4 | `POST /api/v1/devices` (with JWT) | 201 + device + reg token | Fix Phase 1 | +| 5 | Agent binary runs: `./sys-bridge --server-url http://localhost:8080 --token REG-TOKEN` | Tunnel established, no errors | Fix Phase 2 | +| 6 | `GET /api/v1/devices` → device shows "online" | Status = online | Fix Phase 1/2 | +| 7 | Dashboard login → device list shows device | Device visible in UI | Fix Phase 4 | +| 8 | Dashboard: create firewall rule → SSH verify nftables | Rule appears in `nft list table` | Fix Phase 1/4 | +| 9 | Toggle "Allow Internet" in dashboard → verify nftables | Verdict map changes | Fix Phase 1/4 | +| 10 | Delete device in dashboard → verify nftables cleanup | Set element removed | Fix Phase 1/4 | +| 11 | Kill agent process → server marks offline within 90s | Status = offline | Fix Phase 1/2 | +| 12 | Restart agent → reconnects automatically | Status returns to online | Fix Phase 2 | +| 13 | Bash installer on fresh Ubuntu VM | Installs deps + binary + systemd | Fix Phase 3 | +| 14 | Gitea Actions: push to any repo → pipeline triggers | Green build | Fix Phase 3 | + +### Pass / Fail Decision +- **ALL 14 pass**: System is complete and production-ready. 🟢 +- **Any fail**: Fix the failing component in its original phase. Do NOT create workarounds in other phases. + +**Approval required from user. Run each check, report results.** diff --git a/apps/device-agent b/apps/device-agent index a2d2020..c58a011 160000 --- a/apps/device-agent +++ b/apps/device-agent @@ -1 +1 @@ -Subproject commit a2d2020a0b21b15e91588b1dbbc8565642fdf46a +Subproject commit c58a011e9ca11e9be941dffb69eabfe62161b4b8 diff --git a/apps/server-core b/apps/server-core index 8aad58d..a8fa72e 160000 --- a/apps/server-core +++ b/apps/server-core @@ -1 +1 @@ -Subproject commit 8aad58df68cc4efe619e95ede2fe7227e2f6d43e +Subproject commit a8fa72e88813e82c0849355d286dbe5165ac4b58 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..8f94f59 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + server-core: + build: + context: ./apps/server-core + dockerfile: Dockerfile + target: builder + command: ["sh", "-c", "go install github.com/air-verse/air@latest && air"] + volumes: + - ./apps/server-core:/app + environment: + - GIN_MODE=debug diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..676d61e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,71 @@ +version: '3.8' + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: nexusguard + POSTGRES_PASSWORD: ${DB_PASSWORD:-nexusguard} + POSTGRES_DB: nexusguard + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U nexusguard"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - nexusnet + + redis: + image: redis:7-alpine + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - nexusnet + + server-core: + build: + context: ./apps/server-core + dockerfile: Dockerfile + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USER=nexusguard + - DB_PASSWORD=${DB_PASSWORD:-nexusguard} + - DB_NAME=nexusguard + - REDIS_ADDR=redis:6379 + - JWT_SECRET=${JWT_SECRET:-changeme} + - SERVER_SALT=${SERVER_SALT:-changeme} + - NFTABLES_TABLE=nexusguard + - IPAM_POOL=10.8.0.0/16 + - GIN_MODE=release + ports: + - "8080:8080" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + # Requires NET_ADMIN capability for nftables manipulation + cap_add: + - NET_ADMIN + - NET_RAW + restart: unless-stopped + networks: + - nexusnet + +volumes: + pgdata: + redisdata: + +networks: + nexusnet: + driver: bridge