- PublicKey">type PublicKey
- AuthMethod">type AuthMethod
- ClientConfig">type ClientConfig
- Client">type Client
- Session">type Session
- Signal">type Signal
- 例子:
- 交互式执行
- 注意:
import “golang.org/x/crypto/ssh”
文档 https://godoc.org/golang.org/x/crypto/ssh
type PublicKey
type PublicKey interface {
// Type returns the key's type, e.g. "ssh-rsa".
Type() string
// Marshal returns the serialized key data in SSH wire format,
// with the name prefix. To unmarshal the returned data, use
// the ParsePublicKey function.
Marshal() []byte
// Verify that sig is a signature on the given data using this
// key. This function will hash the data appropriately first.
Verify(data []byte, sig *Signature) error
}
func NewPublicKey(key interface{}) (PublicKey, error)
type AuthMethod
func Password(secret string) AuthMethod 使用密码
func PublicKeys(signers …Signer) AuthMethod 使用密钥
var hostKey ssh.PublicKey
key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
log.Fatalf("unable to connect: %v", err)
}
defer client.Close()
type ClientConfig
type ClientConfig struct {
Config
User string
Auth []AuthMethod
HostKeyCallback HostKeyCallback
BannerCallback BannerCallback
ClientVersion string
HostKeyAlgorithms []string
Timeout time.Duration
}
type Client
func Dial(network, addr string, config ClientConfig) (Client, error)
func (c Client) NewSession() (Session, error)
type Session
一个会话只接受一个run、start或Shell调用。
type Session struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
// contains filtered or unexported fields
}
func (s Session) Close() error
func (s Session) CombinedOutput(cmd string) ([]byte, error) 返回标准输出和标准错误,以0的状态退出错误为nil
本机命令错误才算错误,远程错误属于标准输出
res:scp -p 22 root@1.1.1.1 cat11 /home/test.txt || 1.1.1.1机器上cat11不是一个命令,CombinedOutput认为他是标准输出
res:scp11 -p 22 root@1.1.1.1 cat /home/test.txt || scp11 命令在本机不存在,这样才是标准错误
func (s Session) Output(cmd string) ([]byte, error) 返回标准输出,以0的状态退出,则返回的错误为nil
func (s Session) Run(cmd string) error
- 一个session应只能调用一次,stdin、stdout和stderr没有问题,且以0的状态退出,则返回的错误为nil
func (s Session) Setenv(name, value string) error 设置一个环境变量,该变量将用于Shell或Run执行的任何命令func (s Session) Signal(sig Signal) error 将给定的信号发送给远程进程
func (s Session) Start(cmd string) error 在远程主机上运行cmd
func (s Session) StderrPipe() (io.Reader, error) 错误信息,与标准输出信息一直
func (s Session) StdinPipe() (io.WriteCloser, error) 输入信息
func (s Session) StdoutPipe() (io.Reader, error) 输出信息,与标准错误信息一直
func (s *Session) Wait() error Start+Wait = run
type Signal
type Signal string
const (
SIGABRT Signal = "ABRT"
SIGALRM Signal = "ALRM"
SIGFPE Signal = "FPE"
SIGHUP Signal = "HUP"
SIGILL Signal = "ILL"
SIGINT Signal = "INT"
SIGKILL Signal = "KILL"
SIGPIPE Signal = "PIPE"
SIGQUIT Signal = "QUIT"
SIGSEGV Signal = "SEGV"
SIGTERM Signal = "TERM"
SIGUSR1 Signal = "USR1"
SIGUSR2 Signal = "USR2"
)
例子:
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"log"
"net"
"time"
)
func connect(user, password, host string, port int) (*ssh.Session, error) {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
session *ssh.Session
err error
)
// get auth method
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(password))
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: 30 * time.Second,
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
// connet to ssh
addr = fmt.Sprintf("%s:%d", host, port)
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
return nil, err
}
// create session
if session, err = client.NewSession(); err != nil {
return nil, err
}
return session, nil
}
func main() {
session, err := connect("root", "*****", "*.*.*.*", 22)
if err != nil {
log.Fatal("error:",err)
}
defer session.Close()
res,err := session.CombinedOutput("cd /home/jw && ./shell01.sh")
if err != nil {
log.Fatal("error:",err)
}
res_str := fmt.Sprintf("%s",res)
fmt.Println(res_str)
}
交互式执行
package main
import (
"bytes"
"fmt"
"golang.org/x/crypto/ssh"
"log"
"sync"
)
type MyReader struct {
channel chan string
}
func newReader() *MyReader {
reader := new(MyReader)
reader.channel = make(chan string)
return reader
}
func (r *MyReader) Read(p []byte) (n int, err error) {
var cmd string
cmd = <-r.channel
cmdB := []byte(cmd + "\n")
for i, v := range cmdB {
p[i] = v
}
n = len(cmdB)
return n, err
}
type MyWriter struct {
channel chan string
}
func newWriter() *MyWriter {
writer := new(MyWriter)
writer.channel = make(chan string)
return writer
}
func (w *MyWriter) Write(p []byte) (n int, err error) {
res := string(p)
//fmt.Println(res)
w.channel <- res
return len(p), err
}
func main() {
// 建立SSH客户端连接
client, err := ssh.Dial("tcp", "47.107.180.221:22", &ssh.ClientConfig{
User: "public",
Auth: []ssh.AuthMethod{ssh.Password("public")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
log.Fatalf("SSH dial error: %s", err.Error())
}
// 建立新会话
session, err := client.NewSession()
defer session.Close()
if err != nil {
log.Fatalf("new session error: %s", err.Error())
}
writer := newWriter()
reader := newReader()
session.Stdout = writer // 会话输出
session.Stderr = writer // 会话错误
session.Stdin = reader // 会话输入
go func() {
for {
output, ok := <-writer.channel
if !ok {
break
}
if strings.Contains(output, "-bash:") {
err = errors.New(output)
}
fmt.Println(output)
}
}()
s := "cd /home;ll -a \n exit \n"
go func() {
//f := bufio.NewReader(os.Stdin) //读取输入的内容
f := bytes.NewBufferString(s)
for {
input, err := f.ReadString('\n') //定义一行输入的内容分隔符。
if err != nil {
break
}
reader.channel <- input
}
}()
ExecuteTerminal(session)
}
// 这里是一个阻塞会话,没遇到exit这里不会退出
func ExecuteTerminal(session *ssh.Session) {
var err error
modes := ssh.TerminalModes{
ssh.ECHO: 0, // 禁用回显(0禁用,1启动)
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, //output speed = 14.4kbaud
}
if err = session.RequestPty("linux", 32, 160, 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())
}
}
注意:
- cat 一个不存在的文件会报错
- ls -la 查看隐藏文件,不能用ll -a,会报错
- ls -l | grep -w xxx ,如果xxx不存在,会报错
不是所有的linux命令用ssh去执行与本机执行的结果是一样的
例如:
ssh远程执行
本机执行