From 46d32dc31eda566cc116e014dc3a6c47063ae3e7 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 16:40:30 +0100 Subject: [PATCH 01/12] Copied from Terrform PR #24611 by @htamakos --- http_proxy.go | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 http_proxy.go diff --git a/http_proxy.go b/http_proxy.go new file mode 100644 index 0000000..b29b427 --- /dev/null +++ b/http_proxy.go @@ -0,0 +1,115 @@ +package easyssh + +import ( + "bufio" + "fmt" + "golang.org/x/net/proxy" + "net" + "net/http" + "net/url" +) + +type Direct struct{} + +func (Direct) Dial(network, addr string) (net.Conn, error) { + return net.Dial(network, addr) +} + +type connectProxyDialer struct { + host string + forward proxy.Dialer + auth bool + username string + password string +} + +func NewConnectProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) { + host := u.Host + p := &connectProxyDialer{ + host: host, + forward: forward, + } + + if u.User != nil { + p.auth = true + p.username = u.User.Username() + p.password, _ = u.User.Password() + } + + return p, nil +} + +func RegisterDialerType() { + proxy.RegisterDialerType("http", NewConnectProxyDialer) + proxy.RegisterDialerType("https", NewConnectProxyDialer) +} + +func NewHttpProxyConn(d Direct, proxyAddr, targetAddr string) (net.Conn, error) { + proxyURL, err := url.Parse("http://" + proxyAddr) + if err != nil { + return nil, err + } + + proxyDialer, err := proxy.FromURL(proxyURL, d) + + if err != nil { + return nil, err + } + + proxyConn, err := proxyDialer.Dial("tcp", targetAddr) + + if err != nil { + return nil, err + } + + return proxyConn, err +} + +func (p *connectProxyDialer) Dial(network, addr string) (net.Conn, error) { + c, err := p.forward.Dial("tcp", p.host) + + if err != nil { + return nil, err + } + + reqUrl, err := url.Parse("http://" + addr) + if err != nil { + c.Close() + return nil, err + } + + req, err := http.NewRequest("CONNECT", reqUrl.String(), nil) + if err != nil { + c.Close() + return nil, err + } + + if p.auth { + req.SetBasicAuth(p.username, p.password) + } + + req.Close = false + + err = req.Write(c) + if err != nil { + c.Close() + return nil, err + } + + res, err := http.ReadResponse(bufio.NewReader(c), req) + + if err != nil { + res.Body.Close() + c.Close() + return nil, err + } + + res.Body.Close() + + if res.StatusCode != http.StatusOK { + c.Close() + return nil, fmt.Errorf("Connection Error: StatusCode: %d", res.StatusCode) + } + + return c, nil +} From 46a9684ecf92d4c541652912315191b6519c149b Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 16:47:26 +0100 Subject: [PATCH 02/12] Add support for http proxy when using bastion --- easyssh.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- go.mod | 1 + go.sum | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/easyssh.go b/easyssh.go index 0cc0644..7af6b32 100644 --- a/easyssh.go +++ b/easyssh.go @@ -71,6 +71,12 @@ type ( KeyExchanges []string Fingerprint string + // http proxy support + ProxyHost string + ProxyPort string + ProxyUserName string + ProxyUserPassword string + // Enable the use of insecure ciphers and key exchange methods. // This enables the use of the the following insecure ciphers and key exchange methods: // - aes128-cbc @@ -203,6 +209,16 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { defer closer.Close() } + // HTTP proxy support + var proxyAddr string + if ssh_conf.Proxy.ProxyHost != "" && ssh_conf.Proxy.ProxyPort != "" { + proxyAddr = ssh_conf.Proxy.ProxyHost + ":" + ssh_conf.Proxy.ProxyPort + + if ssh_conf.Proxy.ProxyUserName != "" && ssh_conf.Proxy.ProxyUserPassword != "" { + proxyAddr = ssh_conf.Proxy.ProxyUserName + ":" + ssh_conf.Proxy.ProxyUserPassword + "@" + proxyAddr + } + } + // Enable proxy command if ssh_conf.Proxy.Server != "" { proxyConfig, closer := getSSHConfig(DefaultConfig{ @@ -220,8 +236,35 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { if closer != nil { defer closer.Close() } + var err error + var proxyClient *ssh.Client + var direct Direct + + if proxyAddr != "" { + var pConn net.Conn + var bConn ssh.Conn + var bChans <-chan ssh.NewChannel + var bReq <-chan *ssh.Request + + bAddr := net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port) + direct = Direct{} + + RegisterDialerType() + pConn, err = NewHttpProxyConn(direct, proxyAddr, bAddr) - proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig) + if err != nil { + return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) + } + + bConn, bChans, bReq, err = ssh.NewClientConn(pConn, bAddr, proxyConfig) + + if err != nil { + return nil, nil, fmt.Errorf("Error creating new client connection via proxy: %s", err) + } + proxyClient = ssh.NewClient(bConn, bChans, bReq) + } else { + proxyClient, err = ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig) + } if err != nil { return nil, nil, err } diff --git a/go.mod b/go.mod index 4e63da7..dea9b61 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,5 @@ require ( github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 github.com/stretchr/testify v1.6.1 golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 ) diff --git a/go.sum b/go.sum index 6146206..8304160 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From a981d354f9a3da2b4343bcb89e4f945dc64942c3 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 16:48:06 +0100 Subject: [PATCH 03/12] Add .idea/ --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index db171bc..90b1753 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ _testmain.go *.prof coverage.txt + +# Editors +.idea/ From 307e13f4edfca3b1b510d0147128701a19c8b4a1 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 22:27:08 +0100 Subject: [PATCH 04/12] Support HTTP Proxy without Bastion as well Signed-off-by: Andy Lo-A-Foe --- easyssh.go | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/easyssh.go b/easyssh.go index 7af6b32..1f92401 100644 --- a/easyssh.go +++ b/easyssh.go @@ -45,6 +45,12 @@ type ( KeyExchanges []string Fingerprint string + // http proxy support + ProxyHost string + ProxyPort string + ProxyUserName string + ProxyUserPassword string + // Enable the use of insecure ciphers and key exchange methods. // This enables the use of the the following insecure ciphers and key exchange methods: // - aes128-cbc @@ -71,12 +77,6 @@ type ( KeyExchanges []string Fingerprint string - // http proxy support - ProxyHost string - ProxyPort string - ProxyUserName string - ProxyUserPassword string - // Enable the use of insecure ciphers and key exchange methods. // This enables the use of the the following insecure ciphers and key exchange methods: // - aes128-cbc @@ -211,15 +211,15 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { // HTTP proxy support var proxyAddr string - if ssh_conf.Proxy.ProxyHost != "" && ssh_conf.Proxy.ProxyPort != "" { - proxyAddr = ssh_conf.Proxy.ProxyHost + ":" + ssh_conf.Proxy.ProxyPort + if ssh_conf.ProxyHost != "" && ssh_conf.ProxyPort != "" { + proxyAddr = ssh_conf.ProxyHost + ":" + ssh_conf.ProxyPort - if ssh_conf.Proxy.ProxyUserName != "" && ssh_conf.Proxy.ProxyUserPassword != "" { - proxyAddr = ssh_conf.Proxy.ProxyUserName + ":" + ssh_conf.Proxy.ProxyUserPassword + "@" + proxyAddr + if ssh_conf.ProxyUserName != "" && ssh_conf.ProxyUserPassword != "" { + proxyAddr = ssh_conf.ProxyUserName + ":" + ssh_conf.ProxyUserPassword + "@" + proxyAddr } } - // Enable proxy command + // Use bastion server if ssh_conf.Proxy.Server != "" { proxyConfig, closer := getSSHConfig(DefaultConfig{ User: ssh_conf.Proxy.User, @@ -259,7 +259,7 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { bConn, bChans, bReq, err = ssh.NewClientConn(pConn, bAddr, proxyConfig) if err != nil { - return nil, nil, fmt.Errorf("Error creating new client connection via proxy: %s", err) + return nil, nil, fmt.Errorf("Error creating new client connection via proxy bastion: %s", err) } proxyClient = ssh.NewClient(bConn, bChans, bReq) } else { @@ -281,7 +281,31 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { client = ssh.NewClient(ncc, chans, reqs) } else { - client, err = ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig) + if proxyAddr != "" { + var pConn net.Conn + var bConn ssh.Conn + var bChans <-chan ssh.NewChannel + var bReq <-chan *ssh.Request + + bAddr := net.JoinHostPort(ssh_conf.Server, ssh_conf.Port) + direct := Direct{} + + RegisterDialerType() + pConn, err = NewHttpProxyConn(direct, proxyAddr, bAddr) + + if err != nil { + return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) + } + + bConn, bChans, bReq, err = ssh.NewClientConn(pConn, bAddr, targetConfig) + + if err != nil { + return nil, nil, fmt.Errorf("Error creating new client connection via proxy: %s", err) + } + client = ssh.NewClient(bConn, bChans, bReq) + } else { + client, err = ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig) + } if err != nil { return nil, nil, err } From 4217c9f3340c1e429986b4c3373ad0e8e5f118ee Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 22:30:04 +0100 Subject: [PATCH 05/12] Add HTTP proxy example --- example/http_proxy/http_proxy.go | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 example/http_proxy/http_proxy.go diff --git a/example/http_proxy/http_proxy.go b/example/http_proxy/http_proxy.go new file mode 100644 index 0000000..fc75480 --- /dev/null +++ b/example/http_proxy/http_proxy.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + "github.com/appleboy/easyssh-proxy" +) + +func main() { + // Create MakeConfig instance with remote username, server address and path to private key. + // Use a HTTP proxy listening on 127.0.0.1:8888 to connect to Proxy/Bastion + ssh := &easyssh.MakeConfig{ + User: "drone-scp", + Server: "localhost", + Port: "22", + KeyPath: "./tests/.ssh/id_rsa", + ProxyHost: "127.0.0.1", + ProxyPort: "8888", + Proxy: easyssh.DefaultConfig{ + User: "drone-scp", + Server: "localhost", + Port: "22", + KeyPath: "./tests/.ssh/id_rsa", + }, + } + + // Call Scp method with file you want to upload to remote server. + // Please make sure the `tmp` floder exists. + err := ssh.Scp("/root/source.csv", "/tmp/target.csv") + + // Handle errors + if err != nil { + panic("Can't run remote command: " + err.Error()) + } else { + fmt.Println("success") + } +} From a2ed9ebf2e3d8428f2c13bd0ef286f8e5dbee119 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 22:51:36 +0100 Subject: [PATCH 06/12] Linting fixes --- easyssh.go | 14 +++++++------- http_proxy.go | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/easyssh.go b/easyssh.go index 1f92401..c8da924 100644 --- a/easyssh.go +++ b/easyssh.go @@ -238,7 +238,7 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { } var err error var proxyClient *ssh.Client - var direct Direct + var direct directDialer if proxyAddr != "" { var pConn net.Conn @@ -247,10 +247,10 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { var bReq <-chan *ssh.Request bAddr := net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port) - direct = Direct{} + direct = directDialer{} - RegisterDialerType() - pConn, err = NewHttpProxyConn(direct, proxyAddr, bAddr) + registerDialerType() + pConn, err = newHttpProxyConn(direct, proxyAddr, bAddr) if err != nil { return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) @@ -288,10 +288,10 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { var bReq <-chan *ssh.Request bAddr := net.JoinHostPort(ssh_conf.Server, ssh_conf.Port) - direct := Direct{} + direct := directDialer{} - RegisterDialerType() - pConn, err = NewHttpProxyConn(direct, proxyAddr, bAddr) + registerDialerType() + pConn, err = newHttpProxyConn(direct, proxyAddr, bAddr) if err != nil { return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) diff --git a/http_proxy.go b/http_proxy.go index b29b427..a626bec 100644 --- a/http_proxy.go +++ b/http_proxy.go @@ -9,9 +9,9 @@ import ( "net/url" ) -type Direct struct{} +type directDialer struct{} -func (Direct) Dial(network, addr string) (net.Conn, error) { +func (directDialer) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } @@ -23,7 +23,7 @@ type connectProxyDialer struct { password string } -func NewConnectProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) { +func newConnectProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) { host := u.Host p := &connectProxyDialer{ host: host, @@ -39,12 +39,12 @@ func NewConnectProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, erro return p, nil } -func RegisterDialerType() { - proxy.RegisterDialerType("http", NewConnectProxyDialer) - proxy.RegisterDialerType("https", NewConnectProxyDialer) +func registerDialerType() { + proxy.RegisterDialerType("http", newConnectProxyDialer) + proxy.RegisterDialerType("https", newConnectProxyDialer) } -func NewHttpProxyConn(d Direct, proxyAddr, targetAddr string) (net.Conn, error) { +func newHttpProxyConn(d directDialer, proxyAddr, targetAddr string) (net.Conn, error) { proxyURL, err := url.Parse("http://" + proxyAddr) if err != nil { return nil, err @@ -65,22 +65,22 @@ func NewHttpProxyConn(d Direct, proxyAddr, targetAddr string) (net.Conn, error) return proxyConn, err } -func (p *connectProxyDialer) Dial(network, addr string) (net.Conn, error) { +func (p *connectProxyDialer) Dial(_, addr string) (net.Conn, error) { c, err := p.forward.Dial("tcp", p.host) if err != nil { return nil, err } - reqUrl, err := url.Parse("http://" + addr) + reqURL, err := url.Parse("http://" + addr) if err != nil { - c.Close() + _ = c.Close() return nil, err } - req, err := http.NewRequest("CONNECT", reqUrl.String(), nil) + req, err := http.NewRequest("CONNECT", reqURL.String(), nil) if err != nil { - c.Close() + _ = c.Close() return nil, err } @@ -92,7 +92,7 @@ func (p *connectProxyDialer) Dial(network, addr string) (net.Conn, error) { err = req.Write(c) if err != nil { - c.Close() + _ = c.Close() return nil, err } @@ -100,14 +100,14 @@ func (p *connectProxyDialer) Dial(network, addr string) (net.Conn, error) { if err != nil { res.Body.Close() - c.Close() + _ = c.Close() return nil, err } res.Body.Close() if res.StatusCode != http.StatusOK { - c.Close() + _ = c.Close() return nil, fmt.Errorf("Connection Error: StatusCode: %d", res.StatusCode) } From e82d883b2b3781f37634d429030fb13d623d521b Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 22:53:09 +0100 Subject: [PATCH 07/12] More linting fixes --- easyssh.go | 4 ++-- http_proxy.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/easyssh.go b/easyssh.go index c8da924..5257768 100644 --- a/easyssh.go +++ b/easyssh.go @@ -250,7 +250,7 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { direct = directDialer{} registerDialerType() - pConn, err = newHttpProxyConn(direct, proxyAddr, bAddr) + pConn, err = newHTTPProxyConn(direct, proxyAddr, bAddr) if err != nil { return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) @@ -291,7 +291,7 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { direct := directDialer{} registerDialerType() - pConn, err = newHttpProxyConn(direct, proxyAddr, bAddr) + pConn, err = newHTTPProxyConn(direct, proxyAddr, bAddr) if err != nil { return nil, nil, fmt.Errorf("Error connecting to proxy: %s", err) diff --git a/http_proxy.go b/http_proxy.go index a626bec..9c75a97 100644 --- a/http_proxy.go +++ b/http_proxy.go @@ -44,7 +44,7 @@ func registerDialerType() { proxy.RegisterDialerType("https", newConnectProxyDialer) } -func newHttpProxyConn(d directDialer, proxyAddr, targetAddr string) (net.Conn, error) { +func newHTTPProxyConn(d directDialer, proxyAddr, targetAddr string) (net.Conn, error) { proxyURL, err := url.Parse("http://" + proxyAddr) if err != nil { return nil, err From a4d6f9185b90719f7eca62aaa751a3469e13a5e0 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 22:55:51 +0100 Subject: [PATCH 08/12] Formatting fixes --- go.mod | 5 ++++- go.sum | 18 +++++++++++++++--- http_proxy.go | 7 ++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index dea9b61..4b7332f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,10 @@ go 1.15 require ( github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 + github.com/kr/pretty v0.1.0 // indirect github.com/stretchr/testify v1.6.1 golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 - golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + golang.org/x/net v0.0.0-20201021035429-f5854403a974 + golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/go.sum b/go.sum index 8304160..12cc946 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -12,20 +17,27 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http_proxy.go b/http_proxy.go index 9c75a97..ec6365d 100644 --- a/http_proxy.go +++ b/http_proxy.go @@ -3,10 +3,11 @@ package easyssh import ( "bufio" "fmt" - "golang.org/x/net/proxy" "net" "net/http" "net/url" + + "golang.org/x/net/proxy" ) type directDialer struct{} @@ -51,13 +52,11 @@ func newHTTPProxyConn(d directDialer, proxyAddr, targetAddr string) (net.Conn, e } proxyDialer, err := proxy.FromURL(proxyURL, d) - if err != nil { return nil, err } proxyConn, err := proxyDialer.Dial("tcp", targetAddr) - if err != nil { return nil, err } @@ -67,7 +66,6 @@ func newHTTPProxyConn(d directDialer, proxyAddr, targetAddr string) (net.Conn, e func (p *connectProxyDialer) Dial(_, addr string) (net.Conn, error) { c, err := p.forward.Dial("tcp", p.host) - if err != nil { return nil, err } @@ -97,7 +95,6 @@ func (p *connectProxyDialer) Dial(_, addr string) (net.Conn, error) { } res, err := http.ReadResponse(bufio.NewReader(c), req) - if err != nil { res.Body.Close() _ = c.Close() From 19b0bc873c46f7057d6d0eab5d55d56b448dcc4c Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sat, 23 Jan 2021 23:03:09 +0100 Subject: [PATCH 09/12] Update documentation --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 15ccb1c..7ac02be 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This project is forked from [easyssh](https://github.com/hypersleep/easyssh) but * [x] Support key path of user private key. * [x] Support Timeout for the TCP connection to establish. * [x] Support SSH ProxyCommand. +* [x] Support HTTP Proxy traversal. ```bash +--------+ +----------+ +-----------+ @@ -28,6 +29,15 @@ This project is forked from [easyssh](https://github.com/hypersleep/easyssh) but | Laptop | <--> | Firewall | <--> | FooServer | +--------+ +----------+ +-----------+ 192.168.1.5 121.1.2.3 10.10.29.68 + + + OR + + +--------+ +-----------------+ +----------+ +-----------+ + | Laptop | <--> | Corporate Proxy | <--> | Jumphost | <--> | FooServer | + +--------+ +-----------------+ +----------+ +-----------+ + 192.168.1.5 192.168.1.1:8080 121.1.2.3 10.10.29.68 + ``` ## Usage @@ -55,7 +65,7 @@ func main() { User: "appleboy", Server: "example.com", // Optional key or Password without either we try to contact your agent SOCKET - //Password: "password", + // Password: "password", // Paste your source content of private key // Key: `-----BEGIN RSA PRIVATE KEY----- // MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26 @@ -71,7 +81,7 @@ func main() { // Optional fingerprint SHA256 verification // Get Fingerprint: ssh.FingerprintSHA256(key) - //Fingerprint: "SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE" + // Fingerprint: "SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE" // Enable the use of insecure ciphers and key exchange methods. // This enables the use of the the following insecure ciphers and key exchange methods: @@ -93,7 +103,6 @@ func main() { } else { fmt.Println("don is :", done, "stdout is :", stdout, "; stderr is :", stderr) } - } ``` From f6edb9f593cd36feeb0faef7f040a3cd37ac4d4a Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sun, 24 Jan 2021 12:11:05 +0100 Subject: [PATCH 10/12] Allow us to use http.ProxyFromEnvironment --- easyssh.go | 77 +++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/easyssh.go b/easyssh.go index 5257768..25fa781 100644 --- a/easyssh.go +++ b/easyssh.go @@ -11,6 +11,8 @@ import ( "io/ioutil" "log" "net" + "net/http" + "net/url" "os" "path/filepath" "sync" @@ -32,36 +34,33 @@ type ( // Note: easyssh looking for private key in user's home directory (ex. /home/john + Key). // Then ensure your Key begins from '/' (ex. /.ssh/id_rsa) MakeConfig struct { - User string - Server string - Key string - KeyPath string - Port string - Passphrase string - Password string - Timeout time.Duration - Proxy DefaultConfig - Ciphers []string - KeyExchanges []string - Fingerprint string - - // http proxy support - ProxyHost string - ProxyPort string - ProxyUserName string - ProxyUserPassword string - - // Enable the use of insecure ciphers and key exchange methods. - // This enables the use of the the following insecure ciphers and key exchange methods: - // - aes128-cbc - // - aes192-cbc - // - aes256-cbc - // - 3des-cbc - // - diffie-hellman-group-exchange-sha256 - // - diffie-hellman-group-exchange-sha1 - // Those algorithms are insecure and may allow plaintext data to be recovered by an attacker. - UseInsecureCipher bool - } + User string + Server string + Key string + KeyPath string + Port string + Passphrase string + Password string + Timeout time.Duration + Proxy DefaultConfig + Ciphers []string + KeyExchanges []string + Fingerprint string + + // HTTP Proxy support + ProxyInfo func(req *http.Request) (*url.URL, error) + + // Enable the use of insecure ciphers and key exchange methods. + // This enables the use of the the following insecure ciphers and key exchange methods: + // - aes128-cbc + // - aes192-cbc + // - aes256-cbc + // - 3des-cbc + // - diffie-hellman-group-exchange-sha256 + // - diffie-hellman-group-exchange-sha1 + // Those algorithms are insecure and may allow plaintext data to be recovered by an attacker. + UseInsecureCipher bool +} // DefaultConfig for ssh proxy config DefaultConfig struct { @@ -211,11 +210,19 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) { // HTTP proxy support var proxyAddr string - if ssh_conf.ProxyHost != "" && ssh_conf.ProxyPort != "" { - proxyAddr = ssh_conf.ProxyHost + ":" + ssh_conf.ProxyPort - - if ssh_conf.ProxyUserName != "" && ssh_conf.ProxyUserPassword != "" { - proxyAddr = ssh_conf.ProxyUserName + ":" + ssh_conf.ProxyUserPassword + "@" + proxyAddr + if ssh_conf.ProxyInfo != nil { + req, _ := http.NewRequest("CONNECT", "https://"+ssh_conf.Server, nil) + proxyInfo, err := ssh_conf.ProxyInfo(req) + if proxyInfo == nil { // Try http:// as well + req, _ = http.NewRequest("CONNECT", "http://"+ssh_conf.Server, nil) + proxyInfo, err = ssh_conf.ProxyInfo(req) + } + if err == nil && proxyInfo != nil { + proxyAddr = proxyInfo.Host + if proxyInfo.User != nil { + password, _ := proxyInfo.User.Password() + proxyAddr = proxyInfo.User.Username() + ":" + password + "@" + proxyAddr + } } } From 1a2f7c301aa149f81406626397444b8a40368ba7 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sun, 24 Jan 2021 12:12:19 +0100 Subject: [PATCH 11/12] Update example --- example/http_proxy/http_proxy.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/example/http_proxy/http_proxy.go b/example/http_proxy/http_proxy.go index fc75480..0fcb584 100644 --- a/example/http_proxy/http_proxy.go +++ b/example/http_proxy/http_proxy.go @@ -2,8 +2,8 @@ package main import ( "fmt" - "github.com/appleboy/easyssh-proxy" + "net/http" ) func main() { @@ -14,8 +14,7 @@ func main() { Server: "localhost", Port: "22", KeyPath: "./tests/.ssh/id_rsa", - ProxyHost: "127.0.0.1", - ProxyPort: "8888", + ProxyInfo: http.ProxyFromEnvironment, Proxy: easyssh.DefaultConfig{ User: "drone-scp", Server: "localhost", From e3892930f3bea5b1762c67b43021ec0e2852083f Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Sun, 24 Jan 2021 12:14:27 +0100 Subject: [PATCH 12/12] Fix formatting --- easyssh.go | 54 ++++++++++++++++---------------- example/http_proxy/http_proxy.go | 3 +- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/easyssh.go b/easyssh.go index 25fa781..01f2e45 100644 --- a/easyssh.go +++ b/easyssh.go @@ -34,33 +34,33 @@ type ( // Note: easyssh looking for private key in user's home directory (ex. /home/john + Key). // Then ensure your Key begins from '/' (ex. /.ssh/id_rsa) MakeConfig struct { - User string - Server string - Key string - KeyPath string - Port string - Passphrase string - Password string - Timeout time.Duration - Proxy DefaultConfig - Ciphers []string - KeyExchanges []string - Fingerprint string - - // HTTP Proxy support - ProxyInfo func(req *http.Request) (*url.URL, error) - - // Enable the use of insecure ciphers and key exchange methods. - // This enables the use of the the following insecure ciphers and key exchange methods: - // - aes128-cbc - // - aes192-cbc - // - aes256-cbc - // - 3des-cbc - // - diffie-hellman-group-exchange-sha256 - // - diffie-hellman-group-exchange-sha1 - // Those algorithms are insecure and may allow plaintext data to be recovered by an attacker. - UseInsecureCipher bool -} + User string + Server string + Key string + KeyPath string + Port string + Passphrase string + Password string + Timeout time.Duration + Proxy DefaultConfig + Ciphers []string + KeyExchanges []string + Fingerprint string + + // HTTP Proxy support + ProxyInfo func(req *http.Request) (*url.URL, error) + + // Enable the use of insecure ciphers and key exchange methods. + // This enables the use of the the following insecure ciphers and key exchange methods: + // - aes128-cbc + // - aes192-cbc + // - aes256-cbc + // - 3des-cbc + // - diffie-hellman-group-exchange-sha256 + // - diffie-hellman-group-exchange-sha1 + // Those algorithms are insecure and may allow plaintext data to be recovered by an attacker. + UseInsecureCipher bool + } // DefaultConfig for ssh proxy config DefaultConfig struct { diff --git a/example/http_proxy/http_proxy.go b/example/http_proxy/http_proxy.go index 0fcb584..72ef8f2 100644 --- a/example/http_proxy/http_proxy.go +++ b/example/http_proxy/http_proxy.go @@ -2,8 +2,9 @@ package main import ( "fmt" - "github.com/appleboy/easyssh-proxy" "net/http" + + "github.com/appleboy/easyssh-proxy" ) func main() {