Go實現ssh執行遠端命令及遠端終端

youmen發表於2020-12-20

什麼是ssh?

SSH是一種網路協議,用於計算機之間的加密登入.

如果一個使用者從本地計算機,使用SSH協議登入另一臺遠端計算機,我們就可以認為,這種登入是安全的,即使被中途截獲,密碼也不會洩露.

網際網路通訊早期都是明文通訊,一旦被截獲,內容就暴露無疑. 1995年,芬蘭學者Tatu Ylonen設計了SSH協議,將登入資訊全部加密,成為網際網路安全的一個基本解決方案,迅速在全世界獲得推廣, 目前已經成為Linux系統的標準配置.

Go實現ssh執行遠端命令

package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"log"
	"time"
)

func main(){
	sshHost := ""
	sshUser := "root"
	sshPassword := ""
	sshType := "password"
	sshPort := 22


	//建立sshp登陸配置
	config := &ssh.ClientConfig{
		Timeout:         time.Second,//ssh 連線time out 時間一秒鐘, 如果ssh驗證錯誤 會在一秒內返回
		User:            sshUser,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), //這個可以, 但是不夠安全
		//HostKeyCallback: hostKeyCallBackFunc(h.Host),
	}
	if sshType == "password" {
		config.Auth = []ssh.AuthMethod{ssh.Password(sshPassword)}
	}



	//dial 獲取ssh client
	addr := fmt.Sprintf("%s:%d", sshHost, sshPort)
	sshClient, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		log.Fatal("建立ssh client 失敗",err)
	}
	defer sshClient.Close()


	//建立ssh-session
	session, err := sshClient.NewSession()
	if err != nil {
		log.Fatal("建立ssh session 失敗",err)
	}
	defer session.Close()
	//執行遠端命令
	combo,err := session.CombinedOutput("whoami; cd /; ls -al;echo https://github.com/libragen/felix")
	if err != nil {
		log.Fatal("遠端執行cmd 失敗",err)
	}
	log.Println("命令輸出:",string(combo))

}

Go實現一個遠端終端

package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
	"io"
	"log"
	"net"
	"os"
	"time"
)

type Cli struct {
	IP string
	Username string
	Password string
	Port int
	client *ssh.Client
	LastResult string
}

func New(ip string, username string, password string, port ...int) *Cli  {
	cli := new(Cli)
	cli.IP = ip
	cli.Username = username
	cli.Password = password
	switch  {
	case len(port) <= 0:
		cli.Port = 22
	case len(port) > 0:
		cli.Port = port[0]
	}
	return cli
}

func (c *Cli) connect() error  {
	config := ssh.ClientConfig{
		User: c.Username,
		Auth: []ssh.AuthMethod{ssh.Password(c.Password)},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
		Timeout: 10 * time.Second,
	}
	addr := fmt.Sprintf("%s:%d", c.IP,c.Port)
	sshClient, err := ssh.Dial("tcp",addr,&config)
	if err != nil {
		return err
	}
	c.client = sshClient
	return nil
}

func (c *Cli) RunTerminal(stdout, stderr io.Writer) error  {
	if c.client == nil {
		if err := c.connect(); err != nil {
			return err
		}
	}
	session, err := c.client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	fd := int(os.Stdin.Fd())
	oldState,err := terminal.MakeRaw(fd)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(fd,oldState)

	session.Stdout = stdout
	session.Stderr = stderr
	session.Stdin = os.Stdin

	termWidth, termHeight, err := terminal.GetSize(fd)
	if err != nil {
		panic(err)
	}

	modes := ssh.TerminalModes{
		ssh.ECHO: 1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm-256color", termHeight,termWidth,modes); err != nil {
		return err
	}
	session.Shell()
	session.Wait()
	return nil
}

func main()  {
	cli := New("IP","使用者名稱","密碼",22)
	err := cli.RunTerminal( os.Stdout, os.Stdin)
	if err != nil {
		log.Fatal(err)
	}
}

相關文章