search
HomeBackend DevelopmentGolangAn article explains in detail how golang implements ssh related operations

This article is introduced by the golang tutorial column to introduce to you how golang implements SSH connections and other related operations. I wonder how much you know about SSH? Let me talk to you in detail about the operational issues related to Go's implementation of ssh. I hope it will be helpful to friends who need it!

An article explains in detail how golang implements ssh related operations

1.ssh

1.1 Introduction

In some daily development scenarios, we need to communicate with the remote server Some communication and execution of some related command operations. At this time we can use the SSH protocol to achieve the goal. The SSH protocol is a security protocol built on the application layer. Its full name is Secure Shell. It uses the connection-oriented TCP protocol for transmission, which means it is safe and reliable. It should be noted that file transfer cannot be completed on the SSH protocol and needs to be completed on the SFTP protocol mentioned below.

1.2 Go implementation

Go official provides us with a package for implementing SSH connections, located under golang.org/x/crypto, which is provided by calling the package in the program Related methods can be used to communicate with other machines. Before use, we need to use go get to import related dependency packages.

go get golang.org/x/crypto/ssh

Before communicating, we also need to configure some related parameters for Configuring some related parameters for establishing a connection. The ClientConfig structure under the ssh package defines some configuration items needed to establish an SSH connection. Some items provide default parameters, which we do not need to declare when using them.

In the code snippet below, we first declare the username and password, set the connection timeout to 10 seconds, and the addr variable defines the IP address and port of the target machine.

HostKeyCallback item, we set it to ignore. This is because the SSH protocol provides two security verification methods for the client. One is password-based security verification, which is the account password form we often use. In addition, One is key-based security verification. Compared with the first type, this form of verification method greatly improves the security level. The disadvantage is that it takes a relatively long time.

If we need to use this method for verification, first we need to create a pair of keys for ourselves on the server. When accessing as a client, we will first send a security verification request to the server, and the server will receive After receiving the request, it will first compare the public key saved on the machine with the public key sent by the client. If they are consistent, the server will respond to the client with an encrypted challenge. After receiving the challenge, the client will use the private key to decrypt. Then the decryption result is sent to the server, and the server performs verification and then returns the response result. At this point, a period of key verification is completed.

        //添加配置
        config := &ssh.ClientConfig{
                User: "root",
                Auth: []ssh.AuthMethod{ssh.Password("Password")},
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),
                Timeout: 10 * time.Second,
            }
        }
        addr := fmt.Sprintf("%v:%v", IP, Port)

1.2.2 Establishing a connection

After completing all parameter initialization, we can call the Dial method to establish an SSH connection. The Dial method has a total of three parameters and two return values. The first parameter network is the network type. Here we use the connection-oriented TCP protocol. The second parameter addr is the IP address and port number of the target machine. The third parameter is the network type. The parameter config is the configuration item of our previous life. Dial will return an SSH connection and error type.

func Dial(network, addr string, config *ClientConfig) (*Client, error)

        //建立SSH连接
        sshClient, err := ssh.Dial("tcp", addr, config)       
         if err != nil {
            log.Fatal("unable to create ssh conn")
        }

1.2.3 Create session

After establishing SSH with the target machine Once connected, we can communicate with the target machine by creating an SSH session. This operation can be achieved through the NewSession() method.

        //建立SSH会话
        sshSession, err := sshClient.NewSession()       if err != nil {
           log.Fatal("unable to create ssh session")
        }

1.2.4 Perform operations

After establishing a session with the target machine, we can operate the remote server by executing commands, etc. Go currently provides us with five methods for operating remote machines, namely Run(), Start(), Output(), CombineOutpt(), Shell().

? Among them, Output() and **CombineOutpt()** are two methods that encapsulate the Run() method to varying degrees and verify the output stream and error stream. and other related content.

        // Output runs cmd on the remote host and returns its standard output.
        func (s *Session) Output(cmd string) ([]byte, error) {           if s.Stdout != nil {              return nil, errors.New("ssh: Stdout already set")
           }           var b bytes.Buffer
           s.Stdout = &b
           err := s.Run(cmd)           return b.Bytes(), err
        }        
        
        // CombinedOutput runs cmd on the remote host and returns its combined
        // standard output and standard error.
        func (s *Session) CombinedOutput(cmd string) ([]byte, error) {           if s.Stdout != nil {              return nil, errors.New("ssh: Stdout already set")
           }           if s.Stderr != nil {              return nil, errors.New("ssh: Stderr already set")
           }           var b singleWriter
           s.Stdout = &b
           s.Stderr = &b
           err := s.Run(cmd)           return b.b.Bytes(), err
        }

The Run() method encapsulates the Start() method and adds a Wait method to verify the exit command of the remote server. There is a pipe type variable exitStatus in the Wait() method, which is used to save the exit status returned by the machine after each command is executed. Friends who are interested can take a look at the code of this piece, but the code will not be posted here.

There is a pitfall here. If we run a program on a remote machine that will never stop, and our program has not been waiting for the exit command sent by the remote machine, it will cause the program to be blocked. Unable to return normally. The solution is to use a coroutine to execute this task separately, or use a timer to end the session regularly to return normally.

Start()方法与Shell方法一致,都是返回一个error类型,在底层都是调用了start()方法和SendRequest方法,关于这两个方法的内容这里就不做详细介绍了,有兴趣的朋友可以自行去阅读。唯一的区别是Start()方法有一个string类型的参数,用于接收用户输入的参数,而Shell()方法是无参数的。

使用Shell()方法配合RequestPty()等方法可以在本地建立一个伪终端,可以直接通过输入命令的形式操作目标机器。下面都会做一个示例。

        //Run
        func (s *Session) Run(cmd string) error {
           err := s.Start(cmd)           if err != nil {
              fmt.Println(err)              return err
           }           return s.Wait()
                }                
                
        // Start runs cmd on the remote host. Typically, the remote
        // server passes cmd to the shell for interpretation.
        // A Session only accepts one call to Run, Start or Shell.
        func (s *Session) Start(cmd string) error {           if s.started {              return errors.New("ssh: session already started")
           }
           req := execMsg{
              Command: cmd,
           }
        
           ok, err := s.ch.SendRequest("exec", true, Marshal(&req))           if err == nil && !ok {
              err = fmt.Errorf("ssh: command %v failed", cmd)
           }           if err != nil {              return err
           }           return s.start()
        }

1.2.5 示例代码(执行命令)

这里我们使用Run()方法来演示一下如果去执行命令,其他方法类型就不做演示了。这里我们使用一个标准输出流、错误流来保存执行结果。

这里演示了一个简单的执行过程,使用了cd命令到/home/min目录下,在给helloworld程序添加可执行权限,最后运行程序。

        var stdoutBuf, stderrBuf bytes.Buffer
        session.Stdout = &stdoutBuf
        session.Stderr = &stderrBuf    
        // cd /home/min
        // chmod +x helloworld
        // ./helloworld
        cmd := fmt.Sprintf("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld)
        err := session.Run(cmd)        if err != nil {
            log.Fatal("[ERROR]: ", session.Stderr, err)
        }

1.2.6(创建伪终端)

        // 设置Terminal Mode
	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // 关闭回显
		ssh.TTY_OP_ISPEED: 14400, // 设置传输速率
		ssh.TTY_OP_OSPEED: 14400,
	}    
        // 请求伪终端
	err = session.RequestPty("linux", 32, 160, modes)	if err != nil {
		log.Println(err)		return
	}    
        // 设置输入输出
	session.Stdout = os.Stdout
	session.Stdin = os.Stdin
	session.Stderr = os.Stderr
 
	session.Shell() // 启动shell
	session.Wait()  // 等待退出

1.2.7 完整代码

//机器平台信息type Machine struct {
   IP       string
   Port     string
   Username string
   Password string}//建立SSH连接func CreateSSHConn(m *model.Machine) error {   //初始化连接信息
   config := &ssh.ClientConfig{
      User:            m.Username,
      Auth:            []ssh.AuthMethod{ssh.Password(m.Password)},
      HostKeyCallback: ssh.InsecureIgnoreHostKey(),
      Timeout:         10 * time.Second,
   }
   addr := fmt.Sprintf("%v:%v", m.IP, m.Port)   //建立ssh连接
   sshClient, err := ssh.Dial("tcp", addr, config)   if err != nil {
      fmt.Println("unable create ssh conn", err)      return err
   }   defer sshClient.Close()   
   //建立ssh会话
   session, err := sshClient.NewSession()   if err != nil {
      fmt.Println("unable create ssh conn", err)      return err
   }   defer session.Close()   
   
   //执行命令
   var stdoutBuf, stderrBuf bytes.Buffer
   session.Stdout = &stdoutBuf
   session.Stderr = &stderrBuf
   
   cmd := fmt.Sprintf("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld)   if err := session.Run(cmd); err != nil {
       log.Fatal("[ERROR]: ", session.Stderr, err)
   }  
   //创建伪终端
   // 设置Terminal Mode
   modes := ssh.TerminalModes{
           ssh.ECHO:          0,     // 关闭回显
           ssh.TTY_OP_ISPEED: 14400, // 设置传输速率
           ssh.TTY_OP_OSPEED: 14400,
   }      
   // 请求伪终端
   err = session.RequestPty("linux", 32, 160, modes)   if err != nil {
           log.Fatal(err)
   }      
   // 设置输入输出
   session.Stdout = os.Stdout
   session.Stdin = os.Stdin
   session.Stderr = os.Stderr
   
   session.Shell() // 启动shell
   session.Wait()  // 等待退出
   
   return err
}

