mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
301 lines
7.5 KiB
Go
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
|
|
}
|