230 lines
5.3 KiB
Go
230 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/huh"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
type App struct {
|
|
cfg Config
|
|
flags Flags
|
|
}
|
|
|
|
func NewApp() (*App, error) {
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading config: %w", err)
|
|
}
|
|
|
|
return &App{
|
|
cfg: cfg,
|
|
}, nil
|
|
}
|
|
|
|
func (a *App) connect() {
|
|
tw := NewTimeWarrior()
|
|
tw.StartWork()
|
|
a.wakeWorkstation()
|
|
sshCon, err := a.newSSHConnection()
|
|
if err != nil {
|
|
log.Fatalf("failed to establish ssh-connection: %v", err)
|
|
}
|
|
defer sshCon.Close()
|
|
|
|
sshFowarder := NewPortForwarder(sshCon.client, "2048", "22", a.cfg.WorkstationIP)
|
|
go sshFowarder.forward()
|
|
|
|
rdpFowarder := NewPortForwarder(sshCon.client, "6000", "3389", a.cfg.WorkstationIP)
|
|
go rdpFowarder.forward()
|
|
|
|
a.connectToWorkstation()
|
|
}
|
|
|
|
func (a *App) 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(a.cfg.RDPPassword))
|
|
if err != nil {
|
|
log.Fatalf("Failed to parse private key: %s", err)
|
|
}
|
|
|
|
return &ssh.ClientConfig{
|
|
User: a.cfg.SSHUser,
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.PublicKeys(key),
|
|
},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
}
|
|
|
|
func (a *App) 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 (a *App) wakeWorkstation() {
|
|
sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"",
|
|
fmt.Sprintf("%v", a.cfg.SSHPort),
|
|
a.cfg.SSHUser,
|
|
a.cfg.SSHHost,
|
|
a.cfg.JumpUser,
|
|
a.cfg.JumpHost,
|
|
a.cfg.WorkstationMac)
|
|
args := strings.Split(sshCommand, " ")
|
|
log.Println(args)
|
|
a.runCommand("ssh", args[1:]...)
|
|
}
|
|
|
|
func (a *App) connectToJump() {
|
|
sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s",
|
|
a.cfg.WorkstationHost,
|
|
a.cfg.SSHUser,
|
|
a.cfg.SSHHost)
|
|
args := strings.Split(sshCommand, " ")
|
|
a.runCommand("ssh", args[1:]...)
|
|
}
|
|
|
|
func (a *App) connectToWorkstation() {
|
|
sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1",
|
|
a.cfg.WorkstationHost,
|
|
a.cfg.SSHUser)
|
|
args := strings.Split(sshCommand, " ")
|
|
a.runCommand("ssh", args[1:]...)
|
|
}
|
|
|
|
func (a *App) startRDPConnection() {
|
|
rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350",
|
|
a.cfg.RDPUser,
|
|
a.cfg.RDPPassword)
|
|
a.runCommand("bash", "-c", rdpCommand)
|
|
}
|
|
|
|
func (a *App) makeChoice() {
|
|
var choice string
|
|
tw := NewTimeWarrior()
|
|
form := huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewSelect[string]().
|
|
Title("What would you like to do?").
|
|
Options(
|
|
huh.NewOption("Start Work", "start work"),
|
|
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("Connect to Jump", "connect to jump"),
|
|
huh.NewOption("Connect to Workstation", "connect to workstation"),
|
|
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 work":
|
|
a.connect()
|
|
case "stop work":
|
|
tw.StopWork()
|
|
case "start break":
|
|
tw.StartBreak()
|
|
case "stop break":
|
|
tw.StopBreak()
|
|
case "show week summary":
|
|
tw.ShowSummary(":week")
|
|
case "show month summary":
|
|
tw.ShowSummary(":month")
|
|
case "connect to jump":
|
|
a.connectToJump()
|
|
case "connect to workstation":
|
|
a.connectToWorkstation()
|
|
case "start rdp connection":
|
|
a.startRDPConnection()
|
|
case "export":
|
|
tw.ExportSummary(a.flags.ExportName)
|
|
}
|
|
}
|
|
|
|
func (a *App) getSSHAuth() ssh.AuthMethod {
|
|
keypath := os.ExpandEnv("$HOME/.ssh/hegenberg")
|
|
keyBytes, err := os.ReadFile(keypath)
|
|
if err != nil {
|
|
fmt.Printf("unable to read private key: %v", err)
|
|
return nil
|
|
}
|
|
|
|
key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(a.cfg.RDPPassword))
|
|
if err != nil {
|
|
fmt.Printf("unable to parse privat key: %v", err)
|
|
return nil
|
|
}
|
|
|
|
return ssh.PublicKeys(key)
|
|
}
|
|
|
|
func (a *App) newSSHConnection() (*SSHConnection, error) {
|
|
config := &ssh.ClientConfig{
|
|
User: a.cfg.SSHUser,
|
|
Auth: []ssh.AuthMethod{a.getSSHAuth()},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
|
|
client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", a.cfg.SSHHost, a.cfg.SSHPort), config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ssh dial failed: %w", err)
|
|
}
|
|
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
client.Close()
|
|
return nil, fmt.Errorf("creating ssh session failed: %w", err)
|
|
}
|
|
|
|
return &SSHConnection{
|
|
client: client,
|
|
session: session,
|
|
}, nil
|
|
}
|
|
|
|
func (a *App) killForwardings() error {
|
|
ports := []string{"2048", "6000"}
|
|
for _, port := range ports {
|
|
cmd := exec.Command("lsof", "-ti", "tcp:"+port)
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
pids := strings.Split(string(output), "\n")
|
|
pid := strings.TrimSpace(pids[0])
|
|
killCmd := exec.Command("kill", pid)
|
|
killCmd.Run()
|
|
}
|
|
return nil
|
|
}
|