mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 01:03:40 +00:00
Added integration tests and minor fixes.
This commit is contained in:
parent
81a8e6bca6
commit
3bfe61dc0b
2
Makefile
2
Makefile
|
@ -124,7 +124,7 @@ test: $(VERSRC)
|
|||
#
|
||||
.PHONY: integration
|
||||
integration:
|
||||
go test -v ./integration/...
|
||||
go test -v ./integration/... -check.v
|
||||
|
||||
# This rule triggers re-generation of version.go and gitref.go if Makefile changes
|
||||
$(VERSRC): Makefile
|
||||
|
|
|
@ -8,10 +8,16 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/native"
|
||||
|
@ -23,6 +29,8 @@ import (
|
|||
"github.com/gravitational/teleport/lib/reversetunnel"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
"github.com/gravitational/teleport/lib/teleagent"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
@ -253,7 +261,6 @@ func (i *TeleInstance) CreateEx(trustedSecrets []*InstanceSecrets, tconf *servic
|
|||
tconf = service.MakeDefaultConfig()
|
||||
}
|
||||
tconf.DataDir = dataDir
|
||||
tconf.Auth.ClusterConfig = services.DefaultClusterConfig()
|
||||
tconf.Auth.ClusterName, err = services.NewClusterName(services.ClusterNameSpecV2{
|
||||
ClusterName: i.Secrets.SiteName,
|
||||
})
|
||||
|
@ -294,16 +301,23 @@ func (i *TeleInstance) CreateEx(trustedSecrets []*InstanceSecrets, tconf *servic
|
|||
Type: boltbk.GetName(),
|
||||
Params: backend.Params{"path": dataDir},
|
||||
}
|
||||
tconf.Keygen = testauthority.New()
|
||||
|
||||
tconf.Auth.ClusterConfig = services.DefaultClusterConfig()
|
||||
tconf.Keygen = testauthority.New()
|
||||
|
||||
i.Config = tconf
|
||||
i.Process, err = service.NewTeleport(tconf)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// create users and roles if they don't exist, or sign their keys if they're already present
|
||||
|
||||
// if the auth server is not enabled, nothing more to do be done
|
||||
if !tconf.Auth.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if this instance contains an auth server, configure the auth server as well.
|
||||
// create users and roles if they don't exist, or sign their keys if they're
|
||||
// already present
|
||||
auth := i.Process.GetAuthServer()
|
||||
|
||||
for _, user := range i.Secrets.Users {
|
||||
|
@ -315,6 +329,12 @@ func (i *TeleInstance) CreateEx(trustedSecrets []*InstanceSecrets, tconf *servic
|
|||
if len(user.Roles) == 0 {
|
||||
role := services.RoleForUser(teleUser)
|
||||
role.SetLogins(services.Allow, user.AllowedLogins)
|
||||
|
||||
// allow tests to forward agent, still needs to be passed in client
|
||||
roleOptions := role.GetOptions()
|
||||
roleOptions.Set(services.ForwardAgent, true)
|
||||
role.SetOptions(roleOptions)
|
||||
|
||||
err = auth.UpsertRole(role, backend.Forever)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
@ -358,30 +378,72 @@ func (i *TeleInstance) CreateEx(trustedSecrets []*InstanceSecrets, tconf *servic
|
|||
return nil
|
||||
}
|
||||
|
||||
// StartNode starts SSH node and connects it to the cluster
|
||||
func (i *TeleInstance) StartNode(name string, sshPort, proxyWebPort, proxySSHPort int) error {
|
||||
// StartNode starts SSH node and connects it to the cluster.
|
||||
func (i *TeleInstance) StartNode(name string, sshPort int) (*service.TeleportProcess, error) {
|
||||
dataDir, err := ioutil.TempDir("", "cluster-"+i.Secrets.SiteName)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
tconf := service.MakeDefaultConfig()
|
||||
|
||||
authServer := utils.MustParseAddr(net.JoinHostPort(i.Hostname, i.GetPortAuth()))
|
||||
tconf.AuthServers = append(tconf.AuthServers, *authServer)
|
||||
tconf.Token = "token"
|
||||
tconf.HostUUID = name
|
||||
tconf.Hostname = name
|
||||
tconf.DataDir = dataDir
|
||||
tconf.CachePolicy = service.CachePolicy{Enabled: true}
|
||||
|
||||
tconf.Auth.Enabled = false
|
||||
|
||||
tconf.Proxy.Enabled = false
|
||||
|
||||
tconf.SSH.Enabled = true
|
||||
tconf.SSH.Addr.Addr = net.JoinHostPort(i.Hostname, fmt.Sprintf("%v", sshPort))
|
||||
|
||||
process, err := service.NewTeleport(tconf)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
i.Nodes = append(i.Nodes, process)
|
||||
|
||||
err = process.Start()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return process, nil
|
||||
}
|
||||
|
||||
// StartNodeAndProxy starts SSH node and proxy and connects it to the cluster.
|
||||
func (i *TeleInstance) StartNodeAndProxy(name string, sshPort, proxyWebPort, proxySSHPort int) error {
|
||||
dataDir, err := ioutil.TempDir("", "cluster-"+i.Secrets.SiteName)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
tconf := service.MakeDefaultConfig()
|
||||
tconf.HostUUID = name
|
||||
tconf.Hostname = name
|
||||
tconf.DataDir = dataDir
|
||||
tconf.Auth.Enabled = false
|
||||
tconf.Proxy.Enabled = true
|
||||
tconf.SSH.Enabled = true
|
||||
tconf.SSH.Addr.Addr = net.JoinHostPort(i.Hostname, fmt.Sprintf("%v", sshPort))
|
||||
|
||||
authServer := utils.MustParseAddr(net.JoinHostPort(i.Hostname, i.GetPortAuth()))
|
||||
tconf.AuthServers = append(tconf.AuthServers, *authServer)
|
||||
tconf.Token = "token"
|
||||
tconf.HostUUID = name
|
||||
tconf.Hostname = name
|
||||
tconf.DataDir = dataDir
|
||||
tconf.CachePolicy = service.CachePolicy{Enabled: true}
|
||||
|
||||
tconf.Auth.Enabled = false
|
||||
|
||||
tconf.Proxy.Enabled = true
|
||||
tconf.Proxy.SSHAddr.Addr = net.JoinHostPort(i.Hostname, fmt.Sprintf("%v", proxySSHPort))
|
||||
tconf.Proxy.WebAddr.Addr = net.JoinHostPort(i.Hostname, fmt.Sprintf("%v", proxyWebPort))
|
||||
tconf.Proxy.DisableReverseTunnel = true
|
||||
tconf.Proxy.DisableWebService = true
|
||||
// Enable caching
|
||||
tconf.CachePolicy = service.CachePolicy{Enabled: true}
|
||||
|
||||
tconf.SSH.Enabled = true
|
||||
tconf.SSH.Addr.Addr = net.JoinHostPort(i.Hostname, fmt.Sprintf("%v", sshPort))
|
||||
|
||||
process, err := service.NewTeleport(tconf)
|
||||
if err != nil {
|
||||
|
@ -531,6 +593,9 @@ type ClientConfig struct {
|
|||
Port int
|
||||
// Proxy is an optional alternative proxy to use
|
||||
Proxy *ProxyConfig
|
||||
// ForwardAgent controls if the client requests it's agent be forwarded to
|
||||
// the server.
|
||||
ForwardAgent bool
|
||||
}
|
||||
|
||||
// NewClient returns a fully configured and pre-authenticated client
|
||||
|
@ -574,6 +639,7 @@ func (i *TeleInstance) NewClient(cfg ClientConfig) (tc *client.TeleportClient, e
|
|||
InsecureSkipVerify: true,
|
||||
KeysDir: keyDir,
|
||||
SiteName: cfg.Cluster,
|
||||
ForwardAgent: cfg.ForwardAgent,
|
||||
}
|
||||
cconf.SetProxy(proxyHost, proxyWebPort, proxySSHPort)
|
||||
|
||||
|
@ -723,6 +789,189 @@ func (p *proxyServer) Count() int {
|
|||
return p.count
|
||||
}
|
||||
|
||||
// discardServer is a SSH server that discards SSH exec requests and starts
|
||||
// with the passed in host signer.
|
||||
type discardServer struct {
|
||||
sshServer *sshutils.Server
|
||||
}
|
||||
|
||||
func newDiscardServer(host string, port int, hostSigner ssh.Signer) (*discardServer, error) {
|
||||
ds := &discardServer{}
|
||||
|
||||
// create underlying ssh server
|
||||
sshServer, err := sshutils.NewServer(
|
||||
"integration-discard-server",
|
||||
utils.NetAddr{AddrNetwork: "tcp", Addr: fmt.Sprintf("%v:%v", host, port)},
|
||||
ds,
|
||||
[]ssh.Signer{hostSigner},
|
||||
sshutils.AuthMethods{
|
||||
PublicKey: ds.userKeyAuth,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
ds.sshServer = sshServer
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (s *discardServer) userKeyAuth(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *discardServer) Start() error {
|
||||
return s.sshServer.Start()
|
||||
}
|
||||
|
||||
func (s *discardServer) Stop() {
|
||||
s.sshServer.Close()
|
||||
}
|
||||
|
||||
func (s *discardServer) HandleNewChan(conn net.Conn, sconn *ssh.ServerConn, newChannel ssh.NewChannel) {
|
||||
channel, reqs, err := newChannel.Accept()
|
||||
if err != nil {
|
||||
sconn.Close()
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
go s.handleChannel(channel, reqs)
|
||||
}
|
||||
|
||||
func (s *discardServer) handleChannel(channel ssh.Channel, reqs <-chan *ssh.Request) {
|
||||
defer channel.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case req := <-reqs:
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
if req.Type == "exec" {
|
||||
successPayload := ssh.Marshal(struct{ C uint32 }{C: uint32(0)})
|
||||
channel.SendRequest("exit-status", false, successPayload)
|
||||
if req.WantReply {
|
||||
req.Reply(true, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if req.WantReply {
|
||||
req.Reply(true, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// externalSSHCommand runs an external SSH command (if an external ssh binary
|
||||
// exists) with the passed in parameters.
|
||||
func externalSSHCommand(forwardAgent bool, socketPath string, proxyPort string, nodePort string, command string) (*exec.Cmd, error) {
|
||||
var execArgs []string
|
||||
|
||||
// don't check the host certificate during tests
|
||||
execArgs = append(execArgs, "-oStrictHostKeyChecking=no")
|
||||
execArgs = append(execArgs, "-oUserKnownHostsFile=/dev/null")
|
||||
|
||||
// connect to node on the passed in port
|
||||
execArgs = append(execArgs, "-p")
|
||||
execArgs = append(execArgs, nodePort)
|
||||
|
||||
// build proxy command
|
||||
var proxyCommand string
|
||||
switch forwardAgent {
|
||||
case true:
|
||||
proxyCommand = fmt.Sprintf("ProxyCommand ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oForwardAgent=yes -p %v %%r@localhost -s proxy:%%h:%%p", proxyPort)
|
||||
case false:
|
||||
proxyCommand = fmt.Sprintf("ProxyCommand ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -p %v %%r@localhost -s proxy:%%h:%%p", proxyPort)
|
||||
}
|
||||
execArgs = append(execArgs, "-o")
|
||||
execArgs = append(execArgs, proxyCommand)
|
||||
|
||||
// add in the host the connect to and the command to run when connected
|
||||
execArgs = append(execArgs, Host)
|
||||
execArgs = append(execArgs, command)
|
||||
|
||||
// find the ssh binary
|
||||
sshpath, err := exec.LookPath("ssh")
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// create exec command and tell it where to find the ssh agent
|
||||
cmd, err := exec.Command(sshpath, execArgs...), nil
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
cmd.Env = []string{fmt.Sprintf("SSH_AUTH_SOCK=%v", socketPath)}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// createAgent creates a SSH agent with the passed in private key and
|
||||
// certificate that can be used in tests. This is useful so tests don't
|
||||
// clobber your system agent.
|
||||
func createAgent(me *user.User, privateKeyByte []byte, certificateBytes []byte) (*teleagent.AgentServer, string, string, error) {
|
||||
// create a path to the unix socket
|
||||
sockDir, err := ioutil.TempDir("", "int-test")
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
sockPath := filepath.Join(sockDir, "agent.sock")
|
||||
|
||||
uid, err := strconv.Atoi(me.Uid)
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
gid, err := strconv.Atoi(me.Gid)
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
|
||||
// transform the key and certificate bytes into something the agent can understand
|
||||
publicKey, _, _, _, err := ssh.ParseAuthorizedKey(certificateBytes)
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
privateKey, err := ssh.ParseRawPrivateKey(privateKeyByte)
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
agentKey := agent.AddedKey{
|
||||
PrivateKey: privateKey,
|
||||
Certificate: publicKey.(*ssh.Certificate),
|
||||
Comment: "",
|
||||
LifetimeSecs: 0,
|
||||
ConfirmBeforeUse: false,
|
||||
}
|
||||
|
||||
// create a (unstarted) agent and add the key to it
|
||||
teleAgent := teleagent.NewServer()
|
||||
teleAgent.Add(agentKey)
|
||||
|
||||
// start the SSH agent
|
||||
err = teleAgent.ListenUnixSocket(sockPath, uid, gid, 0600)
|
||||
if err != nil {
|
||||
return nil, "", "", trace.Wrap(err)
|
||||
}
|
||||
go teleAgent.Serve()
|
||||
|
||||
return teleAgent, sockDir, sockPath, nil
|
||||
}
|
||||
|
||||
func closeAgent(teleAgent *teleagent.AgentServer, socketDirPath string) error {
|
||||
err := teleAgent.Close()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(socketDirPath)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fatalIf(err error) {
|
||||
if err != nil {
|
||||
log.Fatal("", err)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -300,7 +300,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
|
|||
|
||||
// user should not have agent forwarding
|
||||
_, exists := parsedCert.Extensions[teleport.CertExtensionPermitAgentForwarding]
|
||||
c.Assert(exists, Equals, false)
|
||||
c.Assert(exists, Equals, true)
|
||||
|
||||
// now update role to permit agent forwarding
|
||||
roleOptions := userRole.GetOptions()
|
||||
|
|
|
@ -408,26 +408,40 @@ func migrateCertAuthority(asrv *AuthServer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DELETE IN: 2.5.0
|
||||
// All users will be migrated to the new roles in Teleport 2.4.0, which means
|
||||
// this entire function can be removed in Teleport 2.5.0.
|
||||
func migrateRoles(asrv *AuthServer) error {
|
||||
roles, err := asrv.GetRoles()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// loop over all roles and only migrate RoleV2 -> RoleV3
|
||||
// loop over all roles and make sure any v3 roles have permit port
|
||||
// forward and forward agent allowed
|
||||
for i, _ := range roles {
|
||||
role := roles[i]
|
||||
_, ok := (role.GetRawObject()).(services.RoleV2)
|
||||
if !ok {
|
||||
continue
|
||||
|
||||
roleOptions := role.GetOptions()
|
||||
|
||||
_, err = roleOptions.GetBoolean(services.PortForwarding)
|
||||
if err != nil {
|
||||
roleOptions.Set(services.PortForwarding, true)
|
||||
role.SetOptions(roleOptions)
|
||||
}
|
||||
|
||||
_, err := roleOptions.GetBoolean(services.ForwardAgent)
|
||||
if err != nil {
|
||||
roleOptions.Set(services.ForwardAgent, true)
|
||||
role.SetOptions(roleOptions)
|
||||
}
|
||||
|
||||
// with RoleV2 we never had a TTL so upsert them forever
|
||||
err = asrv.UpsertRole(role, backend.Forever)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
log.Infof("[MIGRATION] Updated Role: %v", role.GetName())
|
||||
|
||||
log.Infof("[MIGRATION] Updated Role: %v to include port_forwarding and forward_agent option", role.GetName())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/backend/boltbk"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
|
@ -252,3 +253,54 @@ func (s *AuthInitSuite) TestClusterID(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
c.Assert(cc.GetClusterID(), Equals, clusterID)
|
||||
}
|
||||
|
||||
func (s *AuthInitSuite) TestOptions(c *C) {
|
||||
bk, err := boltbk.New(backend.Params{"path": c.MkDir()})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
clusterName, err := services.NewClusterName(services.ClusterNameSpecV2{
|
||||
ClusterName: "me.localhost",
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
authServer, _, err := Init(InitConfig{
|
||||
DataDir: c.MkDir(),
|
||||
HostUUID: "00000000-0000-0000-0000-000000000000",
|
||||
NodeName: "foo",
|
||||
Backend: bk,
|
||||
Authority: testauthority.New(),
|
||||
ClusterConfig: services.DefaultClusterConfig(),
|
||||
ClusterName: clusterName,
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
role := services.NewAdminRole()
|
||||
role.SetOptions(services.RoleOptions{
|
||||
services.MaxSessionTTL: services.NewDuration(defaults.MaxCertDuration),
|
||||
})
|
||||
err = authServer.UpsertRole(role, backend.Forever)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// do it again and make sure the options have been populated
|
||||
authServer, _, err = Init(InitConfig{
|
||||
DataDir: c.MkDir(),
|
||||
HostUUID: "00000000-0000-0000-0000-000000000000",
|
||||
NodeName: "foo",
|
||||
Backend: bk,
|
||||
Authority: testauthority.New(),
|
||||
ClusterConfig: services.DefaultClusterConfig(),
|
||||
ClusterName: clusterName,
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
role, err = authServer.GetRole(teleport.AdminRoleName)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
portForward, err := role.GetOptions().GetBoolean(services.PortForwarding)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(portForward, Equals, true)
|
||||
|
||||
forwardAgent, err := role.GetOptions().GetBoolean(services.ForwardAgent)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(forwardAgent, Equals, true)
|
||||
}
|
||||
|
|
|
@ -182,6 +182,9 @@ func (s *localSite) dialWithAgent(from net.Addr, to net.Addr, userAgent agent.Ag
|
|||
SrcAddr: from,
|
||||
DstAddr: to,
|
||||
HostCertificate: hostCertificate,
|
||||
Ciphers: s.srv.Config.Ciphers,
|
||||
KEXAlgorithms: s.srv.Config.KEXAlgorithms,
|
||||
MACAlgorithms: s.srv.Config.MACAlgorithms,
|
||||
}
|
||||
remoteServer, err := forward.New(serverConfig)
|
||||
if err != nil {
|
||||
|
|
|
@ -396,20 +396,12 @@ func (s *remoteSite) dialAccessPoint(network, addr string) (net.Conn, error) {
|
|||
}
|
||||
|
||||
func (s *remoteSite) DialAuthServer() (conn net.Conn, err error) {
|
||||
// get list of remote auth servers
|
||||
authServers, err := s.remoteClient.GetAuthServers()
|
||||
conn, err = s.connThroughTunnel(chanTransportDialReq, RemoteAuthServer)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
for _, authServer := range authServers {
|
||||
conn, err = s.connThroughTunnel(chanTransportDialReq, authServer.GetAddr())
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, trace.ConnectionProblem(err, "unable to connect to auth server")
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Dial is used to connect a requesting client (say, tsh) to an SSH server
|
||||
|
@ -472,6 +464,9 @@ func (s *remoteSite) dialWithAgent(from, to net.Addr, userAgent agent.Agent) (ne
|
|||
SrcAddr: from,
|
||||
DstAddr: to,
|
||||
HostCertificate: hostCertificate,
|
||||
Ciphers: s.srv.Config.Ciphers,
|
||||
KEXAlgorithms: s.srv.Config.KEXAlgorithms,
|
||||
MACAlgorithms: s.srv.Config.MACAlgorithms,
|
||||
}
|
||||
remoteServer, err := forward.New(serverConfig)
|
||||
if err != nil {
|
||||
|
|
|
@ -121,6 +121,18 @@ type Config struct {
|
|||
// Clock is a clock used in the server, set up to
|
||||
// wall clock if not set
|
||||
Clock clockwork.Clock
|
||||
|
||||
// Ciphers is a list of ciphers that the server supports. If omitted,
|
||||
// the defaults will be used.
|
||||
Ciphers []string
|
||||
|
||||
// KEXAlgorithms is a list of key exchange (KEX) algorithms that the
|
||||
// server supports. If omitted, the defaults will be used.
|
||||
KEXAlgorithms []string
|
||||
|
||||
// MACAlgorithms is a list of message authentication codes (MAC) that
|
||||
// the server supports. If omitted the defaults will be used.
|
||||
MACAlgorithms []string
|
||||
}
|
||||
|
||||
// CheckAndSetDefaults checks parameters and sets default values
|
||||
|
@ -745,7 +757,7 @@ func newRemoteSite(srv *server, domainName string) (*remoteSite, error) {
|
|||
// certificate cache is created in each site (instead of creating it in
|
||||
// reversetunnel.server and passing it along) so that the host certificate
|
||||
// is signed by the correct certificate authority.
|
||||
certificateCache, err := NewHostCertificateCache(clt)
|
||||
certificateCache, err := NewHostCertificateCache(srv.localAuthClient)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -755,6 +755,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
|
|||
Client: conn.Client,
|
||||
},
|
||||
},
|
||||
Ciphers: cfg.Ciphers,
|
||||
KEXAlgorithms: cfg.KEXAlgorithms,
|
||||
MACAlgorithms: cfg.MACAlgorithms,
|
||||
})
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
|
|
@ -93,6 +93,7 @@ func NewAdminRole() Role {
|
|||
Options: RoleOptions{
|
||||
MaxSessionTTL: NewDuration(defaults.MaxCertDuration),
|
||||
PortForwarding: true,
|
||||
ForwardAgent: true,
|
||||
},
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
|
@ -140,6 +141,7 @@ func RoleForUser(u User) Role {
|
|||
Options: RoleOptions{
|
||||
MaxSessionTTL: NewDuration(defaults.MaxCertDuration),
|
||||
PortForwarding: true,
|
||||
ForwardAgent: true,
|
||||
},
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
|
|
|
@ -179,8 +179,8 @@ func (h *AuthHandlers) UserKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*s
|
|||
}
|
||||
h.Debugf("Successfully authenticated")
|
||||
|
||||
// see if the host user is valid (no need to do this in proxy mode)
|
||||
if !h.isProxy() {
|
||||
// see if the host user is valid, we only do this when logging into a teleport node
|
||||
if h.isTeleportNode() {
|
||||
_, err = user.Lookup(conn.User())
|
||||
if err != nil {
|
||||
host, _ := os.Hostname()
|
||||
|
@ -424,6 +424,14 @@ func (h *AuthHandlers) isProxy() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (h *AuthHandlers) isTeleportNode() bool {
|
||||
if h.Component == teleport.ComponentNode {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// extractRolesFromCert extracts roles from certificate metadata extensions.
|
||||
func extractRolesFromCert(cert *ssh.Certificate) ([]string, error) {
|
||||
data, ok := cert.Extensions[teleport.CertExtensionTeleportRoles]
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package forward
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/srv"
|
||||
"github.com/gravitational/teleport/lib/utils/proxy"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// newRemoteSession will create and return a *ssh.Client and *ssh.Session
|
||||
// with a remote host.
|
||||
func newRemoteSession(conn net.Conn, dstAddr string, systemLogin string, userAgent agent.Agent, authHandlers *srv.AuthHandlers) (*ssh.Client, *ssh.Session, error) {
|
||||
// the proxy will use the agent that has been forwarded to it as the auth
|
||||
// method when connecting to the remote host
|
||||
if userAgent == nil {
|
||||
return nil, nil, trace.AccessDenied("agent must be forwarded to proxy")
|
||||
}
|
||||
authMethod := ssh.PublicKeysCallback(userAgent.Signers)
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: systemLogin,
|
||||
Auth: []ssh.AuthMethod{
|
||||
authMethod,
|
||||
},
|
||||
HostKeyCallback: authHandlers.HostKeyAuth,
|
||||
Timeout: defaults.DefaultDialTimeout,
|
||||
}
|
||||
|
||||
client, err := proxy.NewClientConnWithDeadline(conn, dstAddr, clientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return client, session, nil
|
||||
}
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/gravitational/teleport/lib/srv"
|
||||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/teleport/lib/utils/proxy"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -69,10 +70,10 @@ type Server struct {
|
|||
clientConn net.Conn
|
||||
serverConn net.Conn
|
||||
|
||||
// agent is the SSH user agent that was forwarded to the proxy.
|
||||
agent agent.Agent
|
||||
// agentChannel is the channel over which communication with the agent occurs.
|
||||
agentChannel ssh.Channel
|
||||
// userAgent is the SSH user agent that was forwarded to the proxy.
|
||||
userAgent agent.Agent
|
||||
// userAgentChannel is the channel over which communication with the agent occurs.
|
||||
userAgentChannel ssh.Channel
|
||||
|
||||
// hostCertificate is the SSH host certificate this in-memory server presents
|
||||
// to the client.
|
||||
|
@ -90,6 +91,16 @@ type Server struct {
|
|||
// forwarding server.
|
||||
termHandlers *srv.TermHandlers
|
||||
|
||||
// ciphers is a list of ciphers that the server supports. If omitted,
|
||||
// the defaults will be used.
|
||||
ciphers []string
|
||||
// kexAlgorithms is a list of key exchange (KEX) algorithms that the
|
||||
// server supports. If omitted, the defaults will be used.
|
||||
kexAlgorithms []string
|
||||
// macAlgorithms is a list of message authentication codes (MAC) that
|
||||
// the server supports. If omitted the defaults will be used.
|
||||
macAlgorithms []string
|
||||
|
||||
authClient auth.ClientI
|
||||
auditLog events.IAuditLog
|
||||
authService auth.AccessPoint
|
||||
|
@ -106,6 +117,18 @@ type ServerConfig struct {
|
|||
SrcAddr net.Addr
|
||||
DstAddr net.Addr
|
||||
HostCertificate ssh.Signer
|
||||
|
||||
// Ciphers is a list of ciphers that the server supports. If omitted,
|
||||
// the defaults will be used.
|
||||
Ciphers []string
|
||||
|
||||
// KEXAlgorithms is a list of key exchange (KEX) algorithms that the
|
||||
// server supports. If omitted, the defaults will be used.
|
||||
KEXAlgorithms []string
|
||||
|
||||
// MACAlgorithms is a list of message authentication codes (MAC) that
|
||||
// the server supports. If omitted the defaults will be used.
|
||||
MACAlgorithms []string
|
||||
}
|
||||
|
||||
// CheckDefaults makes sure all required parameters are passed in.
|
||||
|
@ -163,7 +186,7 @@ func New(c ServerConfig) (*Server, error) {
|
|||
targetConn: c.TargetConn,
|
||||
serverConn: serverConn,
|
||||
clientConn: clientConn,
|
||||
agent: c.UserAgent,
|
||||
userAgent: c.UserAgent,
|
||||
hostCertificate: c.HostCertificate,
|
||||
authClient: c.AuthClient,
|
||||
auditLog: c.AuthClient,
|
||||
|
@ -282,7 +305,7 @@ func (s *Server) Serve() {
|
|||
|
||||
// build a remote session to the remote node
|
||||
s.log.Debugf("Creating remote connection to %v@%v", sconn.User(), s.clientConn.RemoteAddr().String())
|
||||
s.remoteClient, s.remoteSession, err = newRemoteSession(s.targetConn, s.targetConn.RemoteAddr().String(), sconn.User(), s.agent, s.authHandlers)
|
||||
s.remoteClient, s.remoteSession, err = s.newRemoteSession(sconn.User())
|
||||
if err != nil {
|
||||
// reject the connection with an error so the client doesn't hang then
|
||||
// close the connection
|
||||
|
@ -306,6 +329,49 @@ func (s *Server) Serve() {
|
|||
go s.handleConnection(ctx, sconn, identityContext, chans, reqs)
|
||||
}
|
||||
|
||||
// newRemoteSession will create and return a *ssh.Client and *ssh.Session
|
||||
// with a remote host.
|
||||
func (s *Server) newRemoteSession(systemLogin string) (*ssh.Client, *ssh.Session, error) {
|
||||
// the proxy will use the agent that has been forwarded to it as the auth
|
||||
// method when connecting to the remote host
|
||||
if s.userAgent == nil {
|
||||
return nil, nil, trace.AccessDenied("agent must be forwarded to proxy")
|
||||
}
|
||||
authMethod := ssh.PublicKeysCallback(s.userAgent.Signers)
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: systemLogin,
|
||||
Auth: []ssh.AuthMethod{
|
||||
authMethod,
|
||||
},
|
||||
HostKeyCallback: s.authHandlers.HostKeyAuth,
|
||||
Timeout: defaults.DefaultDialTimeout,
|
||||
}
|
||||
|
||||
if len(s.ciphers) > 0 {
|
||||
clientConfig.Ciphers = s.ciphers
|
||||
}
|
||||
if len(s.kexAlgorithms) > 0 {
|
||||
clientConfig.KeyExchanges = s.kexAlgorithms
|
||||
}
|
||||
if len(s.macAlgorithms) > 0 {
|
||||
clientConfig.MACs = s.macAlgorithms
|
||||
}
|
||||
|
||||
dstAddr := s.targetConn.RemoteAddr().String()
|
||||
client, err := proxy.NewClientConnWithDeadline(s.targetConn, dstAddr, clientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return client, session, nil
|
||||
}
|
||||
|
||||
func (s *Server) handleConnection(ctx context.Context, sconn *ssh.ServerConn, identityContext srv.IdentityContext, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
|
||||
for {
|
||||
select {
|
||||
|
@ -428,7 +494,7 @@ func (s *Server) handleDirectTCPIPRequest(sconn *ssh.ServerConn, identityContext
|
|||
|
||||
ctx.RemoteClient = s.remoteClient
|
||||
ctx.RemoteSession = s.remoteSession
|
||||
ctx.SetAgent(s.agent, s.agentChannel)
|
||||
ctx.SetAgent(s.userAgent, s.userAgentChannel)
|
||||
|
||||
ctx.AddCloser(ch)
|
||||
ctx.AddCloser(sconn)
|
||||
|
@ -507,7 +573,7 @@ func (s *Server) handleSessionRequests(sconn *ssh.ServerConn, identityContext sr
|
|||
|
||||
ctx.RemoteClient = s.remoteClient
|
||||
ctx.RemoteSession = s.remoteSession
|
||||
ctx.SetAgent(s.agent, s.agentChannel)
|
||||
ctx.SetAgent(s.userAgent, s.userAgentChannel)
|
||||
|
||||
ctx.AddCloser(ch)
|
||||
ctx.AddCloser(sconn)
|
||||
|
|
|
@ -86,7 +86,12 @@ const teleportTestUser = "teleport-test"
|
|||
var _ = Suite(&SrvSuite{})
|
||||
|
||||
func (s *SrvSuite) SetUpSuite(c *C) {
|
||||
var err error
|
||||
|
||||
utils.InitLoggerForTests()
|
||||
|
||||
s.freePorts, err = utils.GetFreeTCPPorts(100)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
const hostID = "00000000-0000-0000-0000-000000000000"
|
||||
|
@ -103,9 +108,6 @@ func (s *SrvSuite) SetUpTest(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
s.user = u.Username
|
||||
|
||||
s.freePorts, err = utils.GetFreeTCPPorts(10)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.bk, err = dir.New(backend.Params{"path": s.dir})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
|
@ -174,8 +176,7 @@ func (s *SrvSuite) SetUpTest(c *C) {
|
|||
s.signer, err = sshutils.NewSigner(hpriv, hcert)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.srvPort = s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
s.srvPort = s.freePorts.Pop()
|
||||
s.srvAddress = "127.0.0.1:" + s.srvPort
|
||||
|
||||
s.srvHostPort = fmt.Sprintf("%v:%v", s.domainName, s.srvPort)
|
||||
|
@ -237,10 +238,6 @@ func (s *SrvSuite) TestAdvertiseAddr(c *C) {
|
|||
// TestAgentForwardPermission makes sure if RBAC rules don't allow agent
|
||||
// forwarding, we don't start an agent even if requested.
|
||||
func (s *SrvSuite) TestAgentForwardPermission(c *C) {
|
||||
se, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
defer se.Close()
|
||||
|
||||
// make sure the role does not allow agent forwarding
|
||||
roleName := services.RoleNameForUser(s.user)
|
||||
role, err := s.a.GetRole(roleName)
|
||||
|
@ -251,6 +248,10 @@ func (s *SrvSuite) TestAgentForwardPermission(c *C) {
|
|||
err = s.a.UpsertRole(role, backend.Forever)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
se, err := s.clt.NewSession()
|
||||
c.Assert(err, IsNil)
|
||||
defer se.Close()
|
||||
|
||||
// to interoperate with OpenSSH, requests for agent forwarding always succeed.
|
||||
// however that does not mean the users agent will actually be forwarded.
|
||||
err = agent.RequestAgentForwarding(se)
|
||||
|
@ -497,8 +498,7 @@ func (s *SrvSuite) testClient(c *C, proxyAddr, targetAddr, remoteAddr string, ss
|
|||
func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
||||
log.Infof("[TEST START] TestProxyReverseTunnel")
|
||||
|
||||
reverseTunnelPort := s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
reverseTunnelPort := s.freePorts.Pop()
|
||||
reverseTunnelAddress := utils.NetAddr{AddrNetwork: "tcp", Addr: fmt.Sprintf("%v:%v", s.domainName, reverseTunnelPort)}
|
||||
reverseTunnelServer, err := reversetunnel.NewServer(reversetunnel.Config{
|
||||
ID: s.domainName,
|
||||
|
@ -587,8 +587,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
|||
s.testClient(c, proxy.Addr(), s.srvHostPort, s.srv.Addr(), sshConfig)
|
||||
|
||||
// adding new node
|
||||
bobAddr := "127.0.0.1:" + s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
bobAddr := "127.0.0.1:" + s.freePorts.Pop()
|
||||
srv2, err := New(
|
||||
utils.NetAddr{AddrNetwork: "tcp", Addr: bobAddr},
|
||||
"bob",
|
||||
|
@ -666,8 +665,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
|||
func (s *SrvSuite) TestProxyRoundRobin(c *C) {
|
||||
log.Infof("[TEST START] TestProxyRoundRobin")
|
||||
|
||||
reverseTunnelPort := s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
reverseTunnelPort := s.freePorts.Pop()
|
||||
reverseTunnelAddress := utils.NetAddr{
|
||||
AddrNetwork: "tcp",
|
||||
Addr: fmt.Sprintf("%v:%v", s.domainName, reverseTunnelPort),
|
||||
|
@ -894,8 +892,7 @@ func (s *SrvSuite) TestLimiter(c *C) {
|
|||
)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
srvAddress := "127.0.0.1:" + s.freePorts[len(s.freePorts)-1]
|
||||
s.freePorts = s.freePorts[:len(s.freePorts)-1]
|
||||
srvAddress := "127.0.0.1:" + s.freePorts.Pop()
|
||||
srv, err := New(
|
||||
utils.NetAddr{AddrNetwork: "tcp", Addr: srvAddress},
|
||||
s.domainName,
|
||||
|
|
|
@ -122,25 +122,11 @@ func (p *PortList) Pop() string {
|
|||
return val
|
||||
}
|
||||
|
||||
// GetFreeTCPPorts returns a lit of available ports on localhost
|
||||
// used for testing
|
||||
// GetFreeTCPPorts returns n ports starting from port 20000.
|
||||
func GetFreeTCPPorts(n int) (PortList, error) {
|
||||
list := make(PortList, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
tcpAddr, ok := listener.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, trace.Errorf("Can't get tcp address")
|
||||
}
|
||||
list = append(list, strconv.Itoa(tcpAddr.Port))
|
||||
for i := 20000; i < 20000+n; i++ {
|
||||
list = append(list, strconv.Itoa(i))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue