You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
3.8 KiB
176 lines
3.8 KiB
|
3 months ago
|
package tunnel
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"github.com/go-redis/redis/v8"
|
||
|
|
"golang.org/x/crypto/ssh"
|
||
|
|
"gorm.io/driver/mysql"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
"io"
|
||
|
|
"log"
|
||
|
|
"net"
|
||
|
|
"sync/atomic"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
/*
|
||
|
|
notice: 利用隧道代理实现本地连接服务器程序
|
||
|
|
*/
|
||
|
|
|
||
|
|
var (
|
||
|
|
// ssh配置
|
||
|
|
sshPem = `` // todo 私钥填充
|
||
|
|
sshHost = "47.106.150.32"
|
||
|
|
sshPort = 22
|
||
|
|
sshUser = "root"
|
||
|
|
sshPass = "cH6YIEPCCrFsTNJ4"
|
||
|
|
|
||
|
|
// mysql配置
|
||
|
|
mysqlHost = "127.0.0.1"
|
||
|
|
mysqlPort = 3306
|
||
|
|
mysqlUser = "root"
|
||
|
|
mysqlPass = "pFlu4oNgTKhMdttQ"
|
||
|
|
mysqlDB = "c_game"
|
||
|
|
|
||
|
|
// redis配置
|
||
|
|
redisHost = "127.0.0.1"
|
||
|
|
redisPort = 6379
|
||
|
|
redisPass = "cYUTZma6wEzrdvDJ"
|
||
|
|
redisDB = 1
|
||
|
|
|
||
|
|
// 本地转发端口
|
||
|
|
LocalPort int32 = 1000 // 从1000开始尝试20次
|
||
|
|
)
|
||
|
|
|
||
|
|
// 转发数据
|
||
|
|
func forward(local, remote net.Conn) {
|
||
|
|
defer local.Close()
|
||
|
|
defer remote.Close()
|
||
|
|
|
||
|
|
done := make(chan struct{})
|
||
|
|
go func() {
|
||
|
|
_, _ = io.Copy(local, remote)
|
||
|
|
done <- struct{}{}
|
||
|
|
}()
|
||
|
|
go func() {
|
||
|
|
_, _ = io.Copy(remote, local)
|
||
|
|
done <- struct{}{}
|
||
|
|
}()
|
||
|
|
<-done
|
||
|
|
}
|
||
|
|
|
||
|
|
func getListener() (net.Listener, int32) {
|
||
|
|
var (
|
||
|
|
listener net.Listener
|
||
|
|
err error
|
||
|
|
port int32
|
||
|
|
tryCount int32 = 100
|
||
|
|
)
|
||
|
|
for index := int32(0); index < tryCount; index++ {
|
||
|
|
port = atomic.AddInt32(&LocalPort, 1)
|
||
|
|
address := fmt.Sprintf("localhost:%d", port)
|
||
|
|
listener, err = net.Listen("tcp", address)
|
||
|
|
if err == nil && listener != nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
log.Printf("[WARN]: listen %s, listener[%+v] err[%+v]", address, listener, err)
|
||
|
|
}
|
||
|
|
if listener == nil && err != nil {
|
||
|
|
log.Fatalf("listener err, tryCount[%d] ", tryCount)
|
||
|
|
}
|
||
|
|
return listener, port
|
||
|
|
}
|
||
|
|
|
||
|
|
func getSshConfig() (cfg *ssh.ClientConfig) {
|
||
|
|
cfg = &ssh.ClientConfig{
|
||
|
|
User: sshUser,
|
||
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||
|
|
Timeout: 10 * time.Second,
|
||
|
|
}
|
||
|
|
if sshPem != "" {
|
||
|
|
signer, err := ssh.ParsePrivateKey([]byte(sshPem))
|
||
|
|
if err != nil {
|
||
|
|
log.Fatalf("Failed to parse private key: %s", err.Error())
|
||
|
|
}
|
||
|
|
cfg.Auth = append(cfg.Auth, ssh.PublicKeys(signer))
|
||
|
|
}
|
||
|
|
if sshPass != "" {
|
||
|
|
cfg.Auth = append(cfg.Auth, ssh.Password(sshPass))
|
||
|
|
}
|
||
|
|
return cfg
|
||
|
|
}
|
||
|
|
|
||
|
|
func binding(listener net.Listener, sshClient *ssh.Client, processHost string, processPort int) {
|
||
|
|
for {
|
||
|
|
localConn, err := listener.Accept()
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Failed to accept connection: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
remoteConn, err := sshClient.Dial("tcp", fmt.Sprintf("%s:%d", processHost, processPort))
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Failed to dial remote Redis: %v", err)
|
||
|
|
localConn.Close()
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
go forward(localConn, remoteConn)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func getSshClient() *ssh.Client {
|
||
|
|
sshConfig := getSshConfig()
|
||
|
|
sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", sshHost, sshPort), sshConfig)
|
||
|
|
if err != nil {
|
||
|
|
log.Fatalf("Failed to dial SSH: %v", err)
|
||
|
|
}
|
||
|
|
return sshClient
|
||
|
|
}
|
||
|
|
|
||
|
|
func GetMysql() *gorm.DB {
|
||
|
|
// 连接服务器
|
||
|
|
sshClient := getSshClient()
|
||
|
|
// 获取本地监听
|
||
|
|
listener, localPort := getListener()
|
||
|
|
// 转发进程数据
|
||
|
|
go binding(listener, sshClient, mysqlHost, mysqlPort)
|
||
|
|
// 连接本地端口
|
||
|
|
dsn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", mysqlUser, mysqlPass, localPort, mysqlDB)
|
||
|
|
// 打开数据库连接
|
||
|
|
db, err := gorm.Open(mysql.New(mysql.Config{
|
||
|
|
DSN: dsn,
|
||
|
|
}), &gorm.Config{})
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
log.Fatalf("create db err, %s", err.Error())
|
||
|
|
}
|
||
|
|
|
||
|
|
return db
|
||
|
|
}
|
||
|
|
|
||
|
|
func GetRedis() (*redis.Client, int32) {
|
||
|
|
// 连接服务器
|
||
|
|
sshClient := getSshClient()
|
||
|
|
// 建立本地监听
|
||
|
|
listener, localPort := getListener()
|
||
|
|
// 转发进程数据
|
||
|
|
go binding(listener, sshClient, redisHost, redisPort)
|
||
|
|
// 连接本地端口
|
||
|
|
rdb := redis.NewClient(&redis.Options{
|
||
|
|
Addr: fmt.Sprintf("127.0.0.1:%d", localPort),
|
||
|
|
Password: redisPass,
|
||
|
|
DB: redisDB,
|
||
|
|
})
|
||
|
|
|
||
|
|
ctx := context.Background()
|
||
|
|
v, err := rdb.Ping(ctx).Result()
|
||
|
|
_ = v
|
||
|
|
if err != nil {
|
||
|
|
log.Fatalf("Failed to connect to Redis: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return rdb, localPort
|
||
|
|
}
|