├── .gitignore
├── CHANGELOG.md
├── dssh
├── dssh_test.go
└── dssh.go
├── go.mod
├── .example.config.yaml
├── LICENSE
├── go.sum
├── main.go
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | .git
3 | .svn
4 | .idea
5 | .vscode
6 | *.log
7 | git.sh
8 | gittag.sh
9 | gitinit.sh
10 | config.yaml
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v1.0.3 / 2021-06-26
2 |
3 | - 修复扩展包
4 |
5 | ## v1.0.2 / 2021-06-25
6 |
7 | - Golang SSH 隧道
8 | - 增加扩展包方式
--------------------------------------------------------------------------------
/dssh/dssh_test.go:
--------------------------------------------------------------------------------
1 | package dssh
2 |
3 | import "testing"
4 |
5 | func TestName(t *testing.T) {
6 | Tunnel("root", "", ":22", ":3306", "localhost:13306")
7 | }
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/dtapps/go-ssh-tunnel
2 |
3 | go 1.16
4 |
5 | require golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
6 |
7 | require gopkg.in/yaml.v2 v2.4.0
8 |
--------------------------------------------------------------------------------
/.example.config.yaml:
--------------------------------------------------------------------------------
1 | # 服务器
2 | username: "root" #服务器用户名
3 | password: "" #服务器密码
4 | serverAddr: "" #服务器地址
5 | serverPort: "22" #服务器端口
6 | remoteAddr: "" #远程地址
7 | remotePort: "3306" #远程端口
8 | # 本地
9 | localAddr: "localhost" #地址
10 | localPort: "13306" #端口
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 李光春
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
2 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
3 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
4 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
6 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
8 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
9 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
10 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
13 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
14 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
15 |
--------------------------------------------------------------------------------
/dssh/dssh.go:
--------------------------------------------------------------------------------
1 | package dssh
2 |
3 | import (
4 | "fmt"
5 | "golang.org/x/crypto/ssh"
6 | "io"
7 | "net"
8 | "time"
9 | )
10 |
11 | // 转发
12 | func sForward(serverAddr string, remoteAddr string, localConn net.Conn, config *ssh.ClientConfig) {
13 | // 设置sshClientConn
14 | sshClientConn, err := ssh.Dial("tcp", serverAddr, config)
15 | if err != nil {
16 | fmt.Printf("ssh.Dial failed: %s", err)
17 | }
18 |
19 | // 设置Connection
20 | sshConn, err := sshClientConn.Dial("tcp", remoteAddr)
21 |
22 | // 将localConn.Reader复制到sshConn.Writer
23 | go func() {
24 | _, err = io.Copy(sshConn, localConn)
25 | if err != nil {
26 | fmt.Printf("io.Copy failed: %v", err)
27 | }
28 | }()
29 |
30 | // 将sshConn.Reader复制到localConn.Writer
31 | go func() {
32 | _, err = io.Copy(localConn, sshConn)
33 | if err != nil {
34 | fmt.Printf("io.Copy failed: %v", err)
35 | }
36 | }()
37 | }
38 | func Tunnel(username string, password string, serverAddr string, remoteAddr string, localAddr string) {
39 | // 设置SSH配置
40 | fmt.Printf("%s,服务器:%s;远程:%s;本地:%s\n", "设置SSH配置", serverAddr, remoteAddr, localAddr)
41 | config := &ssh.ClientConfig{
42 | User: username,
43 | Auth: []ssh.AuthMethod{
44 | ssh.Password(password),
45 | },
46 | Timeout: 30 * time.Second,
47 | HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
48 | return nil
49 | },
50 | }
51 |
52 | // 设置本地监听器
53 | localListener, err := net.Listen("tcp", localAddr)
54 | if err != nil {
55 | fmt.Printf("net.Listen failed: %v\n", err)
56 | }
57 |
58 | for {
59 | // 设置本地
60 | localConn, err := localListener.Accept()
61 | if err != nil {
62 | fmt.Printf("localListener.Accept failed: %v\n", err)
63 | }
64 | go sForward(serverAddr, remoteAddr, localConn, config)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "golang.org/x/crypto/ssh"
6 | "gopkg.in/yaml.v2"
7 | "io"
8 | "io/ioutil"
9 | "log"
10 | "net"
11 | "time"
12 | )
13 |
14 | // GlobConfig 定义一个 config结构体变量
15 | var GlobConfig Config
16 |
17 | // Config 声明 配置结构体
18 | type Config struct {
19 | Username string `yaml:"username"`
20 | Password string `yaml:"password"`
21 | ServerAddr string `yaml:"serverAddr"`
22 | ServerPort string `yaml:"serverPort"`
23 | RemoteAddr string `yaml:"remoteAddr"`
24 | RemotePort string `yaml:"remotePort"`
25 | LocalAddr string `yaml:"localAddr"`
26 | LocalPort string `yaml:"localPort"`
27 | }
28 |
29 | // 读取配置文件
30 | func (c *Config) getConfig() *Config {
31 | yamlFile, err := ioutil.ReadFile("config.yaml")
32 | if err != nil {
33 | log.Printf("yamlFile.Get err #%v ", err)
34 | }
35 | err = yaml.Unmarshal(yamlFile, c)
36 | if err != nil {
37 | log.Fatalf("Unmarshal: %v", err)
38 | }
39 | return c
40 | }
41 |
42 | // 初始化读取配置
43 | func init() {
44 | // 直接赋值给结构体
45 | GlobConfig.getConfig()
46 | }
47 |
48 | // 转发
49 | func forward(localConn net.Conn, config *ssh.ClientConfig) {
50 | // 设置sshClientConn
51 | sshClientConn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", GlobConfig.ServerAddr, GlobConfig.ServerPort), config)
52 | if err != nil {
53 | fmt.Printf("ssh.Dial failed: %s", err)
54 | }
55 |
56 | // 设置Connection
57 | sshConn, err := sshClientConn.Dial("tcp", fmt.Sprintf("%s:%s", GlobConfig.RemoteAddr, GlobConfig.RemotePort))
58 |
59 | // 将localConn.Reader复制到sshConn.Writer
60 | go func() {
61 | _, err = io.Copy(sshConn, localConn)
62 | if err != nil {
63 | fmt.Printf("io.Copy failed: %v", err)
64 | }
65 | }()
66 |
67 | // 将sshConn.Reader复制到localConn.Writer
68 | go func() {
69 | _, err = io.Copy(localConn, sshConn)
70 | if err != nil {
71 | fmt.Printf("io.Copy failed: %v", err)
72 | }
73 | }()
74 | }
75 |
76 | func main() {
77 | // 设置SSH配置
78 | fmt.Printf("%s,服务器:%s:%s;远程:%s:%s;本地:%s:%s\n", "设置SSH配置", GlobConfig.ServerAddr, GlobConfig.ServerPort, GlobConfig.RemoteAddr, GlobConfig.RemotePort, GlobConfig.LocalAddr, GlobConfig.LocalPort)
79 | config := &ssh.ClientConfig{
80 | User: GlobConfig.Username,
81 | Auth: []ssh.AuthMethod{
82 | ssh.Password(GlobConfig.Password),
83 | },
84 | Timeout: 30 * time.Second,
85 | HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
86 | return nil
87 | },
88 | }
89 |
90 | // 设置本地监听器
91 | localListener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", GlobConfig.LocalAddr, GlobConfig.LocalPort))
92 | if err != nil {
93 | fmt.Printf("net.Listen failed: %v\n", err)
94 | }
95 |
96 | for {
97 | // 设置本地
98 | localConn, err := localListener.Accept()
99 | if err != nil {
100 | fmt.Printf("localListener.Accept failed: %v\n", err)
101 | }
102 | go forward(localConn, config)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |