mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
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:
committed by
Russell Jones
parent
152d626a1d
commit
6f96595e99
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user