From 061b3da42eaa8e4ac33e687e740836e0fd981842 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Mon, 4 May 2020 13:53:59 +0800 Subject: [PATCH] ssh: support ssh forwarder --- README.md | 5 ++ conf.go | 4 ++ config/glider.conf.example | 5 ++ go.mod | 2 +- go.sum | 4 +- main.go | 1 + proxy/ssh/ssh.go | 113 +++++++++++++++++++++++++++++++++++++ 7 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 proxy/ssh/ssh.go diff --git a/README.md b/README.md index b76a52b..45a2080 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ we can set up local listeners as proxy servers, and forward requests to internet |mixed |√|√| | |http+socks5 server |ss |√|√|√|√|client & server |ssr | | |√| |client only +|ssh | | |√| |client only |trojan | | |√|√|client only |vmess | | |√| |client only |redir |√| | | |linux only @@ -173,6 +174,9 @@ Available methods for ss: SSR scheme: ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz +SSH scheme: + ssh://user[:pass]@host:port[?key=keypath] + VMess scheme: vmess://[security:]uuid@host:port?alterID=num @@ -377,3 +381,4 @@ glider -config CONFIGPATH -listen :8080 -verbose - [conflag](https://github.com/nadoo/conflag): command line and config file parse support - [ArchLinux](https://www.archlinux.org/packages/community/x86_64/glider): a great linux distribution with glider pre-built package +- [urlEncode](https://www.w3schools.com/tags/ref_urlencode.asp): you should encode special characters in your sechme. e.g: `@`->`%40` diff --git a/conf.go b/conf.go index 87914c3..71464a6 100644 --- a/conf.go +++ b/conf.go @@ -157,6 +157,10 @@ func usage() { fmt.Fprintf(w, " ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz\n") fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "SSH scheme:\n") + fmt.Fprintf(w, " ssh://user[:pass]@host:port[?key=keypath]\n") + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "VMess scheme:\n") fmt.Fprintf(w, " vmess://[security:]uuid@host:port?alterID=num\n") fmt.Fprintf(w, "\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index e84f0df..5d6131b 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -94,6 +94,11 @@ listen=socks5://:1080 # SSR proxy as forwarder # forward=ssr://method:pass@1.1.1.1:8443?protocol=auth_aes128_md5&protocol_param=xxx&obfs=tls1.2_ticket_auth&obfs_param=yyy +# ssh forwarder +# forward=ssh://user[:pass]@host:port[?key=keypath] +# forward=ssh://root:pass@host:port +# forward=ssh://root@host:port?key=/path/to/keyfile + # http proxy as forwarder # forward=http://1.1.1.1:8080 diff --git a/go.mod b/go.mod index 6c7ad4c..7e46b6a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/nadoo/glider go 1.14 require ( - github.com/klauspost/cpuid v1.2.3 // indirect + github.com/klauspost/cpuid v1.2.4 // indirect github.com/klauspost/reedsolomon v1.9.4 // indirect github.com/mzz2017/shadowsocksR v0.0.0-20200126130347-721f53a7b15a github.com/nadoo/conflag v0.2.3 diff --git a/go.sum b/go.sum index 502469b..b0d5eef 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 h1:fBHFH+Y/GPGFGo7LIrErQc github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0LoLRb6ShHiJl8e39dGlx6A4g/ujlow= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4= +github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY= github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/klauspost/reedsolomon v1.9.4 h1:FB9jDBGqUNyhUg4Gszz384ulFqVSc61Pdap+HRPgnSo= diff --git a/main.go b/main.go index 1e7739e..4debb27 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( _ "github.com/nadoo/glider/proxy/socks4" _ "github.com/nadoo/glider/proxy/socks5" _ "github.com/nadoo/glider/proxy/ss" + _ "github.com/nadoo/glider/proxy/ssh" _ "github.com/nadoo/glider/proxy/ssr" _ "github.com/nadoo/glider/proxy/tcptun" _ "github.com/nadoo/glider/proxy/tls" diff --git a/proxy/ssh/ssh.go b/proxy/ssh/ssh.go new file mode 100644 index 0000000..c2a806d --- /dev/null +++ b/proxy/ssh/ssh.go @@ -0,0 +1,113 @@ +package ssh + +import ( + "errors" + "io/ioutil" + "net" + "net/url" + "time" + + "golang.org/x/crypto/ssh" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +type SSH struct { + dialer proxy.Dialer + proxy proxy.Proxy + addr string + config *ssh.ClientConfig +} + +func init() { + proxy.RegisterDialer("ssh", NewSSHDialer) +} + +// NewSS returns a ssh proxy. +func NewSSH(s string, d proxy.Dialer, p proxy.Proxy) (*SSH, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + user := u.User.Username() + if user == "" { + user = "root" + } + + config := &ssh.ClientConfig{ + User: user, + Timeout: time.Second * 3, + HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, + } + + if pass, _ := u.User.Password(); pass != "" { + config.Auth = []ssh.AuthMethod{ssh.Password(pass)} + } + + if key := privateKeyFile(u.Query().Get("key")); key != nil { + config.Auth = append(config.Auth, key) + } + + ssh := &SSH{ + dialer: d, + proxy: p, + addr: u.Host, + config: config, + } + + return ssh, nil +} + +// NewSSHDialer returns a ssh proxy dialer. +func NewSSHDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { + return NewSSH(s, d, nil) +} + +// Addr returns forwarder's address. +func (s *SSH) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// Dial connects to the address addr on the network net via the proxy. +func (s *SSH) Dial(network, addr string) (net.Conn, error) { + c, err := s.dialer.Dial(network, s.addr) + if err != nil { + log.F("[ssh]: dial to %s error: %s", s.addr, err) + return nil, err + } + + sshc, ch, req, err := ssh.NewClientConn(c, s.addr, s.config) + if err != nil { + log.F("[ssh]: initial connection to %s error: %s", s.addr, err) + return nil, err + } + + return ssh.NewClient(sshc, ch, req).Dial(network, addr) +} + +// DialUDP connects to the given address via the proxy. +func (s *SSH) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { + return nil, nil, errors.New("ssh client does not support udp") +} + +func privateKeyFile(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) +}