The above is the detailed content of An article explains in detail how golang implements ssh related operations. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:juejin. If there is any infringement, please contact admin@php.cn delete
Learn Go String Manipulation: Working with the 'strings' PackageLearn Go String Manipulation: Working with the 'strings' PackageMay 09, 2025 am 12:07 AM

Go's "strings" package provides rich features to make string operation efficient and simple. 1) Use strings.Contains() to check substrings. 2) strings.Split() can be used to parse data, but it should be used with caution to avoid performance problems. 3) strings.Join() is suitable for formatting strings, but for small datasets, looping = is more efficient. 4) For large strings, it is more efficient to build strings using strings.Builder.

Go: String Manipulation with the Standard 'strings' PackageGo: String Manipulation with the Standard 'strings' PackageMay 09, 2025 am 12:07 AM

Go uses the "strings" package for string operations. 1) Use strings.Join function to splice strings. 2) Use the strings.Contains function to find substrings. 3) Use the strings.Replace function to replace strings. These functions are efficient and easy to use and are suitable for various string processing tasks.

Mastering Byte Slice Manipulation with Go's 'bytes' Package: A Practical GuideMastering Byte Slice Manipulation with Go's 'bytes' Package: A Practical GuideMay 09, 2025 am 12:02 AM

ThebytespackageinGoisessentialforefficientbyteslicemanipulation,offeringfunctionslikeContains,Index,andReplaceforsearchingandmodifyingbinarydata.Itenhancesperformanceandcodereadability,makingitavitaltoolforhandlingbinarydata,networkprotocols,andfileI

Learn Go Binary Encoding/Decoding: Working with the 'encoding/binary' PackageLearn Go Binary Encoding/Decoding: Working with the 'encoding/binary' PackageMay 08, 2025 am 12:13 AM

Go uses the "encoding/binary" package for binary encoding and decoding. 1) This package provides binary.Write and binary.Read functions for writing and reading data. 2) Pay attention to choosing the correct endian (such as BigEndian or LittleEndian). 3) Data alignment and error handling are also key to ensure the correctness and performance of the data.

Go: Byte Slice Manipulation with the Standard 'bytes' PackageGo: Byte Slice Manipulation with the Standard 'bytes' PackageMay 08, 2025 am 12:09 AM

The"bytes"packageinGooffersefficientfunctionsformanipulatingbyteslices.1)Usebytes.Joinforconcatenatingslices,2)bytes.Bufferforincrementalwriting,3)bytes.Indexorbytes.IndexByteforsearching,4)bytes.Readerforreadinginchunks,and5)bytes.SplitNor

Go encoding/binary package: Optimizing performance for binary operationsGo encoding/binary package: Optimizing performance for binary operationsMay 08, 2025 am 12:06 AM

Theencoding/binarypackageinGoiseffectiveforoptimizingbinaryoperationsduetoitssupportforendiannessandefficientdatahandling.Toenhanceperformance:1)Usebinary.NativeEndianfornativeendiannesstoavoidbyteswapping.2)BatchReadandWriteoperationstoreduceI/Oover

Go bytes package: short reference and tipsGo bytes package: short reference and tipsMay 08, 2025 am 12:05 AM

Go's bytes package is mainly used to efficiently process byte slices. 1) Using bytes.Buffer can efficiently perform string splicing to avoid unnecessary memory allocation. 2) The bytes.Equal function is used to quickly compare byte slices. 3) The bytes.Index, bytes.Split and bytes.ReplaceAll functions can be used to search and manipulate byte slices, but performance issues need to be paid attention to.

Go bytes package: practical examples for byte slice manipulationGo bytes package: practical examples for byte slice manipulationMay 08, 2025 am 12:01 AM

The byte package provides a variety of functions to efficiently process byte slices. 1) Use bytes.Contains to check the byte sequence. 2) Use bytes.Split to split byte slices. 3) Replace the byte sequence bytes.Replace. 4) Use bytes.Join to connect multiple byte slices. 5) Use bytes.Buffer to build data. 6) Combined bytes.Map for error processing and data verification.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment