GoLang 提供了基础的 SSH 库,可以用来作为服务端或者客户端使用,这里整理了常见的用法。
简单示例
package main
import (
"log"
//"bytes"
"os"
//"fmt"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
// 建立SSH客户端连接
client, err := gossh.Dial("tcp", "127.0.0.1:49154", &gossh.ClientConfig{
User: "root",
Auth: []gossh.AuthMethod{gossh.Password("admin")},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
})
if err != nil {
log.Fatalf("SSH dial error: %s", err.Error())
}
// 建立新会话
session, err := client.NewSession()
if err != nil {
log.Fatalf("new session error: %s", err.Error())
}
defer session.Close()
// session.run(command)是直接在host执行命令,不关心执行结果。session.Output是将执行命令之后的Stdout返回
/*
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("ls /"); err != nil {
panic("Failed to run: " + err.Error())
}
fmt.Println(b.String())
result, err := session.Output("ls -al")
if err != nil {
fmt.Fprintf(os.Stdout, "Failed to run command, Err:%s", err.Error())
os.Exit(0)
}
fmt.Println(string(result))
*/
// 使用VT100终端实现Tab提示、上下键历史查看、clear清屏等快捷键操作
fd := int(os.Stdin.Fd())
oldState, err := terminal.MakeRaw(fd)
if err != nil {
log.Fatalln(err.Error())
}
defer terminal.Restore(fd, oldState)
modes := gossh.TerminalModes{
gossh.ECHO: 1, // 禁用回显(0禁用,1启动)
gossh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
gossh.TTY_OP_OSPEED: 14400, //output speed = 14.4kbaud
}
session.Stdout = os.Stdout // 会话输出关联到系统标准输出设备
session.Stderr = os.Stderr // 会话错误输出关联到系统标准错误输出设备
session.Stdin = os.Stdin // 会话输入关联到系统标准输入设备
//if err = session.RequestPty("linux", 32, 160, modes); err != nil {
//if err = session.RequestPty("xterm", 32, 160, modes); err != nil {
termWidth, termHeight, _ := terminal.GetSize(fd)
if err = session.RequestPty("screen-256color", termHeight, termWidth, modes); err != nil {
log.Fatalf("request pty error: %s", err.Error())
}
if err = session.Shell(); err != nil {
log.Fatalf("start shell error: %s", err.Error())
}
if err = session.Wait(); err != nil {
log.Fatalf("return error: %s", err.Error())
}
}
证书登录
直接通过 ssh 生成的证书登录。
// parse the user's private key:
private, err := os.ReadFile("/tmp/id_cert_rsa")
if err != nil {
return err
}
signer, err := gossh.ParsePrivateKey(private)
if err != nil {
return err
}
// parse the user's certificate:
certs, err := os.ReadFile("/tmp/id_cert_rsa-cert.pub")
if err != nil {
return err
}
cert, _, _, _, err := gossh.ParseAuthorizedKey(certs)
if err != nil {
return err
}
// create a signer using both the certificate and the private key:
certSigner, err := gossh.NewCertSigner(cert.(*gossh.Certificate), signer)
if err != nil {
return err
}
// use that signer as an auth method in our client config:
client, err := gossh.Dial("tcp", "127.0.0.1:22", &gossh.ClientConfig{
User: "andy",
Auth: []gossh.AuthMethod{
gossh.PublicKeys(certSigner),
},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
})
if err != nil {
return err
}
session, err := client.NewSession()
if err != nil {
return err
}
var b bytes.Buffer
session.Stdout = &b
session.Run("ls /")
fmt.Println(b.String())
使用CA生成证书
// parse the user's private key:
private, err := os.ReadFile("/tmp/host_ca")
if err != nil {
log.Printf("read private failed %v\n", err)
return err
}
signer, err := gossh.ParsePrivateKey(private)
if err != nil {
log.Printf("parse private failed %v\n", err)
return err
}
//log.Printf("---> %v", gossh.PublicKeys(signer))
public, err := os.ReadFile("/tmp/ssh_host_rsa_key.pub")
if err != nil {
log.Printf("read public failed %v\n", err)
return err
}
key, _, _, _, err := gossh.ParseAuthorizedKey(public)
if err != nil {
log.Printf("parse public failed %v\n", err)
return err
}
/*
key, err := gossh.ParsePublicKey(public)
if err != nil {
log.Printf("parse public failed %v\n", err)
return err
}
*/
cert := &gossh.Certificate{
ValidPrincipals: []string{"127.0.0.1", "andy"},
Key: key,
ValidBefore: gossh.CertTimeInfinity,
CertType: gossh.HostCert,
}
if err := cert.SignCert(rand.Reader, signer); err != nil {
log.Printf("sign cert failed %v\n", err)
return err
}
log.Printf("---> %v\n", string(gossh.MarshalAuthorizedKey(cert)))
/*
certSigner, err := gossh.NewCertSigner(cert, testSigners["rsa"])
if err != nil {
t.Errorf("NewCertSigner: %v", err)
}
*/