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 回复
上次是我的转发测试,需要客户端发送保活请求。
更多关于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连接的保活机制,可以通过配置ClientConfig的ClientVersion字段并定期发送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)
}
关键修改点:
- 在
ssh.ClientConfig中添加ClientVersion: "SSH-2.0-Go-keepalive"以启用keepalive支持 - 使用单独的goroutine定期发送keepalive请求,间隔设置为30秒
- 使用正确的keepalive请求名称
"keepalive@openssh.com" - 为每个连接单独处理,避免阻塞主循环
- 添加了连接关闭的defer语句确保资源释放
这个实现会每30秒发送一次keepalive请求,与OpenSSH的ServerAliveInterval功能类似,可以有效防止连接因不活动而被关闭。

