mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package mcm
|
||
|
||
import (
|
||
"fmt"
|
||
"mayfly-go/internal/machine/domain/entity"
|
||
"mayfly-go/pkg/errorx"
|
||
"mayfly-go/pkg/logx"
|
||
"net"
|
||
"time"
|
||
|
||
"golang.org/x/crypto/ssh"
|
||
)
|
||
|
||
// 机器信息
|
||
type MachineInfo struct {
|
||
Id uint64 `json:"id"`
|
||
Name string `json:"name"`
|
||
|
||
Ip string `json:"ip"` // IP地址
|
||
Port int `json:"-"` // 端口号
|
||
AuthMethod int8 `json:"-"` // 授权认证方式
|
||
Username string `json:"-"` // 用户名
|
||
Password string `json:"-"`
|
||
Passphrase string `json:"-"` // 私钥口令
|
||
|
||
SshTunnelMachine *MachineInfo `json:"-"` // ssh隧道机器
|
||
EnableRecorder int8 `json:"-"` // 是否启用终端回放记录
|
||
TagPath []string `json:"tagPath"`
|
||
}
|
||
|
||
func (m *MachineInfo) UseSshTunnel() bool {
|
||
return m.SshTunnelMachine != nil
|
||
}
|
||
|
||
// 连接
|
||
func (mi *MachineInfo) Conn() (*Cli, error) {
|
||
logx.Infof("[%s]机器连接:%s:%d", mi.Name, mi.Ip, mi.Port)
|
||
|
||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||
err := mi.IfUseSshTunnelChangeIpPort()
|
||
if err != nil {
|
||
return nil, errorx.NewBiz("ssh隧道连接失败: %s", err.Error())
|
||
}
|
||
|
||
cli := &Cli{Info: mi}
|
||
sshClient, err := GetSshClient(mi)
|
||
if err != nil {
|
||
if mi.UseSshTunnel() {
|
||
CloseSshTunnelMachine(int(mi.SshTunnelMachine.Id), mi.Id)
|
||
}
|
||
return nil, err
|
||
}
|
||
cli.sshClient = sshClient
|
||
return cli, nil
|
||
}
|
||
|
||
// 如果使用了ssh隧道,则修改机器ip port为暴露的ip port
|
||
func (me *MachineInfo) IfUseSshTunnelChangeIpPort() error {
|
||
if !me.UseSshTunnel() {
|
||
return nil
|
||
}
|
||
|
||
originId := me.Id
|
||
if originId == 0 {
|
||
// 随机设置一个id,如果使用了隧道则用于临时保存隧道
|
||
me.Id = uint64(time.Now().Nanosecond())
|
||
}
|
||
|
||
sshTunnelMachine, err := GetSshTunnelMachine(int(me.SshTunnelMachine.Id), func(u uint64) (*MachineInfo, error) {
|
||
return me.SshTunnelMachine, nil
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
exposeIp, exposePort, err := sshTunnelMachine.OpenSshTunnel(me.Id, me.Ip, me.Port)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 修改机器ip地址
|
||
me.Ip = exposeIp
|
||
me.Port = exposePort
|
||
return nil
|
||
}
|
||
|
||
func GetSshClient(m *MachineInfo) (*ssh.Client, error) {
|
||
config := &ssh.ClientConfig{
|
||
User: m.Username,
|
||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||
return nil
|
||
},
|
||
Timeout: 5 * time.Second,
|
||
}
|
||
|
||
if m.AuthMethod == entity.AuthCertAuthMethodPassword {
|
||
config.Auth = []ssh.AuthMethod{ssh.Password(m.Password)}
|
||
} else if m.AuthMethod == entity.MachineAuthMethodPublicKey {
|
||
var key ssh.Signer
|
||
var err error
|
||
|
||
if len(m.Passphrase) > 0 {
|
||
key, err = ssh.ParsePrivateKeyWithPassphrase([]byte(m.Password), []byte(m.Passphrase))
|
||
} else {
|
||
key, err = ssh.ParsePrivateKey([]byte(m.Password))
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(key)}
|
||
}
|
||
|
||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||
sshClient, err := ssh.Dial("tcp", addr, config)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return sshClient, nil
|
||
}
|