55 "io/ioutil"
66 "net"
77 "os"
8+ "strings"
89
910 validation "github.com/go-ozzo/ozzo-validation"
1011 "golang.org/x/crypto/ssh"
@@ -59,21 +60,6 @@ func NewProxyService(
5960 }, nil
6061}
6162
62- func (proxy * proxyService ) handleConnections (channels <- chan ssh.NewChannel ) error {
63- var connector = ServiceConnector {
64- channels : channels ,
65- logger : proxy .logger ,
66- }
67-
68- backendCredentials , err := proxy .retrieveCredentials ()
69- defer internal .ZeroizeCredentials (backendCredentials )
70- if err != nil {
71- return fmt .Errorf ("failed on retrieve credentials: %s" , err )
72- }
73-
74- return connector .Connect (backendCredentials )
75- }
76-
7763// Start initiates the net.Listener to listen for incoming connections
7864// Listen accepts SSH connections and MITMs them using a ServiceConnector.
7965func (proxy * proxyService ) Start () error {
@@ -128,32 +114,72 @@ func (proxy *proxyService) Start() error {
128114 return
129115 }
130116
131- // https://godoc.org/golang.org/x/crypto/ssh#NewServerConn
132- conn , chans , reqs , err := ssh .NewServerConn (nConn , serverConfig )
133- if err != nil {
134- logger .Debugf ("Failed to handshake: %s" , err )
135- continue
136- }
137- logger .Debugf (
138- "New connection accepted for user %s from %s" ,
139- conn .User (),
140- conn .RemoteAddr (),
141- )
117+ tcpConn := nConn .(* net.TCPConn )
118+ logger .Debugf ("SSH Client connected. ClientIP=%v" , tcpConn .RemoteAddr ())
119+
142120
143- // The incoming Request channel must be serviced.
144121 go func () {
145- for req := range reqs {
146- logger .Debugf ("Global SSH request : %v" , req )
122+ backendCredentials , err := proxy .retrieveCredentials ()
123+ defer internal .ZeroizeCredentials (backendCredentials )
124+ if err != nil {
125+ logger .Errorf ("Failed on retrieve credentials: %s" , err )
126+ return
147127 }
148- }()
149128
150- go func () {
151- if err := proxy .handleConnections (chans ); err != nil {
152- logger .Errorf ("Failed on handle connection: %s" , err )
129+ clientConfig := & ssh.ClientConfig {}
130+ var address string
131+ if addressBytes , ok := backendCredentials ["address" ]; ok {
132+ address = string (addressBytes )
133+ if ! strings .Contains (address , ":" ) {
134+ address = address + ":22"
135+ }
136+ }
137+
138+ if user , ok := backendCredentials ["user" ]; ok {
139+ clientConfig .User = string (user )
140+ }
141+
142+ logger .Debugf ("Trying to connect with user: %s" , clientConfig .User )
143+
144+ if hostKeyStr , ok := backendCredentials ["hostKey" ]; ok {
145+ var hostKey ssh.PublicKey
146+ if hostKey , err = ssh .ParsePublicKey ([]byte (hostKeyStr )); err != nil {
147+ logger .Errorf ("Unable to parse public key: %v" , err )
148+ return
149+ }
150+ clientConfig .HostKeyCallback = ssh .FixedHostKey (hostKey )
151+ } else {
152+ clientConfig .HostKeyCallback = ssh .InsecureIgnoreHostKey ()
153+ }
154+
155+ if password , ok := backendCredentials ["password" ]; ok {
156+ clientConfig .Auth = append (clientConfig .Auth , ssh .Password (string (password )))
157+ }
158+
159+ if privateKeyBytes , ok := backendCredentials ["privateKey" ]; ok {
160+ var signer ssh.Signer
161+ if signer , err = ssh .ParsePrivateKey ([]byte (privateKeyBytes )); err != nil {
162+ logger .Debugf ("Unable to parse private key: %v" , err )
163+ return
164+ }
165+
166+ clientConfig .Auth = append (clientConfig .Auth , ssh .PublicKeys (signer ))
167+ }
168+
169+ p , err := newSSHProxyConn (
170+ tcpConn ,
171+ serverConfig ,
172+ clientConfig ,
173+ address ,
174+ )
175+ if err != nil {
176+ logger .Errorln ("Connection from %v closed. %v" , tcpConn .RemoteAddr (), err )
153177 return
154178 }
179+ logger .Infof ("Establish a proxy connection between %v and %v" , tcpConn .RemoteAddr (), p .DestinationHost )
155180
156- logger .Infof ("Connection closed on %v" , conn .LocalAddr ())
181+ err = p .Wait ()
182+ logger .Debugf ("Connection from %v closed. %v" , tcpConn .RemoteAddr (), err )
157183 }()
158184 }
159185 }()
@@ -167,3 +193,55 @@ func (proxy *proxyService) Stop() error {
167193 proxy .done = true
168194 return proxy .listener .Close ()
169195}
196+
197+ func newSSHProxyConn (
198+ conn net.Conn ,
199+ serverConfig * ssh.ServerConfig ,
200+ clientConfig * ssh.ClientConfig ,
201+ upstreamHostAndPort string ,
202+ ) (proxyConn * ssh.ProxyConn , err error ) {
203+ d , err := ssh .NewDownstreamConn (conn , serverConfig )
204+ if err != nil {
205+ return nil , err
206+ }
207+ defer func () {
208+ if proxyConn == nil {
209+ d .Close ()
210+ }
211+ }()
212+
213+ authRequestMsg , err := d .GetAuthRequestMsg ()
214+ if err != nil {
215+ return nil , err
216+ }
217+
218+ // use client provided user if client config is empty
219+ if clientConfig .User == "" {
220+ clientConfig .User = authRequestMsg .User
221+ }
222+
223+ upConn , err := net .Dial ("tcp" , upstreamHostAndPort )
224+ if err != nil {
225+ return nil , err
226+ }
227+
228+ u , err := ssh .NewUpstreamConn (upConn , clientConfig )
229+ if err != nil {
230+ return nil , err
231+ }
232+ defer func () {
233+ if proxyConn == nil {
234+ u .Close ()
235+ }
236+ }()
237+ p := & ssh.ProxyConn {
238+ Upstream : u ,
239+ Downstream : d ,
240+ }
241+
242+ if err = p .AuthenticateProxyConn (clientConfig ); err != nil {
243+ return nil , err
244+ }
245+
246+ return p , nil
247+ }
0 commit comments