package main import ( "fmt" "io" "log" "net" "os" "os/exec" "path/filepath" "strings" "time" "github.com/charmbracelet/huh" "github.com/spf13/viper" "golang.org/x/crypto/ssh" ) type Config struct { SSHUser string `mapstructure:"SSH_USER"` SSHHost string `mapstructure:"SSH_HOST"` JumpUser string `mapstructure:"JUMP_USER"` JumpHost string `mapstructure:"JUMP_HOST"` WorkstationHost string `mapstructure:"WORKSTATION_HOST"` WorkstationUser string `mapstructure:"WWORKSTATION_USER"` WorkstationMac string `mapstructure:"WORKSTATION_MAC"` RDPUser string `mapstructure:"RDP_USER"` RDPPassword string `mapstructure:"RDP_PASSWORD"` WorkstationIP string `mapstructure:"WORKSTATION_IP"` SSHPort int `mapstructure:"SSH_PORT"` } var cfg Config func init() { log.Println("Loading Config File") configPath, err := os.UserConfigDir() if err != nil { return } workConfigPath := filepath.Join(configPath, "work") configFile := filepath.Join(workConfigPath, "config.toml") viper.SetConfigFile(configFile) viper.SetConfigType("toml") viper.AddConfigPath(".") viper.AutomaticEnv() err = viper.ReadInConfig() if err != nil { log.Printf("error loading config file: %v", err) } allSettings := viper.AllSettings() log.Printf("All loaded settings: %+v", allSettings) err = viper.UnmarshalKey("default", &cfg) if err != nil { log.Printf("unable to decode into struct: %v", err) } } func connect() { runCommand("timew", "start", "work") wakeLou() sshClient, err := ssh.Dial("tcp", cfg.SSHHost+":"+fmt.Sprintf("%v", cfg.SSHPort), makeSSHClient()) if err != nil { log.Fatal("Failed to dial: ", err) } defer sshClient.Close() session, err := sshClient.NewSession() if err != nil { log.Fatal("Failed to create session: ", err) } defer session.Close() go forwardPort(sshClient, "2048", cfg.WorkstationIP, "22") go forwardPort(sshClient, "6000", cfg.WorkstationIP, "3389") connectToLou() } func forwardPort(sshConn *ssh.Client, localPort, remoteHost, remotePort string) { listener, err := net.Listen("tcp", "127.0.0.1:"+localPort) if err != nil { log.Printf("Fehler beim Öffnen des lokalen Ports %s: %v", localPort, err) return } defer listener.Close() for { localConn, err := listener.Accept() if err != nil { log.Printf("Fehler beim Akzeptieren der Verbindung: %v", err) continue } remoteConn, err := sshConn.Dial("tcp", remoteHost+":"+remotePort) if err != nil { log.Printf("Fehler beim Verbinden zum Remote-Host %s:%s: %v", remoteHost, remotePort, err) localConn.Close() continue } go copyConn(localConn, remoteConn) go copyConn(remoteConn, localConn) } } func copyConn(dst, src net.Conn) { defer dst.Close() defer src.Close() io.Copy(dst, src) } func makeSSHClient() *ssh.ClientConfig { keypath := os.ExpandEnv("$HOME/.ssh/hegenberg") keyBytes, err := os.ReadFile(keypath) if err != nil { log.Fatalf("Failed to read private key: %s", err) } key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(cfg.RDPPassword)) if err != nil { log.Fatalf("Failed to parse private key: %s", err) } return &ssh.ClientConfig{ User: cfg.SSHUser, Auth: []ssh.AuthMethod{ ssh.PublicKeys(key), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 5 * time.Second, } } func runCommand(name string, args ...string) { cmd := exec.Command(name, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin err := cmd.Run() if err != nil { fmt.Println("Error:", err) } } func startSequence() { runCommand("timew", "start", "work") wakeLou() connectToVarda() } func wakeLou() { sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"", fmt.Sprintf("%v", cfg.SSHPort), cfg.SSHUser, cfg.SSHHost, cfg.JumpUser, cfg.JumpHost, cfg.WorkstationMac) args := strings.Split(sshCommand, " ") log.Println(args) runCommand("ssh", args[1:]...) } func connectToVarda() { sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s", cfg.WorkstationHost, cfg.SSHUser, cfg.SSHHost) args := strings.Split(sshCommand, " ") runCommand("ssh", args[1:]...) } func connectToLou() { sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1", cfg.WorkstationHost, cfg.SSHUser) args := strings.Split(sshCommand, " ") runCommand("ssh", args[1:]...) } func startRDPConnection() { rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350", cfg.RDPUser, cfg.RDPPassword) runCommand("bash", "-c", rdpCommand) } func makeChoice() { var choice string form := huh.NewForm( huh.NewGroup( huh.NewSelect[string](). Title("What would you like to do?"). Options( huh.NewOption("Start", "Start"), huh.NewOption("Stop Work", "stop Work"), huh.NewOption("Show Week Summary", "show week summary"), huh.NewOption("Show Month Summary", "show month summary"), huh.NewOption("Start Break", "start break"), huh.NewOption("Stop Break", "stop break"), huh.NewOption("Export Timetable", "export"), huh.NewOption("Wake Lou", "wake lou"), huh.NewOption("Connect to Varda", "connect to varda"), huh.NewOption("Connect to Lou", "connect to lou"), huh.NewOption("Start RDP Connection", "start rdp connection"), ). Value(&choice), ), ) err := form.Run() if err != nil { fmt.Println("Error:", err) return } switch choice { case "Start": connect() // startSequence() case "stop Work": runCommand("timew", "stop", "work") case "start break": runCommand("timew", "stop", "work") runCommand("timew", "start", "break") case "stop break": runCommand("timew", "stop", "break") runCommand("timew", "start", "work") case "show week summary": runCommand("timew", "summary", ":week", "work") case "show month summary": runCommand("timew", "summary", ":month", "work") case "wake lou": wakeLou() case "connect to varda": connectToVarda() case "connect to lou": connectToLou() case "start rdp connection": startRDPConnection() case "export": fmt.Println("Exporting Work times") } } func main() { makeChoice() }