Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improved ssh service connector #953

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ require (
k8s.io/client-go v0.0.0-20180806134042-1f13a808da65
k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c // indirect
)

replace golang.org/x/crypto => github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ github.com/cyberark/summon v0.7.0/go.mod h1:S7grcxHeUxfL1vRTQUyq9jGK8yG6V/tSlLPQ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016132516-629195587fad h1:dqregO1InniAGdH6O8tVPGPYGlL9kj2Tvkie9UYmlUs=
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016132516-629195587fad/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c h1:dTPA7kSjboC+CYBH0z7cwFKX2GwOb78yyLSqbVRb4qQ=
github.com/doodlesbykumbi/sshr.crypto v0.0.0-20191016154246-b1c2b8ffdb9c/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
Expand Down
146 changes: 112 additions & 34 deletions internal/plugin/connectors/ssh/proxy_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io/ioutil"
"net"
"os"
"strings"

validation "github.com/go-ozzo/ozzo-validation"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -59,21 +60,6 @@ func NewProxyService(
}, nil
}

func (proxy *proxyService) handleConnections(channels <-chan ssh.NewChannel) error {
var connector = ServiceConnector{
channels: channels,
logger: proxy.logger,
}

backendCredentials, err := proxy.retrieveCredentials()
defer internal.ZeroizeCredentials(backendCredentials)
if err != nil {
return fmt.Errorf("failed on retrieve credentials: %s", err)
}

return connector.Connect(backendCredentials)
}

// Start initiates the net.Listener to listen for incoming connections
// Listen accepts SSH connections and MITMs them using a ServiceConnector.
func (proxy *proxyService) Start() error {
Expand Down Expand Up @@ -128,32 +114,72 @@ func (proxy *proxyService) Start() error {
return
}

// https://godoc.org/golang.org/x/crypto/ssh#NewServerConn
conn, chans, reqs, err := ssh.NewServerConn(nConn, serverConfig)
if err != nil {
logger.Debugf("Failed to handshake: %s", err)
continue
}
logger.Debugf(
"New connection accepted for user %s from %s",
conn.User(),
conn.RemoteAddr(),
)
tcpConn := nConn.(*net.TCPConn)
logger.Debugf("SSH Client connected. ClientIP=%v", tcpConn.RemoteAddr())


// The incoming Request channel must be serviced.
go func() {
for req := range reqs {
logger.Debugf("Global SSH request : %v", req)
backendCredentials, err := proxy.retrieveCredentials()
defer internal.ZeroizeCredentials(backendCredentials)
if err != nil {
logger.Errorf("Failed on retrieve credentials: %s", err)
return
}
}()

go func() {
if err := proxy.handleConnections(chans); err != nil {
logger.Errorf("Failed on handle connection: %s", err)
clientConfig := &ssh.ClientConfig{}
var address string
if addressBytes, ok := backendCredentials["address"]; ok {
address = string(addressBytes)
if !strings.Contains(address, ":") {
address = address + ":22"
}
}

if user, ok := backendCredentials["user"]; ok {
clientConfig.User = string(user)
}

logger.Debugf("Trying to connect with user: %s", clientConfig.User)

if hostKeyStr, ok := backendCredentials["hostKey"]; ok {
var hostKey ssh.PublicKey
if hostKey, err = ssh.ParsePublicKey([]byte(hostKeyStr)); err != nil {
logger.Errorf("Unable to parse public key: %v", err)
return
}
clientConfig.HostKeyCallback = ssh.FixedHostKey(hostKey)
} else {
clientConfig.HostKeyCallback = ssh.InsecureIgnoreHostKey()
}

if password, ok := backendCredentials["password"]; ok {
clientConfig.Auth = append(clientConfig.Auth, ssh.Password(string(password)))
}

if privateKeyBytes, ok := backendCredentials["privateKey"]; ok {
var signer ssh.Signer
if signer, err = ssh.ParsePrivateKey([]byte(privateKeyBytes)); err != nil {
logger.Debugf("Unable to parse private key: %v", err)
return
}

clientConfig.Auth = append(clientConfig.Auth, ssh.PublicKeys(signer))
}

p, err := newSSHProxyConn(
tcpConn,
serverConfig,
clientConfig,
address,
)
if err != nil {
logger.Errorln("Connection from %v closed. %v", tcpConn.RemoteAddr(), err)
return
}
logger.Infof("Establish a proxy connection between %v and %v", tcpConn.RemoteAddr(), p.DestinationHost)

logger.Infof("Connection closed on %v", conn.LocalAddr())
err = p.Wait()
logger.Debugf("Connection from %v closed. %v", tcpConn.RemoteAddr(), err)
}()
}
}()
Expand All @@ -167,3 +193,55 @@ func (proxy *proxyService) Stop() error {
proxy.done = true
return proxy.listener.Close()
}

func newSSHProxyConn(
conn net.Conn,
serverConfig *ssh.ServerConfig,
clientConfig *ssh.ClientConfig,
upstreamHostAndPort string,
) (proxyConn *ssh.ProxyConn, err error) {
d, err := ssh.NewDownstreamConn(conn, serverConfig)
if err != nil {
return nil, err
}
defer func() {
if proxyConn == nil {
d.Close()
}
}()

authRequestMsg, err := d.GetAuthRequestMsg()
if err != nil {
return nil, err
}

// use client provided user if client config is empty
if clientConfig.User == "" {
clientConfig.User = authRequestMsg.User
}

upConn, err := net.Dial("tcp", upstreamHostAndPort)
if err != nil {
return nil, err
}

u, err := ssh.NewUpstreamConn(upConn, clientConfig)
if err != nil {
return nil, err
}
defer func() {
if proxyConn == nil {
u.Close()
}
}()
p := &ssh.ProxyConn{
Upstream: u,
Downstream: d,
}

if err = p.AuthenticateProxyConn(clientConfig); err != nil {
return nil, err
}

return p, nil
}
190 changes: 0 additions & 190 deletions internal/plugin/connectors/ssh/service_connector.go

This file was deleted.