feat: Fleet Forge UEM - device management, agent, remote desktop (VNC), TLS reverse proxy
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/fleetforge/forge-agent/internal/agent"
|
||||
"github.com/fleetforge/forge-agent/internal/client"
|
||||
"github.com/fleetforge/forge-agent/internal/config"
|
||||
"github.com/fleetforge/forge-agent/internal/inventory"
|
||||
"github.com/fleetforge/forge-agent/internal/winsvc"
|
||||
)
|
||||
|
||||
const AgentVersion = "0.1.0"
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Forge Agent v%s\n", AgentVersion)
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n")
|
||||
fmt.Fprintf(os.Stderr, " forge-agent enroll -token <token> -server <url> Enroll this device\n")
|
||||
fmt.Fprintf(os.Stderr, " forge-agent run [--service] Run the agent\n")
|
||||
fmt.Fprintf(os.Stderr, " forge-agent install -server <url> -token <token> Install as Windows Service\n")
|
||||
fmt.Fprintf(os.Stderr, " forge-agent uninstall Uninstall Windows Service\n")
|
||||
fmt.Fprintf(os.Stderr, " forge-agent version Print version\n")
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "enroll":
|
||||
cmdEnroll(os.Args[2:])
|
||||
case "run":
|
||||
cmdRun(os.Args[2:])
|
||||
case "install":
|
||||
cmdInstall(os.Args[2:])
|
||||
case "uninstall":
|
||||
cmdUninstall()
|
||||
case "version":
|
||||
fmt.Printf("Forge Agent v%s\n", AgentVersion)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown command: %s\n\n", os.Args[1])
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdEnroll(args []string) {
|
||||
fs := flag.NewFlagSet("enroll", flag.ExitOnError)
|
||||
token := fs.String("token", "", "Enrollment token")
|
||||
server := fs.String("server", "http://localhost:3002", "Server URL")
|
||||
label := fs.String("label", "", "Device label")
|
||||
fs.Parse(args)
|
||||
|
||||
if *token == "" {
|
||||
log.Fatal("Error: -token is required")
|
||||
}
|
||||
performEnrollment(*server, *token, *label)
|
||||
}
|
||||
|
||||
func cmdRun(args []string) {
|
||||
fs := flag.NewFlagSet("run", flag.ExitOnError)
|
||||
serviceMode := fs.Bool("service", false, "Run as Windows Service (use with --service flag)")
|
||||
fs.Parse(args)
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config: %v\nRun 'forge-agent enroll' or 'forge-agent install' first.", err)
|
||||
}
|
||||
|
||||
if cfg.EnrollmentToken != "" && cfg.DeviceID == "" {
|
||||
log.Println("First run detected: attempting auto-enrollment...")
|
||||
if err := performEnrollment(cfg.ServerURL, cfg.EnrollmentToken, cfg.Label); err != nil {
|
||||
log.Printf("WARNING: Auto-enrollment failed: %v", err)
|
||||
log.Println("Agent will retry enrollment on next heartbeat cycle.")
|
||||
} else {
|
||||
if loaded, loadErr := config.Load(); loadErr == nil {
|
||||
cfg = loaded
|
||||
}
|
||||
log.Println("Auto-enrollment successful.")
|
||||
}
|
||||
}
|
||||
|
||||
if *serviceMode {
|
||||
if err := winsvc.RunAsService(); err != nil {
|
||||
log.Fatalf("Service error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
a := agent.New(cfg)
|
||||
if err := a.Run(); err != nil {
|
||||
log.Fatalf("Agent error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdInstall(args []string) {
|
||||
fs := flag.NewFlagSet("install", flag.ExitOnError)
|
||||
server := fs.String("server", "", "Server URL")
|
||||
token := fs.String("token", "", "Enrollment token")
|
||||
label := fs.String("label", "", "Device label")
|
||||
fs.Parse(args)
|
||||
|
||||
if *server == "" || *token == "" {
|
||||
log.Fatal("Error: -server and -token are required")
|
||||
}
|
||||
|
||||
if err := winsvc.InstallService(*server, *token, *label); err != nil {
|
||||
log.Fatalf("Install failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdUninstall() {
|
||||
if err := winsvc.UninstallService(); err != nil {
|
||||
log.Fatalf("Uninstall failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func performEnrollment(serverURL, token, label string) error {
|
||||
fmt.Println("Collecting system information...")
|
||||
info, err := inventory.CollectSystemInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect system info: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Hostname: %s\n", info.Hostname)
|
||||
fmt.Printf("OS: %s %s\n", info.OSName, info.OSVersion)
|
||||
fmt.Printf("Arch: %s\n", info.Architecture)
|
||||
fmt.Printf("Server: %s\n", serverURL)
|
||||
fmt.Println("Enrolling...")
|
||||
|
||||
c := client.New(serverURL, "")
|
||||
resp, err := c.Enroll(client.EnrollRequest{
|
||||
Token: token,
|
||||
Hostname: info.Hostname,
|
||||
Platform: info.Platform,
|
||||
OSName: info.OSName,
|
||||
OSVersion: info.OSVersion,
|
||||
Architecture: info.Architecture,
|
||||
KernelVersion: info.KernelVersion,
|
||||
CPUModel: info.CPUModel,
|
||||
CPUCores: info.CPUCores,
|
||||
TotalMemoryBytes: info.TotalMemory,
|
||||
TotalDiskBytes: info.TotalDisk,
|
||||
IPAddress: info.IPAddress,
|
||||
AgentVersion: AgentVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("enrollment failed: %w", err)
|
||||
}
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
cfg = &config.Config{}
|
||||
}
|
||||
cfg.ServerURL = serverURL
|
||||
cfg.DeviceID = resp.DeviceID
|
||||
cfg.Credential = resp.Credential
|
||||
cfg.Label = label
|
||||
cfg.EnrollmentToken = ""
|
||||
if cfg.HeartbeatSec <= 0 {
|
||||
cfg.HeartbeatSec = 30
|
||||
}
|
||||
|
||||
if err := config.Save(cfg); err != nil {
|
||||
return fmt.Errorf("failed to save config: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\nEnrollment successful!\n")
|
||||
fmt.Printf("Device ID: %s\n", resp.DeviceID)
|
||||
fmt.Printf("Config saved to: %s\n", config.ConfigPath())
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user