Golang:通过WebSocket转发SSH

4

我能够使用Golang将Telnet转发到WebSocket,类似于以下内容:

func forwardtcp(wsconn *websocket.Conn, conn *telnet.Conn) {
    connbuf := bufio.NewReader(conn)
    tcpbuffer := make([]byte, 128)

    for {
        n, err := connbuf.Read(tcpbuffer)

        if err != nil {
            log.Println("TCP Read failed")
            break
        }
        if err == nil {
            wsconn.WriteMessage(websocket.BinaryMessage, tcpbuffer[:n])
        }
    }
}

然而,我无法像使用Web浏览器那样在SSH或Shell会话中执行类似操作。我不明白如何使用此功能的基本概念。
targetStdout, _ := session.StdoutPipe()
targetStdin, _ := session.StdinPipe()

片段。

我能使用io.Copy,但不确定如何将它们格式化为可以通过websocket连接发送的数据报。

是否可以以一种方式处理targetStdin和targetStdout管道,使它们可以读取和写入字节,例如从websocket连接接收到的字节? 还是有更好的方法来获取SSH连接的io?


是的 - 适用于基于浏览器的客户端。 - Simon Knight
2个回答

1
如果你想使用SSH进行远程shell会话,你不应该使用websocket包,而应该使用golang.org/x/crypto/ssh包。在godoc.org上有一个很好的例子,我在这里重复一下:
// An SSH client is represented with a ClientConn. Currently only
// the "password" authentication method is supported.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig.
config := &ssh.ClientConfig{
    User: "username",
    Auth: []ssh.AuthMethod{
        ssh.Password("yourpassword"),
    },
}
client, err := ssh.Dial("tcp", "yourserver.com:22", config)
if err != nil {
    panic("Failed to dial: " + err.Error())
}

// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession()
if err != nil {
    panic("Failed to create session: " + err.Error())
}
defer session.Close()

// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
    panic("Failed to run: " + err.Error())
}
fmt.Println(b.String())

您可能想使用 ssh.Session 结构的字段,而不是使用 StdoutPipe()

type Session struct {
    Stdin io.Reader
    Stdout io.Writer
    Stderr io.Writer
}

在示例中,session.Stdout = &b 表示来自远程进程的标准输出将被写入 b。同样,您可以将任何io.Reader 分配给 session.Stdin,它将被远程进程读取为标准输入。有关ssh.Session的详细行为,请参见godoc.org

谢谢 - 不过我需要将它转发到远程基于浏览器的客户端 - 这就是我遇到问题的地方。 执行单个命令是一个好的开始,但理想情况下,我希望能够进行交互式会话的转发。 - Simon Knight
为了后代:GoTTY项目允许您在远程服务器上运行命令,并允许您从Web浏览器中查看终端会话。您可以使用命令行实用程序gotty-client而不是浏览器,因为它使用Websockets与GoTTY服务器通信。 - Gurjeet Singh

1

websocket.Conn 是一个 ReadWriter,因此它可以成为 io.Copy 的源和目的地。 Cmd.StdoutPipeSession.StdoutPipe 都是 io.Reader,而相应的 Stdin 版本则是 io.Writer。因此一切都应该顺利地结合在一起,您只需双向复制即可。

go io.Copy(targetStdin, wsconn)
io.Copy(wsconn, targetStdout)

我尝试使用Gorilla WebSocket实现,却出现了错误: 无法将wsConn(类型*websocket.Conn)用作io.Copy参数中的类型io.Reader: *websocket.Conn未实现io.Reader(缺少Read方法) 并且 无法将wsConn(类型*websocket.Conn)用作io.Copy参数中的类型io.Writer: *websocket.Conn未实现io.Writer(缺少Write方法) 有websocket.write(int,time.Time,... []byte)错误 要求Write([] byte)(int,error)我正在尝试使用NextReader()和NextWriter(),但目前没有成功。 - Simon Knight
你应该看一下 golang.org/x/net/websocket。但是也可以把 gorilla 版本封装在自己的类型中,然后添加 Read 和 Write 方法。 - Rob Napier
@SimonKnight 你使用 Gorilla WebSocket 库有什么进展了吗? - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接