Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send UUIDv1 session IDs to legacy servers. #2969

Merged
merged 1 commit into from
Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
russjones marked this conversation as resolved.
Show resolved Hide resolved
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