英文:
Golang: forward ssh over websocket
问题
我能够使用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])
}
}
}
然而,我无法使用SSH或shell会话做类似的操作。我对使用以下代码中的targetStdout, _ := session.StdoutPipe()
和targetStdin, _ := session.StdinPipe()
这两个部分的基本概念不太理解。
我可以使用io.Copy
,但不确定如何将它们格式化为可以通过websocket连接发送的数据包。
是否可以以一种方式处理targetStdin
和targetStdout
管道,使其可以读取和写入字节,例如从websocket连接接收到的字节?
或者是否有更好的方法来获取SSH连接的io?
英文:
I've been able to forward telnet over a websocket using golang, using something like
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])
}
}
}
However I'm unable to do similar with an SSH or shell session. I'm not understanding a fundamental concept with the using the
targetStdout, _ := session.StdoutPipe()
targetStdin, _ := session.StdinPipe()
pieces.
I am able to use io.Copy, but not sure how to format these into a datagram that can be sent with the websocket connection.
Is it possible to treat the targetStdin and targetStdout pipes in a manner that they can be read and written to with bytes, such as those received from the websocket connection?
Or is there a better approach to get io from the SSH connection?
答案1
得分: 1
如果您想要使用SSH进行远程shell会话,您应该使用golang.org/x/crypto/ssh包,而不是websocket包。在godoc.org上有一个很好的示例,我在这里重复一下:
// SSH客户端通过ClientConn表示。目前只支持“password”身份验证方法。
//
// 要使用远程服务器进行身份验证,您必须通过ClientConfig的Auth字段传递至少一个AuthMethod的实现。
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())
}
// 每个ClientConn可以支持多个交互式会话,由Session表示。
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
// 创建Session后,您可以使用Run方法在远程端执行单个命令。
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。
英文:
If you want to use SSH for a remote shell session you should not use the websocket package but the golang.org/x/crypto/ssh package. There is an excellent example at godoc.org which I repeat here:
// 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())
You probably want to use the fields of the ssh.Session
struct instead of using StdoutPipe()
.
type Session struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
The line in the example session.Stdout = &b
means that the stdout from the remote process will be written to b
. Likewise, you can assign any io.Reader
to session.Stdin
which will be read as stdin by the remote process. The detailed behavior of ssh.Session
can be found at godoc.org
答案2
得分: 1
一个websocket.Conn
是一个ReadWriter
,因此它可以作为io.Copy
的源和目标。Cmd.StdoutPipe
和Session.StdoutPipe
都是io.Reader
,而相应的Stdin
版本是io.Writer
。所以一切都应该很好地连接在一起。你只需要在两个方向上进行复制。
go io.Copy(targetStdin, wsconn)
io.Copy(wsconn, targetStdout)
英文:
A websocket.Conn
is a ReadWriter
, so it can be both the source and destination for io.Copy
. Both Cmd.StdoutPipe
and Session.StdoutPipe
are io.Reader
and the Stdin
versions are io.Writer
. So everything should glue together just fine. You just have to copy in both directions.
go io.Copy(targetStdin, wsconn)
io.Copy(wsconn, targetStdout)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论