Simple SSH port forward in Golang

huangapple go评论92阅读模式
英文:

Simple SSH port forward in Golang

问题

我正在尝试使用Go创建(并稍后关闭)一个简单的TCP端口转发SSH。我对Golang和静态类型语言都不太熟悉(之前使用Ruby)。

在终端中,我只需运行ssh -L 9000:localhost:9999 user@server.com就可以实现我需要的功能。我想用Go以编程的方式实现同样的功能。

我尝试使用这个示例作为起点,并使用这个最近的测试来尝试理解该怎么做,但现在我有一堆混乱的代码,似乎这实际上是一件非常简单的事情。

非常感谢任何帮助!:-)

英文:

I'm trying to create (and later close) a simple TCP port forward over SSH with Go. I'm new to Golang and statically typed languages. (Coming from Ruby.)

In a terminal I would simply run ssh -L 9000:localhost:9999 user@server.com and this accomplishes what I need. I want to do the same, programmatically with Go.

I have tried using this example as a starting point and this recent test to try to understand what to do, but now I have a pile of confusing jumbled code when it seems like this is actually a very simple thing to do.

Any help would be very much appreciated! Simple SSH port forward in Golang

答案1

得分: 22

我终于弄清楚如何做到这一点了,我从IRC频道的schmichael那里得到了一些提示。感谢大家!

编辑:稍作解释:
我遇到的主要问题是我没有意识到需要设置一个本地的net.Listener(而不仅仅是本地的net.Conn)来接收本地请求并在转发字节之前创建net.Conn
此外,存在端口转发和反向端口转发,我之前没有详细考虑到常规端口转发也会发送字节回来,所以我还没有实现将远程读取器复制到本地写入器,但这是非常必要的。
以下是试图概括此代码的要点:

  • 监听本地端口9000。
  • 当从本地端口9000尝试读取时(listener.Accept()),
  • 接受连接并返回本地的io.Readerio.Writer
  • 连接到远程服务器,并
  • 连接到远程端口9999,返回一个io.Readerio.Writer
  • 不断将本地的io.Reader字节复制到远程的io.Writer
  • 不断将远程的io.Reader字节复制到本地的io.Writer

以下是代码:

package main

// 从本地端口9000转发到远程端口9999

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

var (
	username         = "root"
	password         = "password"
	serverAddrString = "192.168.1.100:22"
	localAddrString  = "localhost:9000"
	remoteAddrString = "localhost:9999"
)

func forward(localConn net.Conn, config *ssh.ClientConfig) {
	// 设置sshClientConn(类型为*ssh.ClientConn)
	sshClientConn, err := ssh.Dial("tcp", serverAddrString, config)
	if err != nil {
		log.Fatalf("ssh.Dial失败:%s", err)
	}

	// 设置sshConn(类型为net.Conn)
	sshConn, err := sshClientConn.Dial("tcp", remoteAddrString)

	// 将localConn.Reader复制到sshConn.Writer
	go func() {
		_, err = io.Copy(sshConn, localConn)
		if err != nil {
			log.Fatalf("io.Copy失败:%v", err)
		}
	}()

	// 将sshConn.Reader复制到localConn.Writer
	go func() {
		_, err = io.Copy(localConn, sshConn)
		if err != nil {
			log.Fatalf("io.Copy失败:%v", err)
		}
	}()
}

func main() {
	// 设置SSH配置(类型为*ssh.ClientConfig)
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}

	// 设置localListener(类型为net.Listener)
	localListener, err := net.Listen("tcp", localAddrString)
	if err != nil {
		log.Fatalf("net.Listen失败:%v", err)
	}

	for {
		// 设置localConn(类型为net.Conn)
		localConn, err := localListener.Accept()
		if err != nil {
			log.Fatalf("listen.Accept失败:%v", err)
		}
		go forward(localConn, config)
	}
}
英文:

I finally figured out how to do this, I got hints from schmichael in an IRC channel. Thanks to all!

EDIT: A little explanation:
A big part of the problem I was having was that I did not realize a local net.Listener (not just a local net.Conn) needed setup to receive a local request and create the net.Conn before forwarding the bytes.
Also, there exist both port forwards and reverse port forwards and I hadn't previously thought in detail about the fact that a regular port forward also sends bytes back, so copying the remote reader to local writer was not something I had implemented, yet it's very much needed.
Here is an attempt to relate the essence of what this code does:

  • Listen on local port 9000.
  • Upon attempted read from local port 9000: (listener.Accept()),
  • Accept connection and return a local io.Reader and io.Writer and,
  • Connect to remote server and,
  • Connect to remote port 9999 returning a io.Reader and io.Writer.
  • Continually copy local io.Reader bytes to remote io.Writer,
  • Continually copy remote io.Reader bytes to local io.Writer.

