mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
235 lines
6.9 KiB
Go
235 lines
6.9 KiB
Go
/*
|
|
Copyright 2015-2018 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 session is used for bookkeeping of SSH interactive sessions
|
|
// that happen in realtime across the teleport cluster
|
|
package session
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gravitational/trace"
|
|
"github.com/moby/term"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
)
|
|
|
|
// ID is a unique session ID.
|
|
type ID string
|
|
|
|
// IsZero returns true if this ID is empty.
|
|
func (s *ID) IsZero() bool {
|
|
return len(*s) == 0
|
|
}
|
|
|
|
// String returns string representation of this ID.
|
|
func (s *ID) String() string {
|
|
return string(*s)
|
|
}
|
|
|
|
// Check will check that the underlying UUID is valid.
|
|
func (s *ID) Check() error {
|
|
_, err := ParseID(string(*s))
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
// ParseID parses ID and checks if it's correct.
|
|
func ParseID(id string) (*ID, error) {
|
|
_, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("%v not a valid UUID", id)
|
|
}
|
|
uid := ID(id)
|
|
return &uid, nil
|
|
}
|
|
|
|
// NewID returns new session ID. The session ID is based on UUIDv4.
|
|
func NewID() ID {
|
|
return ID(uuid.New().String())
|
|
}
|
|
|
|
// Session is an interactive collaboration session that represents one
|
|
// or many sessions started by the teleport user.
|
|
type Session struct {
|
|
// Kind describes what kind of session this is e.g. ssh or kubernetes.
|
|
Kind types.SessionKind `json:"kind"`
|
|
// ID is a unique session identifier
|
|
ID ID `json:"id"`
|
|
// Namespace is a session namespace, separating sessions from each other
|
|
Namespace string `json:"namespace"`
|
|
// Parties is a list of session parties.
|
|
Parties []Party `json:"parties"`
|
|
// TerminalParams sets terminal properties
|
|
TerminalParams TerminalParams `json:"terminal_params"`
|
|
// Login is a login used by all parties joining the session
|
|
Login string `json:"login"`
|
|
// Created records the information about the time when session
|
|
// was created
|
|
Created time.Time `json:"created"`
|
|
// LastActive holds the information about when the session
|
|
// was last active
|
|
LastActive time.Time `json:"last_active"`
|
|
// ServerID of session
|
|
ServerID string `json:"server_id"`
|
|
// ServerHostname of session
|
|
ServerHostname string `json:"server_hostname"`
|
|
// ServerHostPort of session
|
|
ServerHostPort int `json:"server_hostport"`
|
|
// ServerAddr of session
|
|
ServerAddr string `json:"server_addr"`
|
|
// ClusterName is the name of the Teleport cluster that this session belongs to.
|
|
ClusterName string `json:"cluster_name"`
|
|
// KubernetesClusterName is the name of the kube cluster that this session is running in.
|
|
KubernetesClusterName string `json:"kubernetes_cluster_name"`
|
|
// DesktopName is the name of the desktop that this session is running in.
|
|
DesktopName string `json:"desktop_name"`
|
|
// DatabaseName is the name of the database being accessed.
|
|
DatabaseName string `json:"database_name"`
|
|
// AppName is the name of the app being accessed.
|
|
AppName string `json:"app_name"`
|
|
// Owner is the name of the session owner, ie the one who created the session.
|
|
Owner string `json:"owner"`
|
|
}
|
|
|
|
// Participants returns the usernames of the current session participants.
|
|
func (s *Session) Participants() []string {
|
|
participants := make([]string, 0, len(s.Parties))
|
|
for _, p := range s.Parties {
|
|
participants = append(participants, p.User)
|
|
}
|
|
return participants
|
|
}
|
|
|
|
// RemoveParty helper allows to remove a party by it's ID from the
|
|
// session's list. Returns 'false' if pid couldn't be found
|
|
func (s *Session) RemoveParty(pid ID) bool {
|
|
for i := range s.Parties {
|
|
if s.Parties[i].ID == pid {
|
|
s.Parties = append(s.Parties[:i], s.Parties[i+1:]...)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Party is a participant a user or a script executing some action
|
|
// in the context of the session
|
|
type Party struct {
|
|
// ID is a unique party id
|
|
ID ID `json:"id"`
|
|
// Site is a remote address?
|
|
RemoteAddr string `json:"remote_addr"`
|
|
// User is a teleport user using this session
|
|
User string `json:"user"`
|
|
// ServerID is an address of the server
|
|
ServerID string `json:"server_id"`
|
|
// LastActive is a last time this party was active
|
|
LastActive time.Time `json:"last_active"`
|
|
}
|
|
|
|
// String returns debug friendly representation
|
|
func (p *Party) String() string {
|
|
return fmt.Sprintf(
|
|
"party(id=%v, remote=%v, user=%v, server=%v, last_active=%v)",
|
|
p.ID, p.RemoteAddr, p.User, p.ServerID, p.LastActive,
|
|
)
|
|
}
|
|
|
|
// TerminalParams holds the terminal size in a session.
|
|
type TerminalParams struct {
|
|
W int `json:"w"`
|
|
H int `json:"h"`
|
|
}
|
|
|
|
// UnmarshalTerminalParams takes a serialized string that contains the
|
|
// terminal parameters and returns a *TerminalParams.
|
|
func UnmarshalTerminalParams(s string) (*TerminalParams, error) {
|
|
parts := strings.Split(s, ":")
|
|
if len(parts) != 2 {
|
|
return nil, trace.BadParameter("failed to unmarshal: too many parts")
|
|
}
|
|
|
|
w, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
h, err := strconv.Atoi(parts[1])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return &TerminalParams{
|
|
W: w,
|
|
H: h,
|
|
}, nil
|
|
}
|
|
|
|
// Serialize is a more strict version of String(): it returns a string
|
|
// representation of terminal size, this is used in our APIs.
|
|
// Format : "W:H"
|
|
// Example: "80:25"
|
|
func (p *TerminalParams) Serialize() string {
|
|
return fmt.Sprintf("%d:%d", p.W, p.H)
|
|
}
|
|
|
|
// String returns debug friendly representation of terminal
|
|
func (p *TerminalParams) String() string {
|
|
return fmt.Sprintf("TerminalParams(w=%v, h=%v)", p.W, p.H)
|
|
}
|
|
|
|
// Winsize returns low-level parameters for changing PTY
|
|
func (p *TerminalParams) Winsize() *term.Winsize {
|
|
return &term.Winsize{
|
|
Width: uint16(p.W),
|
|
Height: uint16(p.H),
|
|
}
|
|
}
|
|
|
|
// MaxSessionSliceLength is the maximum number of sessions per time window
|
|
// that the backend will return.
|
|
const MaxSessionSliceLength = 1000
|
|
|
|
// NewTerminalParamsFromUint32 returns new terminal parameters from uint32 width and height
|
|
func NewTerminalParamsFromUint32(w uint32, h uint32) (*TerminalParams, error) {
|
|
if w > maxSize || w < minSize {
|
|
return nil, trace.BadParameter("bad width")
|
|
}
|
|
if h > maxSize || h < minSize {
|
|
return nil, trace.BadParameter("bad height")
|
|
}
|
|
return &TerminalParams{W: int(w), H: int(h)}, nil
|
|
}
|
|
|
|
// NewTerminalParamsFromInt returns new terminal parameters from int width and height
|
|
func NewTerminalParamsFromInt(w int, h int) (*TerminalParams, error) {
|
|
if w > maxSize || w < minSize {
|
|
return nil, trace.BadParameter("bad witdth")
|
|
}
|
|
if h > maxSize || h < minSize {
|
|
return nil, trace.BadParameter("bad height")
|
|
}
|
|
return &TerminalParams{W: w, H: h}, nil
|
|
}
|
|
|
|
const (
|
|
minSize = 1
|
|
maxSize = 4096
|
|
)
|