


An 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!
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
1.2.1 Configure related parameters
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

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

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











In Go, WebSocket messages can be sent using the gorilla/websocket package. Specific steps: Establish a WebSocket connection. Send a text message: Call WriteMessage(websocket.TextMessage,[]byte("Message")). Send a binary message: call WriteMessage(websocket.BinaryMessage,[]byte{1,2,3}).

Go and the Go language are different entities with different characteristics. Go (also known as Golang) is known for its concurrency, fast compilation speed, memory management, and cross-platform advantages. Disadvantages of the Go language include a less rich ecosystem than other languages, a stricter syntax, and a lack of dynamic typing.

In Go, you can use regular expressions to match timestamps: compile a regular expression string, such as the one used to match ISO8601 timestamps: ^\d{4}-\d{2}-\d{2}T \d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-][0-9]{2}:[0-9]{2})$ . Use the regexp.MatchString function to check if a string matches a regular expression.

Memory leaks can cause Go program memory to continuously increase by: closing resources that are no longer in use, such as files, network connections, and database connections. Use weak references to prevent memory leaks and target objects for garbage collection when they are no longer strongly referenced. Using go coroutine, the coroutine stack memory will be automatically released when exiting to avoid memory leaks.

When passing a map to a function in Go, a copy will be created by default, and modifications to the copy will not affect the original map. If you need to modify the original map, you can pass it through a pointer. Empty maps need to be handled with care, because they are technically nil pointers, and passing an empty map to a function that expects a non-empty map will cause an error.

In Golang, error wrappers allow you to create new errors by appending contextual information to the original error. This can be used to unify the types of errors thrown by different libraries or components, simplifying debugging and error handling. The steps are as follows: Use the errors.Wrap function to wrap the original errors into new errors. The new error contains contextual information from the original error. Use fmt.Printf to output wrapped errors, providing more context and actionability. When handling different types of errors, use the errors.Wrap function to unify the error types.

There are two steps to creating a priority Goroutine in the Go language: registering a custom Goroutine creation function (step 1) and specifying a priority value (step 2). In this way, you can create Goroutines with different priorities, optimize resource allocation and improve execution efficiency.

How to use Gomega for assertions in Golang unit testing In Golang unit testing, Gomega is a popular and powerful assertion library that provides rich assertion methods so that developers can easily verify test results. Install Gomegagoget-ugithub.com/onsi/gomega Using Gomega for assertions Here are some common examples of using Gomega for assertions: 1. Equality assertion import "github.com/onsi/gomega" funcTest_MyFunction(t*testing.T){
