mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 09:44:51 +00:00
Covered host authentication with tests
This commit is contained in:
parent
ef790eefa6
commit
3ff5820b67
|
@ -40,6 +40,9 @@ type LocalKeyAgent struct {
|
|||
// map of "no hosts". these are hosts that user manually (via keyboard
|
||||
// input) refused connecting to.
|
||||
noHosts map[string]bool
|
||||
|
||||
// function which asks a user to trust host/key combination (during host auth)
|
||||
hostPromptFunc func(host string, k ssh.PublicKey) error
|
||||
}
|
||||
|
||||
// NewLocalAgent reads all Teleport certificates from disk (using FSLocalKeyStore),
|
||||
|
@ -212,40 +215,44 @@ func (a *LocalKeyAgent) UserRefusedHosts() bool {
|
|||
|
||||
// CheckHostSignature checks if the given host key was signed by one of the trusted
|
||||
// certificaate authorities (CAs)
|
||||
func (a *LocalKeyAgent) CheckHostSignature(hostId string, remote net.Addr, key ssh.PublicKey) error {
|
||||
promptUser := func() error {
|
||||
func (a *LocalKeyAgent) CheckHostSignature(host string, remote net.Addr, key ssh.PublicKey) error {
|
||||
hostPromptFunc := func(host string, key ssh.PublicKey) error {
|
||||
userAnswer := "no"
|
||||
if !a.noHosts[hostId] {
|
||||
if !a.noHosts[host] {
|
||||
fmt.Printf("The authenticity of host '%s' can't be established. "+
|
||||
"Its public key is:\n%s\nAre you sure you want to continue (yes/no)? ",
|
||||
hostId, ssh.MarshalAuthorizedKey(key))
|
||||
host, ssh.MarshalAuthorizedKey(key))
|
||||
|
||||
bytes := make([]byte, 12)
|
||||
os.Stdin.Read(bytes)
|
||||
userAnswer = strings.TrimSpace(strings.ToLower(string(bytes)))
|
||||
}
|
||||
if !strings.HasPrefix(userAnswer, "y") {
|
||||
a.noHosts[hostId] = true
|
||||
return trace.AccessDenied("untrusted host %v", hostId)
|
||||
return trace.AccessDenied("untrusted host %v", host)
|
||||
}
|
||||
// success
|
||||
return nil
|
||||
}
|
||||
// overwritten host prompt func? (probably for tests)
|
||||
if a.hostPromptFunc != nil {
|
||||
hostPromptFunc = a.hostPromptFunc
|
||||
}
|
||||
cert, ok := key.(*ssh.Certificate)
|
||||
if !ok {
|
||||
// not a signed cert? perhaps we're given a host public key (happens when the host is running
|
||||
// sshd instead of teleport daemon
|
||||
keys, _ := a.keyStore.GetKnownHostKeys(hostId)
|
||||
keys, _ := a.keyStore.GetKnownHostKeys(host)
|
||||
if len(keys) > 0 && sshutils.KeysEqual(key, keys[0]) {
|
||||
log.Debugf("[KEY AGENT] verified host %s", hostId)
|
||||
log.Debugf("[KEY AGENT] verified host %s", host)
|
||||
return nil
|
||||
}
|
||||
// ask user:
|
||||
if err := promptUser(); err != nil {
|
||||
if err := hostPromptFunc(host, key); err != nil {
|
||||
a.noHosts[host] = true
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// remember the host key (put it into 'known_hosts')
|
||||
if err := a.keyStore.AddKnownHostKeys(hostId, []ssh.PublicKey{key}); err != nil {
|
||||
if err := a.keyStore.AddKnownHostKeys(host, []ssh.PublicKey{key}); err != nil {
|
||||
log.Warnf("error saving the host key: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
@ -260,15 +267,16 @@ func (a *LocalKeyAgent) CheckHostSignature(hostId string, remote net.Addr, key s
|
|||
log.Debugf("[KEY AGENT] got %d known hosts", len(keys))
|
||||
for i := range keys {
|
||||
if sshutils.KeysEqual(cert.SignatureKey, keys[i]) {
|
||||
log.Debugf("[KEY AGENT] verified host %s", hostId)
|
||||
log.Debugf("[KEY AGENT] verified host %s", host)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// final step: lets ask user:
|
||||
if err = promptUser(); err != nil {
|
||||
if err = hostPromptFunc(host, key); err != nil {
|
||||
a.noHosts[host] = true
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
err = a.keyStore.AddKnownHostKeys(hostId, []ssh.PublicKey{key})
|
||||
err = a.keyStore.AddKnownHostKeys(host, []ssh.PublicKey{key})
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"golang.org/x/crypto/ssh/agent"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth/native"
|
||||
"github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/trace"
|
||||
|
@ -202,8 +202,61 @@ func (s *KeyAgentTestSuite) TestLoadKey(c *check.C) {
|
|||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *KeyAgentTestSuite) TestHostVerification(c *check.C) {
|
||||
// make a new local agent
|
||||
lka, err := NewLocalAgent(s.keyDir, s.username)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// by default user has not refused any hosts:
|
||||
c.Assert(lka.UserRefusedHosts(), check.Equals, false)
|
||||
|
||||
// make a fake host key:
|
||||
keygen := testauthority.New()
|
||||
_, pub, err := keygen.GenerateKeyPair("")
|
||||
c.Assert(err, check.IsNil)
|
||||
pk, _, _, _, err := ssh.ParseAuthorizedKey(pub)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// test user refusing connection:
|
||||
fakeErr := trace.Errorf("luna cannot be trusted!")
|
||||
lka.hostPromptFunc = func(host string, k ssh.PublicKey) error {
|
||||
c.Assert(host, check.Equals, "luna")
|
||||
c.Assert(k, check.Equals, pk)
|
||||
return fakeErr
|
||||
}
|
||||
var a net.TCPAddr
|
||||
err = lka.CheckHostSignature("luna", &a, pk)
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(err.Error(), check.Equals, "luna cannot be trusted!")
|
||||
c.Assert(lka.UserRefusedHosts(), check.Equals, true)
|
||||
|
||||
// clean user answer:
|
||||
delete(lka.noHosts, "luna")
|
||||
c.Assert(lka.UserRefusedHosts(), check.Equals, false)
|
||||
|
||||
// now lets simulate user being asked:
|
||||
userWasAsked := false
|
||||
lka.hostPromptFunc = func(host string, k ssh.PublicKey) error {
|
||||
// user answered "yes"
|
||||
userWasAsked = true
|
||||
return nil
|
||||
}
|
||||
c.Assert(lka.UserRefusedHosts(), check.Equals, false)
|
||||
err = lka.CheckHostSignature("luna", &a, pk)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(userWasAsked, check.Equals, true)
|
||||
|
||||
// now lets simulate automatic host verification (no need to ask user, he
|
||||
// just said "yes")
|
||||
userWasAsked = false
|
||||
c.Assert(lka.UserRefusedHosts(), check.Equals, false)
|
||||
err = lka.CheckHostSignature("luna", &a, pk)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(userWasAsked, check.Equals, false)
|
||||
}
|
||||
|
||||
func makeKey(username string, allowedLogins []string, ttl time.Duration) (*Key, error) {
|
||||
keygen := native.New()
|
||||
keygen := testauthority.New()
|
||||
|
||||
privateKey, publicKey, err := keygen.GenerateKeyPair("")
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue