Skip to content

Commit

Permalink
sshproxy-managerd: enhance host healthcheck
Browse files Browse the repository at this point in the history
Instead of just checking if a port is reachable we try to connect to the
SSH server using an already connected user.
  • Loading branch information
fihuer authored and arno committed Jul 5, 2018
1 parent 27562cb commit 281532a
Show file tree
Hide file tree
Showing 48 changed files with 16,367 additions and 1 deletion.
14 changes: 13 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ ignored = [
branch = "v2"
source = "https://github.com/go-yaml/yaml.git"

[[constraint]]
name = "golang.org/x/crypto"
# Cannot use a more recent version because of commit 83c378c48 which is
# incompatible with Go 1.8.
revision = "dccd99e89d39b13e12f1103c72e3bc49eb40a833"

[prune]
non-go = true
go-tests = true
Expand Down
26 changes: 26 additions & 0 deletions route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ package route
import (
"math/rand"
"net"
"regexp"
"time"

"github.com/op/go-logging"
"golang.org/x/crypto/ssh"
)

var log = logging.MustGetLogger("sshproxy/route")
Expand All @@ -30,6 +32,8 @@ var (
DefaultRouteKeyword = "default:22"
)

var unableToAuthenticateRegexp = regexp.MustCompile(`handshake failed: ssh: unable to authenticate`)

// HostChecker is the interface that wraps the Check method.
//
// Check tests if a connection to host:port can be made.
Expand Down Expand Up @@ -63,6 +67,28 @@ func CanConnect(hostport string) bool {
return true
}

// MightAuthenticate tests if a connection to host:port can initiate an handshake.
func MightAuthenticate(hostport string, user string) bool {
sshConfig := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password("ThisIsNotIntendedToBeAValidPasswordButWeDontReallyCare"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
log.Debug("Dialing %s with user %s", hostport, user)
client, err := ssh.Dial("tcp", hostport, sshConfig)
if err != nil {
if unableToAuthenticateRegexp.MatchString(err.Error()) {
return true
}
log.Warning("Error while dialing %v", err)
return false
}
client.Close()
return true
}

// selectDestinationOrdered selects the first reachable destination from a list
// of destinations. It returns a string "host:port", an empty string (if no
// destination is found) or an error.
Expand Down
35 changes: 35 additions & 0 deletions sshproxy-managerd/sshproxy-managerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"flag"
"fmt"
"io/ioutil"
"math/rand"
"net"
"os"
"os/signal"
Expand Down Expand Up @@ -186,10 +187,42 @@ func (hc *hostChecker) DoCheck(hostport string) State {
if route.CanConnect(hostport) {
state = Up
}
canaryUser, err := pickUser()
if err == nil && canaryUser != "" && state == Up {
if route.MightAuthenticate(hostport, canaryUser) {
log.Debug("Succefully tried to authenticate to %s as %s", hostport, canaryUser)
} else {
log.Debug("Unable to try to authenticate against %s as %s", hostport, canaryUser)
state = Down
}
} else if err != nil {
log.Debugf("Unable to try to authenticate, found no user to spoof (%s)", err)
}
hc.Update(hostport, state, time.Now())
return state
}

func pickUser() (string, error) {
chosenUser := ""
if len(proxiedConnections) > 0 {
chosenItem := rand.Intn(len(proxiedConnections))
for k := range proxiedConnections {
if chosenItem == 0 {
user, err := getUserFromKey(k)
if err != nil {
return "", err
}
chosenUser = user
break
}
chosenItem--
}
} else {
return "", errors.New("no proxied connections, unable to pick a random user")
}
return chosenUser, nil
}

// Update updates (or creates) the state of an host in the internal view.
func (hc *hostChecker) Update(hostport string, state State, ts time.Time) {
if s, ok := hc.States[hostport]; ok {
Expand Down Expand Up @@ -672,6 +705,8 @@ func main() {
}
defer l.Close()

rand.Seed(time.Now().Unix()) // initialize global pseudo random generator

log.Info("listening on %s\n", config.Listen)

queue := make(chan *request)
Expand Down
3 changes: 3 additions & 0 deletions vendor/golang.org/x/crypto/AUTHORS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions vendor/golang.org/x/crypto/CONTRIBUTORS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions vendor/golang.org/x/crypto/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions vendor/golang.org/x/crypto/PATENTS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions vendor/golang.org/x/crypto/curve25519/const_amd64.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions vendor/golang.org/x/crypto/curve25519/const_amd64.s

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions vendor/golang.org/x/crypto/curve25519/cswap_amd64.s

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 281532a

Please sign in to comment.