164 lines
4.2 KiB
Go
164 lines
4.2 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"next-terminal/server/constant"
|
||
"next-terminal/server/log"
|
||
"next-terminal/server/model"
|
||
"next-terminal/server/repository"
|
||
"next-terminal/server/term"
|
||
"next-terminal/server/utils"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type ShellJob struct {
|
||
ID string
|
||
Mode string
|
||
ResourceIds string
|
||
Metadata string
|
||
}
|
||
|
||
type MetadataShell struct {
|
||
Shell string
|
||
}
|
||
|
||
func (r ShellJob) Run() {
|
||
if r.ID == "" {
|
||
return
|
||
}
|
||
|
||
var assets []model.Asset
|
||
if r.Mode == constant.JobModeAll {
|
||
assets, _ = repository.AssetRepository.FindByProtocol(context.TODO(), "ssh")
|
||
} else {
|
||
assets, _ = repository.AssetRepository.FindByProtocolAndIds(context.TODO(), "ssh", strings.Split(r.ResourceIds, ","))
|
||
}
|
||
|
||
if len(assets) == 0 {
|
||
return
|
||
}
|
||
|
||
var metadataShell MetadataShell
|
||
err := json.Unmarshal([]byte(r.Metadata), &metadataShell)
|
||
if err != nil {
|
||
log.Errorf("JSON数据解析失败 %v", err)
|
||
return
|
||
}
|
||
|
||
msgChan := make(chan string)
|
||
for i := range assets {
|
||
asset, err := AssetService.FindByIdAndDecrypt(context.TODO(), assets[i].ID)
|
||
if err != nil {
|
||
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error())
|
||
return
|
||
}
|
||
|
||
var (
|
||
username = asset.Username
|
||
password = asset.Password
|
||
privateKey = asset.PrivateKey
|
||
passphrase = asset.Passphrase
|
||
ip = asset.IP
|
||
port = asset.Port
|
||
)
|
||
|
||
if asset.AccountType == "credential" {
|
||
credential, err := CredentialService.FindByIdAndDecrypt(context.TODO(), asset.CredentialId)
|
||
if err != nil {
|
||
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error())
|
||
return
|
||
}
|
||
|
||
if credential.Type == constant.Custom {
|
||
username = credential.Username
|
||
password = credential.Password
|
||
} else {
|
||
username = credential.Username
|
||
privateKey = credential.PrivateKey
|
||
passphrase = credential.Passphrase
|
||
}
|
||
}
|
||
|
||
go func() {
|
||
t1 := time.Now()
|
||
result, err := exec(metadataShell.Shell, asset.AccessGatewayId, ip, port, username, password, privateKey, passphrase)
|
||
elapsed := time.Since(t1)
|
||
var msg string
|
||
if err != nil {
|
||
if errors.Is(gorm.ErrRecordNotFound, err) {
|
||
msg = fmt.Sprintf("资产「%v」Shell执行失败,请检查资产所关联接入网关是否存在,耗时「%v」", asset.Name, elapsed)
|
||
} else {
|
||
msg = fmt.Sprintf("资产「%v」Shell执行失败,错误内容为:「%v」,耗时「%v」", asset.Name, err.Error(), elapsed)
|
||
}
|
||
log.Infof(msg)
|
||
} else {
|
||
msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed)
|
||
log.Infof(msg)
|
||
}
|
||
|
||
msgChan <- msg
|
||
}()
|
||
}
|
||
|
||
var message = ""
|
||
for i := 0; i < len(assets); i++ {
|
||
message += <-msgChan + "\n"
|
||
}
|
||
|
||
_ = repository.JobRepository.UpdateLastUpdatedById(context.TODO(), r.ID)
|
||
jobLog := model.JobLog{
|
||
ID: utils.UUID(),
|
||
JobId: r.ID,
|
||
Timestamp: utils.NowJsonTime(),
|
||
Message: message,
|
||
}
|
||
|
||
_ = repository.JobLogRepository.Create(context.TODO(), &jobLog)
|
||
}
|
||
|
||
func exec(shell, accessGatewayId, ip string, port int, username, password, privateKey, passphrase string) (string, error) {
|
||
if accessGatewayId != "" && accessGatewayId != "-" {
|
||
g, err := GatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
uuid := utils.UUID()
|
||
exposedIP, exposedPort, err := g.OpenSshTunnel(uuid, ip, port)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer g.CloseSshTunnel(uuid)
|
||
return ExecCommandBySSH(shell, exposedIP, exposedPort, username, password, privateKey, passphrase)
|
||
} else {
|
||
return ExecCommandBySSH(shell, ip, port, username, password, privateKey, passphrase)
|
||
}
|
||
}
|
||
|
||
func ExecCommandBySSH(cmd, ip string, port int, username, password, privateKey, passphrase string) (result string, err error) {
|
||
sshClient, err := term.NewSshClient(ip, port, username, password, privateKey, passphrase)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
session, err := sshClient.NewSession()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer func() {
|
||
_ = session.Close()
|
||
}()
|
||
//执行远程命令
|
||
combo, err := session.CombinedOutput(cmd)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(combo), nil
|
||
}
|