1Panel/core/utils/ssh/ssh.go
2024-07-25 14:43:41 +08:00

143 lines
3.1 KiB
Go

package ssh
import (
"bytes"
"fmt"
"io"
"strings"
"sync"
"time"
gossh "golang.org/x/crypto/ssh"
)
type ConnInfo struct {
User string `json:"user"`
Addr string `json:"addr"`
Port int `json:"port"`
AuthMode string `json:"authMode"`
Password string `json:"password"`
PrivateKey []byte `json:"privateKey"`
PassPhrase []byte `json:"passPhrase"`
DialTimeOut time.Duration `json:"dialTimeOut"`
Client *gossh.Client `json:"client"`
Session *gossh.Session `json:"session"`
LastResult string `json:"lastResult"`
}
func (c *ConnInfo) NewClient() (*ConnInfo, error) {
if strings.Contains(c.Addr, ":") {
c.Addr = fmt.Sprintf("[%s]", c.Addr)
}
config := &gossh.ClientConfig{}
config.SetDefaults()
addr := fmt.Sprintf("%s:%d", c.Addr, c.Port)
config.User = c.User
if c.AuthMode == "password" {
config.Auth = []gossh.AuthMethod{gossh.Password(c.Password)}
} else {
signer, err := makePrivateKeySigner(c.PrivateKey, c.PassPhrase)
if err != nil {
return nil, err
}
config.Auth = []gossh.AuthMethod{gossh.PublicKeys(signer)}
}
if c.DialTimeOut == 0 {
c.DialTimeOut = 5 * time.Second
}
config.Timeout = c.DialTimeOut
config.HostKeyCallback = gossh.InsecureIgnoreHostKey()
proto := "tcp"
if strings.Contains(c.Addr, ":") {
proto = "tcp6"
}
client, err := gossh.Dial(proto, addr, config)
if nil != err {
return c, err
}
c.Client = client
return c, nil
}
func (c *ConnInfo) Run(shell string) (string, error) {
if c.Client == nil {
if _, err := c.NewClient(); err != nil {
return "", err
}
}
session, err := c.Client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
buf, err := session.CombinedOutput(shell)
c.LastResult = string(buf)
return c.LastResult, err
}
func (c *ConnInfo) Close() {
_ = c.Client.Close()
}
type SshConn struct {
StdinPipe io.WriteCloser
ComboOutput *wsBufferWriter
Session *gossh.Session
}
func (c *ConnInfo) NewSshConn(cols, rows int) (*SshConn, error) {
sshSession, err := c.Client.NewSession()
if err != nil {
return nil, err
}
stdinP, err := sshSession.StdinPipe()
if err != nil {
return nil, err
}
comboWriter := new(wsBufferWriter)
sshSession.Stdout = comboWriter
sshSession.Stderr = comboWriter
modes := gossh.TerminalModes{
gossh.ECHO: 1,
gossh.TTY_OP_ISPEED: 14400,
gossh.TTY_OP_OSPEED: 14400,
}
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
return nil, err
}
if err := sshSession.Shell(); err != nil {
return nil, err
}
return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
}
func (s *SshConn) Close() {
if s.Session != nil {
s.Session.Close()
}
}
type wsBufferWriter struct {
buffer bytes.Buffer
mu sync.Mutex
}
func (w *wsBufferWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.buffer.Write(p)
}
func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
if len(passPhrase) != 0 {
return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
}
return gossh.ParsePrivateKey(privateKey)
}