>  기사  >  백엔드 개발  >  Golang이 SSH 관련 작업을 구현하는 방법을 자세히 설명하는 기사

Golang이 SSH 관련 작업을 구현하는 방법을 자세히 설명하는 기사

藏色散人
藏色散人앞으로
2022-11-09 16:01:374170검색

이 글은 golang tutorial 칼럼에서 golang이 SSH 연결 및 기타 관련 작업을 구현하는 방법을 소개하기 위해 작성되었습니다. SSH에 대해 얼마나 알고 계시나요? Go의 SSH 구현과 관련된 운영 문제에 대해 자세히 설명하겠습니다. 필요한 친구들에게 도움이 되길 바랍니다!

Golang이 SSH 관련 작업을 구현하는 방법을 자세히 설명하는 기사

1.ssh

1.1 서문

일부 개발 시나리오에서는 원격 서버와 통신하고 관련 명령 작업을 수행해야 합니다. 이때 SSH 프로토콜을 사용하여 목표를 달성할 수 있습니다. . SSH 프로토콜은 애플리케이션 계층에 구축된 보안 프로토콜로, 전체 이름은 Secure Shell입니다. 이는 전송에 연결 지향 TCP 프로토콜을 사용하므로 안전하고 신뢰할 수 있습니다. SSH 프로토콜에서는 파일 전송을 완료할 수 없으며 아래 언급된 SFTP 프로토콜에서 완료해야 한다는 점에 유의하세요.

1.2 Go 구현

Go는 SSH 연결을 구현하기 위한 패키지를 golang.org/x/crypto에 공식적으로 제공합니다. 프로그램에서 패키지에 제공된 관련 메소드를 호출하면 다른 시스템과 통신할 수 있습니다. 의사소통하다. 사용하기 전에 go get을 사용하여 관련 종속성 패키지를 가져와야 합니다.

go get golang.org/x/crypto/ssh

1.2.1 관련 매개변수 구성

통신하기 전에 연결 설정을 위한 일부 관련 매개변수 구성 도 구성해야 합니다. ssh 패키지 아래의 ClientConfig 구조는 SSH 연결을 설정하는 데 필요한 일부 구성 항목을 정의합니다. 일부 항목은 사용할 때 선언할 필요가 없는 기본 매개변수를 제공합니다.

아래 코드 조각에서는 먼저 사용자 이름과 비밀번호를 선언하고 연결 시간 제한을 10초로 설정하며 addr 변수는 대상 컴퓨터의 IP 주소와 포트를 정의합니다.

HostKeyCallback 항목을 무시하도록 설정했습니다. 이는 SSH 프로토콜이 클라이언트에 대해 두 가지 보안 확인 방법을 제공하기 때문입니다. 하나는 우리가 자주 사용하는 계정 비밀번호 형식이며, 다른 하나는 비교입니다. 첫 번째 유형의 키 기반 보안 검증인 이 형태의 검증 방법은 보안 수준을 크게 향상시키지만 상대적으로 시간이 오래 걸린다는 단점이 있습니다.

확인을 위해 이 방법을 사용해야 하는 경우 먼저 서버에서 자체 키 쌍을 생성해야 합니다. 클라이언트로 액세스할 때 먼저 서버가 보안 확인 요청을 보냅니다. 먼저, 머신에 저장된 공개 키와 클라이언트가 보낸 공개 키를 비교하여 일치하는 경우 서버는 클라이언트에 암호화 요청을 받은 후 개인 키를 사용합니다. 그 결과를 서버로 전송하고, 서버는 검증을 수행한 후 응답 결과를 반환합니다. 이 시점에서 키 검증 기간이 완료됩니다.

        //添加配置
        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 연결 설정

모든 매개변수 초기화가 완료되면 Dial 메소드를 호출하여 SSH 연결을 설정할 수 있습니다. Dial 메소드에는 총 3개의 매개변수와 2개의 반환 값이 있습니다. 여기서는 연결 지향 TCP 프로토콜을 사용합니다. 두 번째 매개변수 addr은 대상 컴퓨터의 IP 주소와 포트 번호입니다. 세 번째 매개변수는 네트워크 유형입니다. config 매개변수는 우리 전생의 구성 항목입니다. Dial은 SSH 연결 및 오류 유형을 반환합니다.

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 세션 생성

대상 시스템과 SSH 연결을 설정한 후 대상 시스템과 통신하는 SSH 세션을 생성할 수 있습니다. 대상 기계. 이 작업은 NewSession() 메서드를 통해 수행할 수 있습니다.

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

1.2.4 작업 실행

대상 머신과 세션을 설정한 후 명령 등을 실행하여 원격 서버를 작동할 수 있습니다. Go는 현재 원격 시스템을 작동하는 5가지 방법, 즉 Run(), Start(), Output(), CombineOutpt(), Shell()을 제공합니다.

? 그중 Output()과 **CombineOutpt()**는 Run() 메서드를 다양한 수준으로 캡슐화하고 출력 스트림, 오류 스트림 및 기타 관련 내용을 확인하는 두 가지 메서드입니다.

        // 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
        }

Run() 메서드는 Start() 메서드를 캡슐화하고 원격 서버의 종료 명령을 확인하기 위한 Wait 메서드를 추가합니다. Wait() 메서드에는 파이프라인 유형 변수 exitStatus가 있는데, 이는 각 명령 실행 후 머신에서 반환된 종료 상태를 저장하는 데 사용됩니다. 관심 있는 친구들은 이 작품의 코드를 확인할 수 있지만 여기에는 코드를 게시하지 않겠습니다.

여기에는 절대 멈추지 않는 프로그램을 실행하고 프로그램이 원격 컴퓨터에서 보낸 종료 명령을 기다리지 않으면 프로그램이 차단되고 돌아갈 수 없게 되는 함정이 있습니다. 보통. 해결책은 코루틴을 사용하여 이 작업을 별도로 실행하거나 타이머를 사용하여 세션을 정기적으로 종료하여 정상적으로 반환하는 것입니다.

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
}

위 내용은 Golang이 SSH 관련 작업을 구현하는 방법을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.im에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제