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 }