Skip to content

Commit

Permalink
Send UUIDv1 session IDs to legacy servers.
Browse files Browse the repository at this point in the history
Before establishing a session, request the server version. If the server
replies false, that means it does not support that request type and is
an older server version which needs UUIDv1 format session IDs.
  • Loading branch information
russjones committed Sep 10, 2019
1 parent 36838e5 commit 98d65b6
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 6 deletions.
4 changes: 4 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ const (
// SessionEvent is sent by servers to clients when an audit event occurs on
// the session.
SessionEvent = "x-teleport-event"

// VersionRequest is sent by clients to server requesting the Teleport
// version they are running.
VersionRequest = "x-teleport-version"
)

const (
Expand Down
47 changes: 45 additions & 2 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@ func (tc *TeleportClient) runCommand(
if len(nodeAddresses) > 1 {
fmt.Printf("Running command on %v:\n", address)
}
nodeSession, err = newSession(nodeClient, nil, tc.Config.Env, tc.Stdin, tc.Stdout, tc.Stderr)
nodeSession, err = newSession(nodeClient, nil, tc.Config.Env, tc.Stdin, tc.Stdout, tc.Stderr, tc.useLegacyID(nodeClient))
if err != nil {
log.Error(err)
return
Expand Down Expand Up @@ -1424,7 +1424,7 @@ func (tc *TeleportClient) runCommand(
// runShell starts an interactive SSH session/shell.
// sessionID : when empty, creates a new shell. otherwise it tries to join the existing session.
func (tc *TeleportClient) runShell(nodeClient *NodeClient, sessToJoin *session.Session) error {
nodeSession, err := newSession(nodeClient, sessToJoin, tc.Env, tc.Stdin, tc.Stdout, tc.Stderr)
nodeSession, err := newSession(nodeClient, sessToJoin, tc.Env, tc.Stdin, tc.Stdout, tc.Stderr, tc.useLegacyID(nodeClient))
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -2032,6 +2032,49 @@ func (tc *TeleportClient) AskPassword() (pwd string, err error) {
return pwd, nil
}

// DELETE IN: 4.1.0
//
// useLegacyID returns true if an old style (UUIDv1) session ID should be
// generated because the client is talking with a older server.
func (tc *TeleportClient) useLegacyID(nodeClient *NodeClient) bool {
_, err := tc.getServerVersion(nodeClient)
if trace.IsNotFound(err) {
return true
}
return false
}

type serverResponse struct {
version string
err error
}

// getServerVersion makes a SSH global request to the server to request the
// version.
func (tc *TeleportClient) getServerVersion(nodeClient *NodeClient) (string, error) {
responseCh := make(chan serverResponse)

go func() {
ok, payload, err := nodeClient.Client.SendRequest(teleport.VersionRequest, true, nil)
if err != nil {
responseCh <- serverResponse{err: trace.NotFound(err.Error())}
} else if !ok {
responseCh <- serverResponse{err: trace.NotFound("server does not support version request")}
}
responseCh <- serverResponse{version: string(payload)}
}()

select {
case resp := <-responseCh:
if resp.err != nil {
return "", trace.Wrap(resp.err)
}
return resp.version, nil
case <-time.After(500 * time.Millisecond):
return "", trace.NotFound("timed out waiting for server response")
}
}

// passwordFromConsole reads from stdin without echoing typed characters to stdout
func passwordFromConsole() (string, error) {
fd := syscall.Stdin
Expand Down
4 changes: 2 additions & 2 deletions lib/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (s *ClientTestSuite) TestNewSession(c *check.C) {
}

// defaults:
ses, err := newSession(nc, nil, nil, nil, nil, nil)
ses, err := newSession(nc, nil, nil, nil, nil, nil, false)
c.Assert(err, check.IsNil)
c.Assert(ses, check.NotNil)
c.Assert(ses.NodeClient(), check.Equals, nc)
Expand All @@ -70,7 +70,7 @@ func (s *ClientTestSuite) TestNewSession(c *check.C) {
env := map[string]string{
sshutils.SessionEnvVar: "session-id",
}
ses, err = newSession(nc, nil, env, nil, nil, nil)
ses, err = newSession(nc, nil, env, nil, nil, nil, false)
c.Assert(err, check.IsNil)
c.Assert(ses, check.NotNil)
c.Assert(ses.env, check.DeepEquals, env)
Expand Down
13 changes: 11 additions & 2 deletions lib/client/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func newSession(client *NodeClient,
env map[string]string,
stdin io.Reader,
stdout io.Writer,
stderr io.Writer) (*NodeSession, error) {
stderr io.Writer,
legacyID bool) (*NodeSession, error) {

if stdin == nil {
stdin = os.Stdin
Expand Down Expand Up @@ -119,7 +120,15 @@ func newSession(client *NodeClient,
} else {
sid, ok := ns.env[sshutils.SessionEnvVar]
if !ok {
sid = string(session.NewID())
// DELETE IN: 4.1.0.
//
// Always send UUIDv4 after 4.1.
if legacyID {
sid = string(session.NewLegacyID())
} else {
sid = string(session.NewID())
}

}
ns.id = session.ID(sid)
}
Expand Down
7 changes: 7 additions & 0 deletions lib/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ func NewID() ID {
return ID(uuid.New())
}

// DELETE IN: 4.1.0.
//
// NewLegacyID creates a new session ID in the UUIDv1 legacy format.
func NewLegacyID() ID {
return ID(uuid.NewUUID().String())
}

// Session is an interactive collaboration session that represents one
// or many SSH session started by teleport user
type Session struct {
Expand Down
10 changes: 10 additions & 0 deletions lib/srv/forward/sshserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,16 @@ func (s *Server) rejectChannel(chans <-chan ssh.NewChannel, err error) {
}

func (s *Server) handleGlobalRequest(req *ssh.Request) {
// Version requests are internal Teleport requests, they should not be
// forwarded to the remote server.
if req.Type == teleport.VersionRequest {
err := req.Reply(true, []byte(teleport.Version))
if err != nil {
s.log.Debugf("Failed to reply to version request: %v.", err)
}
return
}

ok, payload, err := s.remoteClient.SendRequest(req.Type, req.WantReply, req.Payload)
if err != nil {
s.log.Warnf("Failed to forward global request %v: %v", req.Type, err)
Expand Down
10 changes: 10 additions & 0 deletions lib/srv/regular/sshserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,8 @@ func (s *Server) HandleRequest(r *ssh.Request) {
s.handleKeepAlive(r)
case teleport.RecordingProxyReqType:
s.handleRecordingProxy(r)
case teleport.VersionRequest:
s.handleVersionRequest(r)
default:
if r.WantReply {
r.Reply(false, nil)
Expand Down Expand Up @@ -1273,6 +1275,14 @@ func (s *Server) handleRecordingProxy(req *ssh.Request) {
log.Debugf("Replied to global request (%v, %v): %v", req.Type, req.WantReply, recordingProxy)
}

// handleVersionRequest replies with the Teleport version of the server.
func (s *Server) handleVersionRequest(req *ssh.Request) {
err := req.Reply(true, []byte(teleport.Version))
if err != nil {
log.Debugf("Failed to reply to version request: %v.", err)
}
}

// handleProxyJump handles ProxyJump request that is executed via direct tcp-ip dial on the proxy
func (s *Server) handleProxyJump(conn net.Conn, sconn *ssh.ServerConn, identityContext srv.IdentityContext, ch ssh.Channel, req sshutils.DirectTCPIPReq) {
// Create context for this channel. This context will be closed when the
Expand Down

0 comments on commit 98d65b6

Please sign in to comment.