Skip to content

Commit 2fff806

Browse files
improved ssh service connector
1 parent 932e51b commit 2fff806

File tree

4 files changed

+118
-224
lines changed

4 files changed

+118
-224
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ require (
4848
k8s.io/client-go v0.0.0-20180806134042-1f13a808da65
4949
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c // indirect
5050
)
51+
52+
replace golang.org/x/crypto => github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ github.com/cyberark/summon v0.7.0/go.mod h1:S7grcxHeUxfL1vRTQUyq9jGK8yG6V/tSlLPQ
2525
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2626
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2727
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
28+
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016132516-629195587fad h1:dqregO1InniAGdH6O8tVPGPYGlL9kj2Tvkie9UYmlUs=
29+
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016132516-629195587fad/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
30+
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c h1:dTPA7kSjboC+CYBH0z7cwFKX2GwOb78yyLSqbVRb4qQ=
31+
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
2832
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
2933
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
3034
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

internal/plugin/connectors/ssh/proxy_service.go

Lines changed: 112 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
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.
7965
func (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+
}

internal/plugin/connectors/ssh/service_connector.go

Lines changed: 0 additions & 190 deletions
This file was deleted.

0 commit comments

Comments
 (0)