teleport/srv/srv_test.go
2015-09-29 01:04:10 +03:00

301 lines
7.5 KiB
Go

package srv
import (
"bytes"
"fmt"
"io"
"path/filepath"
"strings"
"testing"
"github.com/gravitational/teleport/auth"
authority "github.com/gravitational/teleport/auth/native"
"github.com/gravitational/teleport/backend/boltbk"
"github.com/gravitational/teleport/backend/encryptedbk"
"github.com/gravitational/teleport/services"
"github.com/gravitational/teleport/sshutils"
"github.com/gravitational/teleport/utils"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/lemma/secret"
"github.com/gravitational/teleport/Godeps/_workspace/src/golang.org/x/crypto/ssh"
"github.com/gravitational/teleport/Godeps/_workspace/src/golang.org/x/crypto/ssh/agent"
. "github.com/gravitational/teleport/Godeps/_workspace/src/gopkg.in/check.v1"
)
func TestSrv(t *testing.T) { TestingT(t) }
type SrvSuite struct {
srv *Server
clt *ssh.Client
bk *encryptedbk.ReplicatedBackend
a *auth.AuthServer
up *upack
scrt secret.SecretService
dir string
}
var _ = Suite(&SrvSuite{})
func (s *SrvSuite) SetUpSuite(c *C) {
key, err := secret.NewKey()
c.Assert(err, IsNil)
srv, err := secret.New(&secret.Config{KeyBytes: key})
c.Assert(err, IsNil)
s.scrt = srv
}
func (s *SrvSuite) SetUpTest(c *C) {
s.dir = c.MkDir()
baseBk, err := boltbk.New(filepath.Join(s.dir, "db"))
c.Assert(err, IsNil)
s.bk, err = encryptedbk.NewReplicatedBackend(baseBk, filepath.Join(s.dir, "keys"), nil)
c.Assert(err, IsNil)
s.a = auth.NewAuthServer(s.bk, authority.New(), s.scrt)
// set up host private key and certificate
c.Assert(s.a.ResetHostCA(""), IsNil)
hpriv, hpub, err := s.a.GenerateKeyPair("")
c.Assert(err, IsNil)
hcert, err := s.a.GenerateHostCert(hpub, "localhost", "localhost", 0)
c.Assert(err, IsNil)
// set up user CA and set up a user that has access to the server
c.Assert(s.a.ResetUserCA(""), IsNil)
signer, err := sshutils.NewSigner(hpriv, hcert)
c.Assert(err, IsNil)
srv, err := New(
utils.NetAddr{Network: "tcp", Addr: "localhost:0"},
[]ssh.Signer{signer},
auth.NewBackendAccessPoint(s.bk),
SetShell("/bin/sh"),
)
c.Assert(err, IsNil)
s.srv = srv
c.Assert(s.srv.Start(), IsNil)
// set up SSH client using the user private key for signing
up, err := newUpack("test", s.a)
c.Assert(err, IsNil)
// set up an agent server and a client that uses agent for forwarding
keyring := agent.NewKeyring()
c.Assert(keyring.Add(up.pkey, up.pcert, ""), IsNil)
s.up = up
sshConfig := &ssh.ClientConfig{
User: "test",
Auth: []ssh.AuthMethod{ssh.PublicKeys(up.certSigner)},
}
client, err := ssh.Dial("tcp", s.srv.Addr(), sshConfig)
c.Assert(err, IsNil)
c.Assert(agent.ForwardToAgent(client, keyring), IsNil)
s.clt = client
}
func (s *SrvSuite) TearDownTest(c *C) {
c.Assert(s.clt.Close(), IsNil)
c.Assert(s.srv.Close(), IsNil)
}
// TestExec executes a command on a remote server
func (s *SrvSuite) TestExec(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
defer se.Close()
out, err := se.Output("expr 2 + 3")
c.Assert(err, IsNil)
c.Assert(strings.Trim(string(out), " \n"), Equals, "5")
}
// TestShell launches interactive shell session and executes a command
func (s *SrvSuite) TestShell(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
w, err := se.StdinPipe()
c.Assert(err, IsNil)
stdout := &bytes.Buffer{}
se.Stdout = stdout
c.Assert(se.Shell(), IsNil)
_, err = io.WriteString(w, "expr 7 + 70;exit\r\n")
c.Assert(err, IsNil)
c.Assert(se.Wait(), IsNil)
c.Assert(removeNL(stdout.String()), Matches, ".*77.*")
}
// TestMux tests multiplexing command with agent forwarding
func (s *SrvSuite) TestMux(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
defer se.Close()
c.Assert(agent.RequestAgentForwarding(se), IsNil)
stdout := &bytes.Buffer{}
reader, err := se.StdoutPipe()
done := make(chan struct{})
go func() {
io.Copy(stdout, reader)
close(done)
}()
c.Assert(se.RequestSubsystem(fmt.Sprintf("mux:%v/expr 22 + 55", s.srv.Addr())), IsNil)
<-done
c.Assert(removeNL(stdout.String()), Matches, ".*77.*")
}
// TestTun tests tunneling command with agent forwarding
func (s *SrvSuite) TestTun(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
defer se.Close()
c.Assert(agent.RequestAgentForwarding(se), IsNil)
writer, err := se.StdinPipe()
c.Assert(err, IsNil)
stdout := &bytes.Buffer{}
reader, err := se.StdoutPipe()
done := make(chan struct{})
go func() {
io.Copy(stdout, reader)
close(done)
}()
c.Assert(se.RequestSubsystem(fmt.Sprintf("tun:%v", s.srv.Addr())), IsNil)
_, err = io.WriteString(writer, "expr 7 + 70;exit\r\n")
c.Assert(err, IsNil)
<-done
c.Assert(removeNL(stdout.String()), Matches, ".*77.*")
}
// TestPTY requests PTY for an interactive session
func (s *SrvSuite) TestPTY(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
defer se.Close()
// request PTY
c.Assert(se.RequestPty("xterm", 30, 30, ssh.TerminalModes{}), IsNil)
}
// TestEnv requests setting environment variables. (We are currently ignoring these requests)
func (s *SrvSuite) TestEnv(c *C) {
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
defer se.Close()
c.Assert(se.Setenv("HOME", "/"), IsNil)
}
// TestNoAuth tries to log in with no auth methods and should be rejected
func (s *SrvSuite) TestNoAuth(c *C) {
_, err := ssh.Dial("tcp", s.srv.Addr(), &ssh.ClientConfig{})
c.Assert(err, NotNil)
}
// TestPasswordAuth tries to log in with empty pass and should be rejected
func (s *SrvSuite) TestPasswordAuth(c *C) {
config := &ssh.ClientConfig{Auth: []ssh.AuthMethod{ssh.Password("")}}
_, err := ssh.Dial("tcp", s.srv.Addr(), config)
c.Assert(err, NotNil)
}
// TODO(klizhentas): figure out the way to check that resources are properly deallocated
// on client disconnects
func (s *SrvSuite) TestClientDisconnect(c *C) {
config := &ssh.ClientConfig{
User: "test",
Auth: []ssh.AuthMethod{ssh.PublicKeys(s.up.certSigner)},
}
clt, err := ssh.Dial("tcp", s.srv.Addr(), config)
c.Assert(clt, NotNil)
c.Assert(err, IsNil)
se, err := s.clt.NewSession()
c.Assert(err, IsNil)
c.Assert(se.Shell(), IsNil)
c.Assert(clt.Close(), IsNil)
}
// upack holds all ssh signing artefacts needed for signing and checking user keys
type upack struct {
// key is a raw private user key
key []byte
// pkey is parsed private SSH key
pkey interface{}
// pub is a public user key
pub []byte
//cert is a certificate signed by user CA
cert []byte
// pcert is a parsed ssh Certificae
pcert *ssh.Certificate
// signer is a signer that answers signing challenges using private key
signer ssh.Signer
// certSigner is a signer that answers signing challenges using private
// key and a certificate issued by user certificate authority
certSigner ssh.Signer
}
func newUpack(user string, a *auth.AuthServer) (*upack, error) {
upriv, upub, err := a.GenerateKeyPair("")
if err != nil {
return nil, err
}
ucert, err := a.UpsertUserKey(user, services.AuthorizedKey{ID: user, Value: upub}, 0)
if err != nil {
return nil, err
}
upkey, err := ssh.ParseRawPrivateKey(upriv)
if err != nil {
return nil, err
}
usigner, err := ssh.NewSignerFromKey(upkey)
if err != nil {
return nil, err
}
pcert, _, _, _, err := ssh.ParseAuthorizedKey(ucert)
if err != nil {
return nil, err
}
ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)
if err != nil {
return nil, err
}
return &upack{
key: upriv,
pkey: upkey,
pub: upub,
cert: ucert,
pcert: pcert.(*ssh.Certificate),
signer: usigner,
certSigner: ucertSigner,
}, nil
}
func removeNL(v string) string {
v = strings.Replace(v, "\r", "", -1)
v = strings.Replace(v, "\n", "", -1)
return v
}