Golang中反向SSH因不活动而关闭的问题探讨

Golang中反向SSH因不活动而关闭的问题探讨 我正在尝试通过Go连接一个反向SSH隧道。目前已经可以工作,但隧道在一段时间后会自动关闭。正如我已经尝试过的(见代码),它并没有生效。有没有什么方法可以实现与终端中ServerAliveInterval相同的功能?有人知道建议吗?谢谢!

sshConfig := &ssh.ClientConfig{

    // SSH连接用户名

    User: "remoteuser",

    Auth: []ssh.AuthMethod{

        // 在此处填入你的私钥路径

        publicKeyFile("C:/Users/user/.ssh/id_rsa"),

    },

    HostKeyCallback: ssh.InsecureIgnoreHostKey(),

    Timeout:         15 * time.Second,

}

// 使用serverEndpoint连接到SSH远程服务器

serverConn, err := ssh.Dial("tcp", serverEndpoint.String(), sshConfig)

if err != nil {

    log.Fatalln(fmt.Printf("连接到远程服务器时出错: %s", err))

}

// 监听远程服务器端口

listener, err := serverConn.Listen("tcp", remoteEndpoint.String())

if err != nil {

    log.Fatalln(fmt.Printf("在远程服务器上打开端口监听时出错: %s", err))

}

defer listener.Close()

// 处理反向转发隧道上的传入连接

for {

    // 打开一个(本地)连接到localEndpoint,其内容将被转发到serverEndpoint

    local, err := net.Dial("tcp", localEndpoint.String())

    if err != nil {

        log.Fatalln(fmt.Printf("连接到本地服务时出错: %s", err))

    }       

    

    client, err := listener.Accept()

    if err != nil {

        log.Fatalln(err)

    }

    _, _, err = serverConn.SendRequest("keepalive", true, []byte("sup"))

    ticker := time.NewTicker(45 * time.Second)

    quit := make(chan struct{})

    go func() {

        for {

            select {

                case <- ticker.C:

                    _, _, err = serverConn.SendRequest("keepalive", true, []byte("ls"))

                    fmt.Println("WhatsUp, "+user.Name+"?")

                case <- quit:

                    ticker.Stop()

                return

    }

}
    fmt.Println("OK")

    handleClient(client, local)

}

更多关于Golang中反向SSH因不活动而关闭的问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

上次是我的转发测试,需要客户端发送保活请求。

GitHub 保活示例

更多关于Golang中反向SSH因不活动而关闭的问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


package main

import (
	"log"
	"net"
	"net/http"
	"time"

	"golang.org/x/crypto/ssh"
)

func main() {
	client, err := ssh.Dial("tcp", "192.168.75.4:8089", &ssh.ClientConfig{
		User: "root",
		Auth: []ssh.AuthMethod{
			ssh.Password(""),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	})
	if err != nil {
		log.Panicln(err)
		return
	}

	ln, err := client.ListenTCP(&net.TCPAddr{
		IP:   net.ParseIP("0.0.0.0"),
		Port: 9091,
	})
	if err != nil {
		log.Panicln(err)
		return
	}
	defer ln.Close()

	go http.Serve(ln, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		resp.Header().Add("Message", "hello ssh")
	}))
	http.DefaultClient.Timeout = 3 * time.Second
	resp, err := http.Get("http://192.168.75.4:9091/ssh/forward-tcp")
	if err != nil {
		log.Println("direct read response error:", err)
		return
	}
	log.Printf("direct request http status: %s,headers: %v\n", resp.Status, resp.Header)
}

在Go中实现SSH连接的保活机制,可以通过配置ClientConfigClientVersion字段并定期发送keepalive请求来实现。以下是修改后的代码示例:

package main

import (
    "fmt"
    "log"
    "net"
    "time"
    
    "golang.org/x/crypto/ssh"
)

func main() {
    sshConfig := &ssh.ClientConfig{
        User: "remoteuser",
        Auth: []ssh.AuthMethod{
            publicKeyFile("C:/Users/user/.ssh/id_rsa"),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Timeout:         15 * time.Second,
        // 添加ClientVersion以启用keepalive支持
        ClientVersion: "SSH-2.0-Go-keepalive",
    }

    serverConn, err := ssh.Dial("tcp", serverEndpoint.String(), sshConfig)
    if err != nil {
        log.Fatalf("连接到远程服务器时出错: %v", err)
    }
    defer serverConn.Close()

    // 启动keepalive协程
    go func() {
        ticker := time.NewTicker(30 * time.Second)
        defer ticker.Stop()
        
        for {
            select {
            case <-ticker.C:
                // 发送全局请求保持连接活跃
                _, _, err := serverConn.SendRequest("keepalive@openssh.com", true, nil)
                if err != nil {
                    log.Printf("发送keepalive失败: %v", err)
                    return
                }
                fmt.Println("发送keepalive请求")
            }
        }
    }()

    listener, err := serverConn.Listen("tcp", remoteEndpoint.String())
    if err != nil {
        log.Fatalf("在远程服务器上打开端口监听时出错: %v", err)
    }
    defer listener.Close()

    for {
        local, err := net.Dial("tcp", localEndpoint.String())
        if err != nil {
            log.Printf("连接到本地服务时出错: %v", err)
            continue
        }

        client, err := listener.Accept()
        if err != nil {
            log.Printf("接受连接时出错: %v", err)
            local.Close()
            continue
        }

        go handleClient(client, local)
    }
}

func handleClient(remote net.Conn, local net.Conn) {
    defer remote.Close()
    defer local.Close()
    
    // 实现双向数据转发
    done := make(chan struct{}, 2)
    
    go func() {
        io.Copy(remote, local)
        done <- struct{}{}
    }()
    
    go func() {
        io.Copy(local, remote)
        done <- struct{}{}
    }()
    
    <-done
}

func publicKeyFile(file string) ssh.AuthMethod {
    buffer, err := ioutil.ReadFile(file)
    if err != nil {
        return nil
    }
    
    key, err := ssh.ParsePrivateKey(buffer)
    if err != nil {
        return nil
    }
    
    return ssh.PublicKeys(key)
}

关键修改点:

  1. ssh.ClientConfig中添加ClientVersion: "SSH-2.0-Go-keepalive"以启用keepalive支持
  2. 使用单独的goroutine定期发送keepalive请求,间隔设置为30秒
  3. 使用正确的keepalive请求名称"keepalive@openssh.com"
  4. 为每个连接单独处理,避免阻塞主循环
  5. 添加了连接关闭的defer语句确保资源释放

这个实现会每30秒发送一次keepalive请求,与OpenSSH的ServerAliveInterval功能类似,可以有效防止连接因不活动而被关闭。

回到顶部