98 lines
3.2 KiB
Go
98 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
func main() {
|
|
base := flag.String("url", envOr("NEURALHUNT_URL", "http://127.0.0.1:8080"), "Neural Hunt server URL")
|
|
identityPath := flag.String("identity", envOr("NEURALHUNT_IDENTITY", defaultIdentityPath()), "persistent terminal identity file")
|
|
taskSelector := flag.String("task", "", "initial task: number, ID prefix or display name")
|
|
maxNodes := flag.Int("max-nodes", 500, "maximum target-field working set")
|
|
nonInteractive := flag.Bool("non-interactive", false, "run unattended without command prompt")
|
|
quiet := flag.Bool("quiet", false, "suppress connection/status chatter")
|
|
beaconPath := flag.String("beacon-path", envOr("NEURALHUNT_BEACON_PATH", "auto"), "Beacon Hunt path: auto, pulse, flux or orbit")
|
|
importPath := flag.String("import", "", "import browser/terminal identity JSON before login")
|
|
exportPath := flag.String("export", "", "export current identity in browser-compatible encrypted format and exit")
|
|
passphrase := flag.String("passphrase", os.Getenv("NEURALHUNT_IDENTITY_PASSPHRASE"), "identity import/export passphrase (prefer environment variable)")
|
|
flag.Parse()
|
|
|
|
if strings.TrimSpace(*importPath) != "" {
|
|
id, err := readIdentityImport(*importPath, *passphrase)
|
|
if err != nil {
|
|
log.Fatal("identity import: ", err)
|
|
}
|
|
if err := saveIdentity(*identityPath, id); err != nil {
|
|
log.Fatal("save imported identity: ", err)
|
|
}
|
|
fmt.Println("Identity importiert nach", *identityPath)
|
|
}
|
|
|
|
id, key, created, err := loadOrCreateIdentity(*identityPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if *exportPath != "" {
|
|
if err := exportBrowserIdentity(*exportPath, *passphrase, id); err != nil {
|
|
log.Fatal("identity export: ", err)
|
|
}
|
|
fmt.Println("Browser-kompatibler verschlüsselter Export:", *exportPath)
|
|
return
|
|
}
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
api := newAPI(*base, id, key)
|
|
if err := api.login(ctx); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := api.registerHostedWorker(ctx); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
go api.hostedWorkerLeaseLoop(ctx, stop)
|
|
if created {
|
|
fmt.Println("Neue Terminal-Identität erzeugt:", *identityPath)
|
|
}
|
|
fmt.Println("NEURAL HUNT SHELL")
|
|
fmt.Println("Client-ID :", api.clientID())
|
|
fmt.Println("Identity-Datei:", *identityPath)
|
|
fmt.Println("Server :", strings.TrimRight(*base, "/"))
|
|
fmt.Println("Backup : identity export <datei> (verschlüsselt, im Browser importierbar)")
|
|
fmt.Println("Hinweis : Dieselbe Identity darf nicht gleichzeitig im Browser verbunden sein.")
|
|
|
|
a := newApp(ctx, api, *identityPath, *passphrase, *maxNodes, *quiet, *nonInteractive)
|
|
a.beaconPathMode = strings.ToLower(strings.TrimSpace(*beaconPath))
|
|
initial, err := selectInitialTask(ctx, a, *taskSelector, !*nonInteractive)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := a.startTask(ctx, initial); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
go a.watcher(ctx)
|
|
|
|
if *nonInteractive {
|
|
<-ctx.Done()
|
|
a.stopSession()
|
|
return
|
|
}
|
|
if err := a.commandLoop(ctx, os.Stdin); err != nil && ctx.Err() == nil {
|
|
log.Println(err)
|
|
}
|
|
a.stopSession()
|
|
}
|
|
|
|
func envOr(name, fallback string) string {
|
|
if v := strings.TrimSpace(os.Getenv(name)); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|