2023-10-30 17:34:56 +08:00
|
|
|
|
package mcm
|
2022-07-23 16:41:04 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"io"
|
2023-09-02 17:24:18 +08:00
|
|
|
|
"mayfly-go/pkg/logx"
|
2022-07-24 15:37:13 +08:00
|
|
|
|
"mayfly-go/pkg/scheduler"
|
2023-07-21 17:07:04 +08:00
|
|
|
|
"mayfly-go/pkg/utils/netx"
|
2022-07-23 16:41:04 +08:00
|
|
|
|
"net"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
2023-03-06 16:59:57 +08:00
|
|
|
|
sshTunnelMachines map[int]*SshTunnelMachine = make(map[int]*SshTunnelMachine)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
|
|
|
|
|
|
mutex sync.Mutex
|
|
|
|
|
|
|
|
|
|
|
|
// 所有检测ssh隧道机器是否被使用的函数
|
|
|
|
|
|
checkSshTunnelMachineHasUseFuncs []CheckSshTunnelMachineHasUseFunc
|
|
|
|
|
|
|
|
|
|
|
|
// 是否开启检查ssh隧道机器是否被使用,只有使用到了隧道机器才启用
|
|
|
|
|
|
startCheckSshTunnelHasUse bool = false
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 检查ssh隧道机器是否有被使用
|
2023-03-06 16:59:57 +08:00
|
|
|
|
type CheckSshTunnelMachineHasUseFunc func(int) bool
|
2022-07-23 16:41:04 +08:00
|
|
|
|
|
|
|
|
|
|
func startCheckUse() {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Info("开启定时检测ssh隧道机器是否还有被使用")
|
2022-07-24 15:37:13 +08:00
|
|
|
|
// 每十分钟检查一次隧道机器是否还有被使用
|
|
|
|
|
|
scheduler.AddFun("@every 10m", func() {
|
|
|
|
|
|
if !mutex.TryLock() {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
// 遍历隧道机器,都未被使用将会被关闭
|
|
|
|
|
|
for mid, sshTunnelMachine := range sshTunnelMachines {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("开始定时检查ssh隧道机器[%d]是否还有被使用...", mid)
|
2022-07-24 15:37:13 +08:00
|
|
|
|
hasUse := false
|
|
|
|
|
|
for _, checkUseFunc := range checkSshTunnelMachineHasUseFuncs {
|
|
|
|
|
|
// 如果一个在使用则返回不关闭,不继续后续检查
|
|
|
|
|
|
if checkUseFunc(mid) {
|
|
|
|
|
|
hasUse = true
|
|
|
|
|
|
break
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
2022-07-24 15:37:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
if !hasUse {
|
|
|
|
|
|
// 都未被使用,则关闭
|
|
|
|
|
|
sshTunnelMachine.Close()
|
|
|
|
|
|
}
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
2022-07-24 15:37:13 +08:00
|
|
|
|
})
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加ssh隧道机器检测是否使用函数
|
|
|
|
|
|
func AddCheckSshTunnelMachineUseFunc(checkFunc CheckSshTunnelMachineHasUseFunc) {
|
|
|
|
|
|
if checkSshTunnelMachineHasUseFuncs == nil {
|
|
|
|
|
|
checkSshTunnelMachineHasUseFuncs = make([]CheckSshTunnelMachineHasUseFunc, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
checkSshTunnelMachineHasUseFuncs = append(checkSshTunnelMachineHasUseFuncs, checkFunc)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ssh隧道机器
|
|
|
|
|
|
type SshTunnelMachine struct {
|
2023-03-06 16:59:57 +08:00
|
|
|
|
machineId int // 隧道机器id
|
2022-07-23 16:41:04 +08:00
|
|
|
|
SshClient *ssh.Client
|
|
|
|
|
|
mutex sync.Mutex
|
2023-12-20 23:01:51 +08:00
|
|
|
|
tunnels map[string]*Tunnel // 隧道id -> 隧道
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-20 23:01:51 +08:00
|
|
|
|
func (stm *SshTunnelMachine) OpenSshTunnel(id string, ip string, port int) (exposedIp string, exposedPort int, err error) {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
stm.mutex.Lock()
|
|
|
|
|
|
defer stm.mutex.Unlock()
|
|
|
|
|
|
|
2024-01-05 22:16:38 +08:00
|
|
|
|
tunnel := stm.tunnels[id]
|
|
|
|
|
|
// 已存在该id隧道,则直接返回
|
|
|
|
|
|
if tunnel != nil {
|
|
|
|
|
|
return tunnel.localHost, tunnel.localPort, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-21 17:07:04 +08:00
|
|
|
|
localPort, err := netx.GetAvailablePort()
|
2022-07-23 16:41:04 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-01 04:03:03 +00:00
|
|
|
|
localHost := "127.0.0.1"
|
|
|
|
|
|
localAddr := fmt.Sprintf("%s:%d", localHost, localPort)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
listener, err := net.Listen("tcp", localAddr)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-05 22:16:38 +08:00
|
|
|
|
tunnel = &Tunnel{
|
2022-07-23 16:41:04 +08:00
|
|
|
|
id: id,
|
|
|
|
|
|
machineId: stm.machineId,
|
2024-03-01 04:03:03 +00:00
|
|
|
|
localHost: localHost,
|
2022-07-23 16:41:04 +08:00
|
|
|
|
localPort: localPort,
|
|
|
|
|
|
remoteHost: ip,
|
|
|
|
|
|
remotePort: port,
|
|
|
|
|
|
listener: listener,
|
|
|
|
|
|
}
|
|
|
|
|
|
go tunnel.Open(stm.SshClient)
|
|
|
|
|
|
stm.tunnels[tunnel.id] = tunnel
|
|
|
|
|
|
|
|
|
|
|
|
return tunnel.localHost, tunnel.localPort, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (st *SshTunnelMachine) GetDialConn(network string, addr string) (net.Conn, error) {
|
|
|
|
|
|
st.mutex.Lock()
|
|
|
|
|
|
defer st.mutex.Unlock()
|
|
|
|
|
|
return st.SshClient.Dial(network, addr)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (stm *SshTunnelMachine) Close() {
|
|
|
|
|
|
stm.mutex.Lock()
|
|
|
|
|
|
defer stm.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
for id, tunnel := range stm.tunnels {
|
|
|
|
|
|
if tunnel != nil {
|
|
|
|
|
|
tunnel.Close()
|
|
|
|
|
|
delete(stm.tunnels, id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if stm.SshClient != nil {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Infof("ssh隧道机器[%d]未被使用, 关闭隧道...", stm.machineId)
|
2022-07-24 15:37:13 +08:00
|
|
|
|
err := stm.SshClient.Close()
|
|
|
|
|
|
if err != nil {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Errorf("关闭ssh隧道机器[%d]发生错误: %s", stm.machineId, err.Error())
|
2022-07-24 15:37:13 +08:00
|
|
|
|
}
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
delete(sshTunnelMachines, stm.machineId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取ssh隧道机器,方便统一管理充当ssh隧道的机器,避免创建多个ssh client
|
2023-10-30 17:34:56 +08:00
|
|
|
|
func GetSshTunnelMachine(machineId int, getMachine func(uint64) (*MachineInfo, error)) (*SshTunnelMachine, error) {
|
|
|
|
|
|
mutex.Lock()
|
|
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
|
2022-07-23 16:41:04 +08:00
|
|
|
|
sshTunnelMachine := sshTunnelMachines[machineId]
|
|
|
|
|
|
if sshTunnelMachine != nil {
|
|
|
|
|
|
return sshTunnelMachine, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-26 17:15:49 +08:00
|
|
|
|
me, err := getMachine(uint64(machineId))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-29 04:20:23 +00:00
|
|
|
|
sshClient, err := GetSshClient(me, nil)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2023-12-20 23:01:51 +08:00
|
|
|
|
sshTunnelMachine = &SshTunnelMachine{SshClient: sshClient, machineId: machineId, tunnels: map[string]*Tunnel{}}
|
2022-07-23 16:41:04 +08:00
|
|
|
|
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Infof("初次连接ssh隧道机器[%d][%s:%d]", machineId, me.Ip, me.Port)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
sshTunnelMachines[machineId] = sshTunnelMachine
|
|
|
|
|
|
|
|
|
|
|
|
// 如果实用了隧道机器且还没开始定时检查是否还被实用,则执行定时任务检测隧道是否还被使用
|
|
|
|
|
|
if !startCheckSshTunnelHasUse {
|
|
|
|
|
|
startCheckUse()
|
|
|
|
|
|
startCheckSshTunnelHasUse = true
|
|
|
|
|
|
}
|
|
|
|
|
|
return sshTunnelMachine, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭ssh隧道机器的指定隧道
|
2023-12-20 23:01:51 +08:00
|
|
|
|
func CloseSshTunnelMachine(machineId int, tunnelId string) {
|
2022-07-23 16:41:04 +08:00
|
|
|
|
sshTunnelMachine := sshTunnelMachines[machineId]
|
|
|
|
|
|
if sshTunnelMachine == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sshTunnelMachine.mutex.Lock()
|
|
|
|
|
|
defer sshTunnelMachine.mutex.Unlock()
|
|
|
|
|
|
t := sshTunnelMachine.tunnels[tunnelId]
|
|
|
|
|
|
if t != nil {
|
|
|
|
|
|
t.Close()
|
|
|
|
|
|
delete(sshTunnelMachine.tunnels, tunnelId)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Tunnel struct {
|
2023-12-20 23:01:51 +08:00
|
|
|
|
id string // 唯一标识
|
2023-03-06 16:59:57 +08:00
|
|
|
|
machineId int // 隧道机器id
|
2022-07-23 16:41:04 +08:00
|
|
|
|
localHost string // 本地监听地址
|
|
|
|
|
|
localPort int // 本地端口
|
|
|
|
|
|
remoteHost string // 远程连接地址
|
|
|
|
|
|
remotePort int // 远程端口
|
|
|
|
|
|
listener net.Listener
|
|
|
|
|
|
localConnections []net.Conn
|
|
|
|
|
|
remoteConnections []net.Conn
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *Tunnel) Open(sshClient *ssh.Client) {
|
|
|
|
|
|
localAddr := fmt.Sprintf("%s:%d", r.localHost, r.localPort)
|
|
|
|
|
|
|
|
|
|
|
|
for {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 等待客户端访问 %v", r.id, localAddr)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
localConn, err := r.listener.Accept()
|
|
|
|
|
|
if err != nil {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 接受连接失败 %v, 退出循环", r.id, err.Error())
|
|
|
|
|
|
logx.Debug("-------------------------------------------------")
|
2022-07-23 16:41:04 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
r.localConnections = append(r.localConnections, localConn)
|
|
|
|
|
|
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 新增本地连接 %v", r.id, localConn.RemoteAddr().String())
|
2022-07-23 16:41:04 +08:00
|
|
|
|
remoteAddr := fmt.Sprintf("%s:%d", r.remoteHost, r.remotePort)
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 连接远程地址 %v ...", r.id, remoteAddr)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
remoteConn, err := sshClient.Dial("tcp", remoteAddr)
|
|
|
|
|
|
if err != nil {
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 连接远程地址 %v, 退出循环", r.id, err.Error())
|
|
|
|
|
|
logx.Debug("-------------------------------------------------")
|
2022-07-23 16:41:04 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
r.remoteConnections = append(r.remoteConnections, remoteConn)
|
|
|
|
|
|
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 连接远程主机成功", r.id)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
go copyConn(localConn, remoteConn)
|
|
|
|
|
|
go copyConn(remoteConn, localConn)
|
2023-09-02 17:24:18 +08:00
|
|
|
|
logx.Debugf("隧道 %v 开始转发数据 [%v]->[%v]", r.id, localAddr, remoteAddr)
|
|
|
|
|
|
logx.Debug("~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~")
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *Tunnel) Close() {
|
|
|
|
|
|
for i := range r.localConnections {
|
|
|
|
|
|
_ = r.localConnections[i].Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
r.localConnections = nil
|
|
|
|
|
|
for i := range r.remoteConnections {
|
|
|
|
|
|
_ = r.remoteConnections[i].Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
r.remoteConnections = nil
|
|
|
|
|
|
_ = r.listener.Close()
|
2023-12-20 23:01:51 +08:00
|
|
|
|
logx.Debugf("隧道 %s 监听器关闭", r.id)
|
2022-07-23 16:41:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func copyConn(writer, reader net.Conn) {
|
|
|
|
|
|
_, _ = io.Copy(writer, reader)
|
|
|
|
|
|
}
|