package ssh import ( "fmt" "log/slog" "os" "path/filepath" "time" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/knownhosts" ) type Connection struct { Client *ssh.Client } func NewConnection(user, host string, port int, auth ssh.AuthMethod) (*Connection, error) { home, err := os.UserHomeDir() if err != nil { return nil, fmt.Errorf("failed to get home dir: %w", err) } knownHostsPath := filepath.Join(home, ".ssh", "known_hosts") hkCallback, err := knownhosts.New(knownHostsPath) if err != nil { slog.Warn("Could not load known_hosts, ensure you connected manually once.", "path", knownHostsPath) return nil, fmt.Errorf("known_hosts error: %w", err) } cfg := &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{auth}, HostKeyCallback: hkCallback, Timeout: 10 * time.Second, } addr := fmt.Sprintf("%s:%d", host, port) slog.Debug("Dialing SSH", "target", addr) client, err := ssh.Dial("tcp", addr, cfg) if err != nil { return nil, fmt.Errorf("ssh dial failed: %w", err) } slog.Debug("SSH connection established", "target", addr) return &Connection{Client: client}, nil } func (c *Connection) Close() error { if c.Client != nil { return c.Client.Close() } return nil }