SoFunction
Updated on 2025-03-05

Summary of Golang's implementation of SSH and SFTP operations

1 Preface

In some daily development scenarios, we need to communicate with the remote server and execute some related command operations. At this time, we can use the SSH protocol to achieve our goals. The SSH protocol is a security protocol built on the application layer. It is called 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 it needs to be completed on the SFTP protocol mentioned below.

2 Go implementation

Go official provides us with a package for implementing SSH connections, located in/x/cryptoNext, by calling the relevant methods provided in the package in the program, communication with other machines can be achieved. Before using it, we need to use go get to import related dependencies.

go get /x/crypto/ssh

2.1 Configuring related parameters

Before communicating, we need to configure some forConfigure some relevant parameters for establishing a connection. In the ClientConfig structure under the ssh package, some configuration items are defined for establishing SSH connections. Some items provide default parameters, which we can not declare when using.

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

We set the HostKeyCallback item to ignore, because the SSH protocol provides clients with two security verification methods. One is password-based security verification, which is the account password form we often use, and the other is key-based security verification. Compared with the first, this form of verification method greatly improves the security level, and the disadvantage is that the time loss is relatively long.

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. After the server receives the request, it will first compare the public key saved on the machine with the public key sent by the client. If it is consistent, the server will respond to the encrypted challenge to the client. After the client receives the challenge, it will use the private key to decrypt it, and then send the decryption result to the server. After the server performs verification, it will return the response result. At this point, a period of key verification is completed.

        //Add configuration        config := &{
                User: "root",
                Auth: []{("Password")},
                HostKeyCallback: (),
                Timeout: 10 * ,
            }
        }
        addr := ("%v:%v", IP, Port)

2.2 Establish a connection

After all the parameters are initialized, we can call the Dial method to establish an SSH connection. The Dial method has 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, and the third 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)

        // Establish SSH connection        sshClient, err := ("tcp", addr, config)
        if err != nil {
            ("unable to create ssh conn")
        }

2.3 Creating a Session

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

        //Create an SSH session        sshSession, err := ()
        if err != nil {
           ("unable to create ssh session")
        }

2.4 Perform an operation

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, namelyRun()Start()Output()CombineOutpt()Shell()

🦫ThemOutput(), **CombineOutpt()**The two methods are to encapsulate the Run() method to varying degrees, verifying the output stream, 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  != nil {
              return nil, ("ssh: Stdout already set")
           }
           var b 
            = &b
           err := (cmd)
           return (), 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  != nil {
              return nil, ("ssh: Stdout already set")
           }
           if  != nil {
              return nil, ("ssh: Stderr already set")
           }
           var b singleWriter
            = &b
            = &b
           err := (cmd)
           return (), err
        }

The Run() method encapsulates the Start() method and adds the Wait method to verify the exit instruction of the remote server. There is a variable of pipeline type in the Wait() methodexitStatus, it is used to save the exit status returned by the machine after each command is executed. Friends who are interested can check out the code in this section, and the code will not be posted here.

There is a pit here. If we run a program that will never stop on a remote machine, our program will not wait for the exit command sent by the remote machine, which will cause the program to block and cannot return normally. The solution is to use a coroutine to execute this task separately, or use a timer to end the session session regularly to return normally.

The Start() method is consistent with the Shell method. Both return an error type. At the bottom, the start() method and the SendRequest method are called. I will not introduce the contents of these two methods in detail here. Interested friends can read it by themselves. The only difference is that the Start() method has a string type parameter that receives parameters input by the user, while the Shell() method is parameterless.

Use the Shell() method and RequestPty() and other methods to establish a pseudo-terminal locally, and the target machine can be operated directly by entering commands. Here are an examples.

        //Run
        func (s *Session) Run(cmd string) error {
           err := (cmd)
           if err != nil {
              (err)
              return err
           }
           return ()
                }
                
                
        // 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  {
              return ("ssh: session already started")
           }
           req := execMsg{
              Command: cmd,
           }
        
           ok, err := ("exec", true, Marshal(&req))
           if err == nil && !ok {
              err = ("ssh: command %v failed", cmd)
           }
           if err != nil {
              return err
           }
           return ()
        }

2.5 Sample code (execute command)

Here we use the Run() method to demonstrate that if you execute the command, you will not demonstrate other method types. Here we use a standard output stream and an error stream to save the execution results.

Here is a simple execution process, using the cd command to the /home/min directory, adding executable permissions to the helloworld program, and finally running the program.

        var stdoutBuf, stderrBuf 
         = &stdoutBuf
         = &stderrBuf
    
        // cd /home/min
        // chmod +x helloworld
        // ./helloworld
        cmd := ("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld)
        err := (cmd)
        if err != nil {
            ("[ERROR]: ", , err)
        }

2.6 (Create a pseudo-terminal)

        // Set Terminal Mode	modes := {
		:          0,     // Turn off echo		ssh.TTY_OP_ISPEED: 14400, // Set the transmission rate		ssh.TTY_OP_OSPEED: 14400,
	}
    
        // Request a pseudo-terminal	err = ("linux", 32, 160, modes)
	if err != nil {
		(err)
		return
	}
    
        // Set input and output	 = 
	 = 
	 = 
 
	() // Start the shell	()  // Wait for exit

2.7 Complete code

// Machine platform informationtype Machine struct {
   IP       string
   Port     string
   Username string
   Password string
}

// Establish SSH connectionfunc CreateSSHConn(m *) error {
   //Initialize connection information   config := &{
      User:            ,
      Auth:            []{()},
      HostKeyCallback: (),
      Timeout:         10 * ,
   }
   addr := ("%v:%v", , )

   //Create an ssh connection   sshClient, err := ("tcp", addr, config)
   if err != nil {
      ("unable create ssh conn", err)
      return err
   }
   defer ()
   
   //Create an ssh session   session, err := ()
   if err != nil {
      ("unable create ssh conn", err)
      return err
   }
   defer ()
   
   
   //Execute the command   var stdoutBuf, stderrBuf 
    = &stdoutBuf
    = &stderrBuf
   
   cmd := ("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld)
   if err := (cmd); err != nil {
       ("[ERROR]: ", , err)
   }
  
   //Create a pseudo-terminal   // Set Terminal Mode   modes := {
           :          0,     // Turn off echo           ssh.TTY_OP_ISPEED: 14400, // Set the transmission rate           ssh.TTY_OP_OSPEED: 14400,
   }
      
   // Request a pseudo-terminal   err = ("linux", 32, 160, modes)
   if err != nil {
           (err)
   }
      
   // Set input and output    = 
    = 
    = 
   
   () // Start the shell   ()  // Wait for exit   
   return err
}

This is the article about Golang's implementation of SSH and SFTP operations. For more related content on Golang's implementation of SSH and SFT, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!