import “golang.org/x/crypto/ssh”
文档 https://godoc.org/golang.org/x/crypto/ssh

type PublicKey

  1. type PublicKey interface {
  2. // Type returns the key's type, e.g. "ssh-rsa".
  3. Type() string
  4. // Marshal returns the serialized key data in SSH wire format,
  5. // with the name prefix. To unmarshal the returned data, use
  6. // the ParsePublicKey function.
  7. Marshal() []byte
  8. // Verify that sig is a signature on the given data using this
  9. // key. This function will hash the data appropriately first.
  10. Verify(data []byte, sig *Signature) error
  11. }

func NewPublicKey(key interface{}) (PublicKey, error)

type AuthMethod

func Password(secret string) AuthMethod 使用密码
func PublicKeys(signers …Signer) AuthMethod 使用密钥

  1. var hostKey ssh.PublicKey
  2. key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
  3. if err != nil {
  4. log.Fatalf("unable to read private key: %v", err)
  5. }
  6. // Create the Signer for this private key.
  7. signer, err := ssh.ParsePrivateKey(key)
  8. if err != nil {
  9. log.Fatalf("unable to parse private key: %v", err)
  10. }
  11. config := &ssh.ClientConfig{
  12. User: "user",
  13. Auth: []ssh.AuthMethod{
  14. // Use the PublicKeys method for remote authentication.
  15. ssh.PublicKeys(signer),
  16. },
  17. HostKeyCallback: ssh.FixedHostKey(hostKey),
  18. }
  19. // Connect to the remote server and perform the SSH handshake.
  20. client, err := ssh.Dial("tcp", "host.com:22", config)
  21. if err != nil {
  22. log.Fatalf("unable to connect: %v", err)
  23. }
  24. defer client.Close()

type ClientConfig

  1. type ClientConfig struct {
  2. Config
  3. User string
  4. Auth []AuthMethod
  5. HostKeyCallback HostKeyCallback
  6. BannerCallback BannerCallback
  7. ClientVersion string
  8. HostKeyAlgorithms []string
  9. Timeout time.Duration
  10. }

type Client

func Dial(network, addr string, config ClientConfig) (Client, error)
func (c Client) NewSession() (Session, error)

type Session

一个会话只接受一个run、start或Shell调用。

  1. type Session struct {
  2. Stdin io.Reader
  3. Stdout io.Writer
  4. Stderr io.Writer
  5. // contains filtered or unexported fields
  6. }

func (s Session) Close() error
func (s
Session) CombinedOutput(cmd string) ([]byte, error) 返回标准输出和标准错误,以0的状态退出错误为nil

  1. 本机命令错误才算错误,远程错误属于标准输出
  2. resscp -p 22 root@1.1.1.1 cat11 /home/test.txt || 1.1.1.1机器上cat11不是一个命令,CombinedOutput认为他是标准输出
  3. resscp11 -p 22 root@1.1.1.1 cat /home/test.txt || scp11 命令在本机不存在,这样才是标准错误

func (s Session) Output(cmd string) ([]byte, error) 返回标准输出,以0的状态退出,则返回的错误为nil
func (s
Session) Run(cmd string) error

  • 一个session应只能调用一次,stdin、stdout和stderr没有问题,且以0的状态退出,则返回的错误为nil

func (s Session) Setenv(name, value string) error 设置一个环境变量,该变量将用于Shell或Run执行的任何命令func (s Session) Signal(sig Signal) error 将给定的信号发送给远程进程
func (s Session) Start(cmd string) error 在远程主机上运行cmd
func (s
Session) StderrPipe() (io.Reader, error) 错误信息,与标准输出信息一直
func (s Session) StdinPipe() (io.WriteCloser, error) 输入信息
func (s
Session) StdoutPipe() (io.Reader, error) 输出信息,与标准错误信息一直
func (s *Session) Wait() error Start+Wait = run

type Signal

  1. type Signal string
  2. const (
  3. SIGABRT Signal = "ABRT"
  4. SIGALRM Signal = "ALRM"
  5. SIGFPE Signal = "FPE"
  6. SIGHUP Signal = "HUP"
  7. SIGILL Signal = "ILL"
  8. SIGINT Signal = "INT"
  9. SIGKILL Signal = "KILL"
  10. SIGPIPE Signal = "PIPE"
  11. SIGQUIT Signal = "QUIT"
  12. SIGSEGV Signal = "SEGV"
  13. SIGTERM Signal = "TERM"
  14. SIGUSR1 Signal = "USR1"
  15. SIGUSR2 Signal = "USR2"
  16. )

例子:

  1. package main
  2. import (
  3. "fmt"
  4. "golang.org/x/crypto/ssh"
  5. "log"
  6. "net"
  7. "time"
  8. )
  9. func connect(user, password, host string, port int) (*ssh.Session, error) {
  10. var (
  11. auth []ssh.AuthMethod
  12. addr string
  13. clientConfig *ssh.ClientConfig
  14. client *ssh.Client
  15. session *ssh.Session
  16. err error
  17. )
  18. // get auth method
  19. auth = make([]ssh.AuthMethod, 0)
  20. auth = append(auth, ssh.Password(password))
  21. clientConfig = &ssh.ClientConfig{
  22. User: user,
  23. Auth: auth,
  24. Timeout: 30 * time.Second,
  25. HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
  26. return nil
  27. },
  28. }
  29. // connet to ssh
  30. addr = fmt.Sprintf("%s:%d", host, port)
  31. if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
  32. return nil, err
  33. }
  34. // create session
  35. if session, err = client.NewSession(); err != nil {
  36. return nil, err
  37. }
  38. return session, nil
  39. }
  40. func main() {
  41. session, err := connect("root", "*****", "*.*.*.*", 22)
  42. if err != nil {
  43. log.Fatal("error:",err)
  44. }
  45. defer session.Close()
  46. res,err := session.CombinedOutput("cd /home/jw && ./shell01.sh")
  47. if err != nil {
  48. log.Fatal("error:",err)
  49. }
  50. res_str := fmt.Sprintf("%s",res)
  51. fmt.Println(res_str)
  52. }

交互式执行

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "golang.org/x/crypto/ssh"
  6. "log"
  7. "sync"
  8. )
  9. type MyReader struct {
  10. channel chan string
  11. }
  12. func newReader() *MyReader {
  13. reader := new(MyReader)
  14. reader.channel = make(chan string)
  15. return reader
  16. }
  17. func (r *MyReader) Read(p []byte) (n int, err error) {
  18. var cmd string
  19. cmd = <-r.channel
  20. cmdB := []byte(cmd + "\n")
  21. for i, v := range cmdB {
  22. p[i] = v
  23. }
  24. n = len(cmdB)
  25. return n, err
  26. }
  27. type MyWriter struct {
  28. channel chan string
  29. }
  30. func newWriter() *MyWriter {
  31. writer := new(MyWriter)
  32. writer.channel = make(chan string)
  33. return writer
  34. }
  35. func (w *MyWriter) Write(p []byte) (n int, err error) {
  36. res := string(p)
  37. //fmt.Println(res)
  38. w.channel <- res
  39. return len(p), err
  40. }
  41. func main() {
  42. // 建立SSH客户端连接
  43. client, err := ssh.Dial("tcp", "47.107.180.221:22", &ssh.ClientConfig{
  44. User: "public",
  45. Auth: []ssh.AuthMethod{ssh.Password("public")},
  46. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  47. })
  48. if err != nil {
  49. log.Fatalf("SSH dial error: %s", err.Error())
  50. }
  51. // 建立新会话
  52. session, err := client.NewSession()
  53. defer session.Close()
  54. if err != nil {
  55. log.Fatalf("new session error: %s", err.Error())
  56. }
  57. writer := newWriter()
  58. reader := newReader()
  59. session.Stdout = writer // 会话输出
  60. session.Stderr = writer // 会话错误
  61. session.Stdin = reader // 会话输入
  62. go func() {
  63. for {
  64. output, ok := <-writer.channel
  65. if !ok {
  66. break
  67. }
  68. if strings.Contains(output, "-bash:") {
  69. err = errors.New(output)
  70. }
  71. fmt.Println(output)
  72. }
  73. }()
  74. s := "cd /home;ll -a \n exit \n"
  75. go func() {
  76. //f := bufio.NewReader(os.Stdin) //读取输入的内容
  77. f := bytes.NewBufferString(s)
  78. for {
  79. input, err := f.ReadString('\n') //定义一行输入的内容分隔符。
  80. if err != nil {
  81. break
  82. }
  83. reader.channel <- input
  84. }
  85. }()
  86. ExecuteTerminal(session)
  87. }
  88. // 这里是一个阻塞会话,没遇到exit这里不会退出
  89. func ExecuteTerminal(session *ssh.Session) {
  90. var err error
  91. modes := ssh.TerminalModes{
  92. ssh.ECHO: 0, // 禁用回显(0禁用,1启动)
  93. ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
  94. ssh.TTY_OP_OSPEED: 14400, //output speed = 14.4kbaud
  95. }
  96. if err = session.RequestPty("linux", 32, 160, modes); err != nil {
  97. log.Fatalf("request pty error: %s", err.Error())
  98. }
  99. if err = session.Shell(); err != nil {
  100. log.Fatalf("start shell error: %s", err.Error())
  101. }
  102. if err = session.Wait(); err != nil {
  103. log.Fatalf("return error: %s", err.Error())
  104. }
  105. }

注意:

  • cat 一个不存在的文件会报错
  • ls -la 查看隐藏文件,不能用ll -a,会报错
  • ls -l | grep -w xxx ,如果xxx不存在,会报错

不是所有的linux命令用ssh去执行与本机执行的结果是一样的
例如:
ssh远程执行
image.png
image.png
本机执行
image.png