From 5dc449b7afb75e4fbf9ea6580a3cdc09606b6767 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 1 Aug 2026 15:33:35 +0000 Subject: [PATCH] Revert "refactor: move Go source into source/ directory" This reverts commit 78e316af6414e66bdc40b7321e9441f27daa787a. --- cmd/wallarm/main.go | 334 ++++++++++++++++++ deploy.sh | 4 +- source/go.mod => go.mod | 0 source/go.sum => go.sum | 0 .../internal => internal}/native/native.go | 0 .../preflight/preflight.go | 0 .../internal => internal}/shared/shared.go | 0 {source/internal => internal}/state/state.go | 0 .../internal => internal}/tunnel/tunnel.go | 0 {source/internal => internal}/ui/ui.go | 0 10 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 cmd/wallarm/main.go rename source/go.mod => go.mod (100%) rename source/go.sum => go.sum (100%) rename {source/internal => internal}/native/native.go (100%) rename {source/internal => internal}/preflight/preflight.go (100%) rename {source/internal => internal}/shared/shared.go (100%) rename {source/internal => internal}/state/state.go (100%) rename {source/internal => internal}/tunnel/tunnel.go (100%) rename {source/internal => internal}/ui/ui.go (100%) diff --git a/cmd/wallarm/main.go b/cmd/wallarm/main.go new file mode 100644 index 0000000..b89d3e1 --- /dev/null +++ b/cmd/wallarm/main.go @@ -0,0 +1,334 @@ +// wallarm — single-binary Wallarm deployment manager. +// +// On every start: run preflight checks → detect existing deployment → route to wizard or dashboard. +// +// Commands: +// +// wallarm Auto-detect state, show wizard or dashboard +// wallarm --tunnel Start reverse SSH tunnel over TLS:443 +// wallarm --help Show help +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" + + "git.sechpoint.app/customer-engineering/wallarm/internal/native" + "git.sechpoint.app/customer-engineering/wallarm/internal/preflight" + "git.sechpoint.app/customer-engineering/wallarm/internal/state" + "git.sechpoint.app/customer-engineering/wallarm/internal/tunnel" + "git.sechpoint.app/customer-engineering/wallarm/internal/ui" +) + +// Embedded tunnel key for --tunnel flag (set at build time with -ldflags). +var tunnelKey string + +// Version set at build time. +var version = "dev" + +// runDeployFlow runs the deployment wizard in plain terminal (no bubbletea). +func runDeployFlow(r preflight.Result) { + reader := bufio.NewReader(os.Stdin) + var s state.State + s.DeploymentType = "native" + + // Step 1: Cloud region + fmt.Println() + fmt.Println("─── Cloud Region ───") + if r.USReachable && r.EUReachable { + fmt.Println(" [1] US (us1.api.wallarm.com)") + fmt.Println(" [2] EU (api.wallarm.com)") + fmt.Print("Choose region [1/2]: ") + choice, _ := reader.ReadString('\n') + choice = strings.TrimSpace(choice) + if choice == "2" { + s.CloudRegion = "EU" + s.APIHost = "api.wallarm.com" + } else { + s.CloudRegion = "US" + s.APIHost = "us1.api.wallarm.com" + } + } else if r.USReachable { + fmt.Println(" US (us1.api.wallarm.com) — only reachable region") + s.CloudRegion = "US" + s.APIHost = "us1.api.wallarm.com" + } else { + fmt.Println(" EU (api.wallarm.com) — only reachable region") + s.CloudRegion = "EU" + s.APIHost = "api.wallarm.com" + } + + // Step 2: API Token + fmt.Println() + fmt.Print("Wallarm API Token (Deploy role): ") + token, _ := reader.ReadString('\n') + s.APIToken = strings.TrimSpace(token) + if s.APIToken == "" { + fmt.Println("Token cannot be empty.") + return + } + + // Step 3: Node configuration + fmt.Println() + fmt.Print("Node name: ") + nodeName, _ := reader.ReadString('\n') + nodeName = strings.TrimSpace(nodeName) + if nodeName == "" { + fmt.Println("Node name required.") + return + } + + fmt.Print("Listen port [8081]: ") + port, _ := reader.ReadString('\n') + port = strings.TrimSpace(port) + if port == "" { + port = "8081" + } + address := "0.0.0.0:" + port + + fmt.Print("Upstream IP [127.0.0.1]: ") + upstreamIP, _ := reader.ReadString('\n') + upstreamIP = strings.TrimSpace(upstreamIP) + if upstreamIP == "" { + upstreamIP = "127.0.0.1" + } + + fmt.Print("Upstream port [80]: ") + upstreamPort, _ := reader.ReadString('\n') + upstreamPort = strings.TrimSpace(upstreamPort) + if upstreamPort == "" { + upstreamPort = "80" + } + + fmt.Print("Labels [group=" + nodeName + "]: ") + labels, _ := reader.ReadString('\n') + labels = strings.TrimSpace(labels) + if labels == "" { + labels = "group=" + nodeName + } + + fmt.Println() + fmt.Printf(" Node: %s\n", nodeName) + fmt.Printf(" Listen: %s\n", address) + fmt.Printf(" Upstream: %s:%s\n", upstreamIP, upstreamPort) + fmt.Printf(" Region: %s (%s)\n", s.CloudRegion, s.APIHost) + fmt.Print("\nProceed with deployment? [Y/n]: ") + confirm, _ := reader.ReadString('\n') + confirm = strings.TrimSpace(strings.ToLower(confirm)) + if confirm != "" && confirm != "y" && confirm != "yes" { + fmt.Println("Cancelled.") + return + } + + baseDir := "/opt/wallarm" + instanceDir := baseDir + "/" + nodeName + + portNum, _ := strconv.Atoi(upstreamPort) + s.Nodes = append(s.Nodes, state.Node{ + Name: nodeName, + Type: "native", + Address: address, + UpstreamIP: upstreamIP, + UpstreamPort: portNum, + Status: "deploying", + CreatedAt: time.Now().Format(time.RFC3339), + }) + + // Step 4: Deploy + fmt.Println() + fmt.Println("Starting deployment...") + + // Create instance directory and .env file + os.MkdirAll(instanceDir+"/etc", 0755) + os.MkdirAll(instanceDir+"/var/log", 0755) + os.MkdirAll(instanceDir+"/var/run", 0755) + + envContent := fmt.Sprintf(`WALLARM_API_TOKEN=%s +WALLARM_API_HOST=%s +WALLARM_LABELS=%s +WALLARM_LISTEN=%s +WALLARM_UPSTREAM=%s:%s +WALLARM_REGION=%s +`, s.APIToken, s.APIHost, labels, address, upstreamIP, upstreamPort, s.CloudRegion) + os.WriteFile(instanceDir+"/.env", []byte(envContent), 0600) + + if err := native.CreateNodesDir(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + if err := native.GenerateSystemdTemplate(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + for _, node := range s.Nodes { + if err := native.InstallNode(node, s.APIToken, s.APIHost, labels); err != nil { + fmt.Fprintf(os.Stderr, "Deployment failed: %v\n", err) + return + } + } + + // Save state + if err := state.Save(&s); err != nil { + fmt.Fprintf(os.Stderr, "Warning: could not save state: %v\n", err) + } + + fmt.Println() + fmt.Println("✅ Deployment complete!") + fmt.Println() + fmt.Printf(" Binary: /opt/wallarm/wallarm\n") + fmt.Printf(" Instance: %s\n", instanceDir) + fmt.Printf(" State: /opt/wallarm/state.json\n") + fmt.Println() + fmt.Println("Run 'sudo /opt/wallarm/wallarm' for the dashboard.") +} +func runTunnelFlow() { + fmt.Println() + fmt.Println("🔗 Remote Assistance — Secure Tunnel Setup") + fmt.Println() + fmt.Println("The tunnel lets a remote admin connect to this server through your jumphost.") + fmt.Println("Enter the SSH endpoint where the admin will connect:") + fmt.Println() + + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Jumphost URL [ssh.sechpoint.app:443]: ") + host, _ := reader.ReadString('\n') + host = strings.TrimSpace(host) + if host == "" { + host = "ssh.sechpoint.app:443" + } + + fmt.Print("Username [wallarm-tunnel]: ") + user, _ := reader.ReadString('\n') + user = strings.TrimSpace(user) + if user == "" { + user = "wallarm-tunnel" + } + + fmt.Print("Password or SSH key path: ") + pass, _ := reader.ReadString('\n') + pass = strings.TrimSpace(pass) + if pass == "" { + fmt.Println("No credentials provided. Aborting.") + return + } + + fmt.Println() + fmt.Println("Starting tunnel...") + + cfg := tunnel.Config{ + Jumphost: host, + User: user, + RemotePort: 9042, + LocalSSHPort: 22, + } + + if strings.HasPrefix(pass, "/") { + cfg.KeyPath = pass + } else { + cfg.Password = pass + } + + fmt.Println("Share this with your admin:") + fmt.Println() + fmt.Printf(" ssh -p 9042 %s@%s\n", user, host) + fmt.Println() + fmt.Println("Press Ctrl+C to close the tunnel.") + + if err := tunnel.Start(cfg); err != nil { + fmt.Fprintf(os.Stderr, "Tunnel error: %v\n", err) + os.Exit(1) + } +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, `wallarm — Wallarm Deployment Manager + +Usage: + wallarm Start interactive deployment wizard/dashboard + wallarm --tunnel Start reverse SSH tunnel to sechpoint.app + wallarm --help Show this help + +On first run, wallarm checks system readiness, then guides you through +deployment. On subsequent runs, it shows your existing deployments. +`) + } + help := flag.Bool("help", false, "Show help") + versionFlag := flag.Bool("version", false, "Show version") + tunnelFlag := flag.Bool("tunnel", false, "Start reverse SSH tunnel") + flag.Parse() + + if *help { + flag.Usage() + return + } + if *versionFlag { + fmt.Println("wallarm version", version) + return + } + + // ── Tunnel mode ────────────────────────────────────────────── + if *tunnelFlag { + cfg := tunnel.DefaultConfig() + if tunnelKey != "" { + cfg.KeyBytes = []byte(tunnelKey) + } else { + fmt.Fprintln(os.Stderr, "No tunnel key configured. Set WALLARM_TUNNEL_KEY or build with -ldflags.") + os.Exit(1) + } + if err := tunnel.Start(cfg); err != nil { + fmt.Fprintf(os.Stderr, "Tunnel error: %v\n", err) + os.Exit(1) + } + return + } + + // ── Default: preflight → TUI wizard/dashboard ──────────────── + fmt.Println("═══ Wallarm Deployment Manager ═══") + fmt.Println() + + // 1. Preflight checks (always run on start) + fmt.Println("Running preflight checks...") + result := preflight.Run() + for _, c := range result.Checks { + marker := "✅" + if !c.Passed { + marker = "❌" + } else if c.Warning { + marker = "⚠️" + } + fmt.Printf(" %s %s — %s\n", marker, c.Name, c.Detail) + } + + if !result.Passed { + fmt.Println("\n❌ Preflight checks failed. Fix the issues above and re-run.") + os.Exit(1) + } + fmt.Println("✅ Preflight checks passed.") + fmt.Println() + + // 2. Launch bubbletea TUI (deploy choice or dashboard) + if err := ui.Run(); err != nil { + fmt.Fprintf(os.Stderr, "UI error: %v\n", err) + os.Exit(1) + } + + // 3. Post-TUI: if no deployment exists, run deploy form in plain terminal + if !state.HasDeployment() { + fmt.Println() + fmt.Print("Start deployment now? [Y/n]: ") + reader := bufio.NewReader(os.Stdin) + answer, _ := reader.ReadString('\n') + answer = strings.TrimSpace(strings.ToLower(answer)) + if answer == "" || answer == "y" || answer == "yes" { + runDeployFlow(result) + } + } +} diff --git a/deploy.sh b/deploy.sh index 6acf8be..e9108d2 100644 --- a/deploy.sh +++ b/deploy.sh @@ -67,7 +67,7 @@ echo -e "${YELLOW}Cloning repository...${NC}" git clone --depth 1 "$REPO_URL" "$BUILD_DIR" 2>/dev/null echo -e "${YELLOW}Building wallarm (this takes ~30 seconds)...${NC}" -cd "$BUILD_DIR/source" +cd "$BUILD_DIR" go build -ldflags="-s -w -X main.version=$(git describe --tags --always 2>/dev/null || echo 'dev')" -o wallarm ./cmd/wallarm/ # Optional: compress with UPX if available @@ -77,7 +77,7 @@ fi # ── Install ─────────────────────────────────────────────────────────── mkdir -p "$WALLARM_DIR" -cp "$BUILD_DIR/source/wallarm" "$WALLARM_DIR/wallarm" +cp wallarm "$WALLARM_DIR/wallarm" chmod +x "$WALLARM_DIR/wallarm" echo diff --git a/source/go.mod b/go.mod similarity index 100% rename from source/go.mod rename to go.mod diff --git a/source/go.sum b/go.sum similarity index 100% rename from source/go.sum rename to go.sum diff --git a/source/internal/native/native.go b/internal/native/native.go similarity index 100% rename from source/internal/native/native.go rename to internal/native/native.go diff --git a/source/internal/preflight/preflight.go b/internal/preflight/preflight.go similarity index 100% rename from source/internal/preflight/preflight.go rename to internal/preflight/preflight.go diff --git a/source/internal/shared/shared.go b/internal/shared/shared.go similarity index 100% rename from source/internal/shared/shared.go rename to internal/shared/shared.go diff --git a/source/internal/state/state.go b/internal/state/state.go similarity index 100% rename from source/internal/state/state.go rename to internal/state/state.go diff --git a/source/internal/tunnel/tunnel.go b/internal/tunnel/tunnel.go similarity index 100% rename from source/internal/tunnel/tunnel.go rename to internal/tunnel/tunnel.go diff --git a/source/internal/ui/ui.go b/internal/ui/ui.go similarity index 100% rename from source/internal/ui/ui.go rename to internal/ui/ui.go