在Go语言中,golang.org/x/crypto/ssh 是一个官方维护的第三方库,用于实现 SSH 客户端和服务器功能。本文我们将学习如何使用该库建立 SSH 连接、执行远程命令、模拟终端交互等常见操作。


一、安装 golang.org/x/crypto/ssh

首先,确保你的 Go 环境已安装。然后,通过以下命令安装 SSH 库:

go get -u golang.org/x/crypto/ssh

二、配置 SSH 客户端

使用 ssh.ClientConfig 结构体配置 SSH 客户端:

package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"log"
)

func createSSHClient(user, password, host string, port int) (*ssh.Client, error) {
	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config)
	if err != nil {
		return nil, err
	}
	return client, nil
}

在上述代码中,HostKeyCallback 使用 ssh.InsecureIgnoreHostKey() 忽略主机密钥验证,适用于开发和测试环境。在生产环境中,应使用更安全的方式验证主机密钥。


三、执行远程命令

通过 client.NewSession() 创建会话,并使用 session.Run() 执行命令:

func runCommand(client *ssh.Client, command string) (string, error) {
	session, err := client.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()
	output, err := session.CombinedOutput(command)
	if err != nil {
		return "", err
	}
	return string(output), nil
}

CombinedOutput 方法返回命令的标准输出和标准错误输出。


四、模拟终端交互

如果需要模拟终端交互(例如执行 top 命令),可以使用伪终端(PTY):

func startInteractiveShell(client *ssh.Client) error {
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}
	termWidth, termHeight, err := term.GetSize(int(os.Stdout.Fd()))
	if err != nil {
		return err
	}
	if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil {
		return err
	}
	session.Stdin = os.Stdin
	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	if err := session.Shell(); err != nil {
		return err
	}
	return session.Wait()
}

上述代码请求一个伪终端,并将标准输入、输出和错误输出绑定到本地终端,实现交互式操作。


五、使用公钥认证

为了提高安全性,可以使用公钥认证替代密码认证:

func createSSHClientWithKey(user, keyPath, host string, port int) (*ssh.Client, error) {
	key, err := ioutil.ReadFile(keyPath)
	if err != nil {
		return nil, err
	}
	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, err
	}
	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config)
	if err != nil {
		return nil, err
	}
	return client, nil
}

在上述代码中,keyPath 是私钥文件的路径。ssh.PublicKeys(signer) 用于创建公钥认证方法。


六、处理主机密钥验证

在生产环境中,建议验证服务器的主机密钥,以防止中间人攻击:

func verifyHostKey(host string, remote net.Addr, key ssh.PublicKey) error {
	// 实现主机密钥验证逻辑
	return nil
}

可以通过 ssh.FixedHostKey() 或自定义回调函数来实现主机密钥验证。


七、完整示例

以下是一个完整的示例,演示如何使用密码认证连接 SSH 服务器并执行命令:

package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"log"
)

func main() {
	client, err := createSSHClient("user", "password", "example.com", 22)
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}
	defer client.Close()

	output, err := runCommand(client, "ls -al")
	if err != nil {
		log.Fatalf("Failed to run command: %v", err)
	}
	fmt.Println(output)
}

孟斯特

声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特