Here is the code:

package main
// Forward from local port 9000 to remote port 9999
import (
"io"
"log"
"net"
"golang.org/x/crypto/ssh"
)
var (
username         = "root"
password         = "password"
serverAddrString = "192.168.1.100:22"
localAddrString  = "localhost:9000"
remoteAddrString = "localhost:9999"
)
func forward(localConn net.Conn, config *ssh.ClientConfig) {
// Setup sshClientConn (type *ssh.ClientConn)
sshClientConn, err := ssh.Dial("tcp", serverAddrString, config)
if err != nil {
log.Fatalf("ssh.Dial failed: %s", err)
}
// Setup sshConn (type net.Conn)
sshConn, err := sshClientConn.Dial("tcp", remoteAddrString)
// Copy localConn.Reader to sshConn.Writer
go func() {
_, err = io.Copy(sshConn, localConn)
if err != nil {
log.Fatalf("io.Copy failed: %v", err)
}
}()
// Copy sshConn.Reader to localConn.Writer
go func() {
_, err = io.Copy(localConn, sshConn)
if err != nil {
log.Fatalf("io.Copy failed: %v", err)
}
}()
}
func main() {
// Setup SSH config (type *ssh.ClientConfig)
config := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
// Setup localListener (type net.Listener)
localListener, err := net.Listen("tcp", localAddrString)
if err != nil {
log.Fatalf("net.Listen failed: %v", err)
}
for {
// Setup localConn (type net.Conn)
localConn, err := localListener.Accept()
if err != nil {
log.Fatalf("listen.Accept failed: %v", err)
}
go forward(localConn, config)
}
}

答案2

得分: 5

我已经使用了你(damick)的示例代码来构建一个小型开源工具:SSHTunnel
https://github.com/SommerEngineering/SSHTunnel

因此,该代码在GitHub上是免费提供给任何人使用的:请随意用于学习目的或其他任何用途 Simple SSH port forward in Golang 我已经提到了你的昵称,并链接到了这个问题。

最好的祝福,
Thorsten。

英文:

I have used your (damick) example code to build a tiny open source tool: SSHTunnel
https://github.com/SommerEngineering/SSHTunnel

Therefore, the code is freely available at GitHub for anyone: Please feel free to use it for learning purposes or for anything else Simple SSH port forward in Golang I have mentioned your nickname and also linked to this question.

Best regards,
Thorsten.

答案3

得分: 2

我已经完成了一个名为mallory的简单SSH端口转发工具。
它提供的是HTTP代理而不是SOCKS代理,与ssh -D非常相似。

核心代码与damick的答案相似。

  1. 创建ClientConfig
  2. 使用配置信息ssh.Dial到远程SSH服务器并返回Client
  3. 现在你可以使用Client.Dial转发任何你喜欢的东西。

    Dial从远程主机初始化到addr的连接。所得到的连接具有零的LocalAddr()和RemoteAddr()。

  4. 如果你想提供一个SOCKS代理服务器,请使用Client.Dial连接到远程服务器。
    1: https://github.com/justmao945/mallory
英文:

I'v finished a simple SSH port forward tool called mallory.
It provides HTTP proxy instead of SOCKS proxy, which is really similar to ssh -D.

The core code is similar to damick's answer.

  1. Create ClientConfig
  2. ssh.Dial to remote SSH server with the config and return Client
  3. Now you can use Client.Dial to forward anything you like.
    >Dial initiates a connection to the addr from the remote host. The resulting connection has a zero LocalAddr() and RemoteAddr().
  4. If you want to serve a SOCKS proxy server, use Client.Dial to connect to the remote server.
    1: https://github.com/justmao945/mallory

答案4

得分: 0

我写了一个工具,叫做gosshtool。使用这个工具,你可以轻松地在Go语言中创建一个简单的SSH TCP端口转发。你可以在这里找到这个工具:https://github.com/scottkiss/gosshtool

我使用这个工具实现了一个端口转发服务器示例项目:https://github.com/scottkiss/gooverssh

英文:

I Wrote a tool,Called gosshtool,with this tool,you can easy to create a simple TCP port forward over SSH with Go.https://github.com/scottkiss/gosshtool

I use this tool implemented a port forward server example project:
https://github.com/scottkiss/gooverssh

huangapple
  • 本文由 发表于 2014年1月28日 08:48:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/21417223.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定