package connection import ( "bytes" "fmt" "io" "net" "os" "path/filepath" "strconv" "github.com/rs/zerolog/log" "golang.org/x/crypto/ssh" ) type SSHConn struct { addr string client *ssh.Client } var _ IConnection = (*SSHConn)(nil) func NewSSHConn(addr, user string, port int, privkey string) (SSHConn, error) { var newconn SSHConn sshAddr := addr + ":" + strconv.Itoa(port) newconn.addr = sshAddr conn, err := net.Dial("tcp", sshAddr) if err != nil { log.Err(err).Str("addr", addr).Msg("unable to dial ssh addr") return newconn, err } c, err := os.ReadFile(privkey) if err != nil { log.Err(err).Str("private key", privkey).Msg("unable to read ssh private key") return newconn, err } sshPrivKey, err := ssh.ParsePrivateKey(c) if err != nil { log.Err(err).Str("private key", privkey).Msg("unable to parse ssh private key") return newconn, err } sshConfig := ssh.ClientConfig{ User: user, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Auth: []ssh.AuthMethod{ ssh.PublicKeys(sshPrivKey), }, } sshConn, chNewChannel, chReq, err := ssh.NewClientConn(conn, sshAddr, &sshConfig) if err != nil { log.Err(err).Str("addr", sshAddr).Msg("unable to establish a new connection to the swarm") return newconn, err } sshClient := ssh.NewClient(sshConn, chNewChannel, chReq) newconn.client = sshClient log.Info().Str("addr", addr).Int("port", port).Msg("ssh connection sucessfully initialized") return newconn, nil } func (c *SSHConn) Close() error { return c.client.Close() } func (c *SSHConn) CopyFile(src, dest string) error { sshSession, err := c.client.NewSession() if err != nil { log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") return err } defer sshSession.Close() fileInfo, err := os.Stat(src) if err != nil { log.Err(err).Str("file", src).Msg("unable to stat scp source file") return err } file, err := os.Open(src) if err != nil { log.Err(err).Str("file", src).Msg("unable to open scp source file") return err } defer file.Close() go func() { w, _ := sshSession.StdinPipe() defer w.Close() fmt.Fprintf(w, "C0644 %d %s\n", fileInfo.Size(), filepath.Base(dest)) if _, err := io.Copy(w, file); err != nil { log.Err(err).Str("src", src).Str("dest", dest).Msg("unable to scp src to dest") return } fmt.Fprint(w, "\x00") }() if err := sshSession.Run(fmt.Sprintf("scp -t %s", dest)); err != nil { log.Err(err).Str("addr", c.addr).Str("dest", dest).Msg("unable to run scp command") return err } log.Info().Str("src", src).Str("dest", dest).Msg("file successfully uploaded") return nil } func (c *SSHConn) Execute(cmd string) (string, error) { sshSession, err := c.client.NewSession() if err != nil { log.Err(err).Str("addr", c.addr).Msg("unable to open an ssh session") return "", err } defer sshSession.Close() var buf bytes.Buffer sshSession.Stdout = &buf if err := sshSession.Run(cmd); err != nil { log.Err(err).Str("addr", c.addr).Str("command", cmd).Msg("unable to execute an ssh command") return "", err } return "", nil }