Send UUIDv1 session IDs to legacy servers.

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.
This commit is contained in:
Russell Jones
2019-09-11 13:50:00 -07:00
committed by Russell Jones
parent 152d626a1d
commit 6f96595e99
7 changed files with 89 additions and 6 deletions
+4
View File
@@ -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 (
+45 -2
View File
@@ -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
@@ -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)
}
@@ -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
+2 -2
View File
@@ -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)
@@ -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)
+11 -2
View File
@@ -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
@@ -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)
}
+7
View File
@@ -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 {
+10
View File
@@ -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)
+10
View File
@@ -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)
@@ -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