Skip to content
New issue

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

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

Already on GitHub? # to your account

Emit session end event when completer finishes upload #6756

Merged
merged 10 commits into from
May 11, 2021
3 changes: 3 additions & 0 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ const (
// session occurred on.
SessionServerLabels = "server_labels"

// SessionClusterName is the cluster name that the session occurred in
SessionClusterName = "cluster_name"

// SessionByteOffset is the number of bytes written to session stream since
// the beginning
SessionByteOffset = "offset"
Expand Down
97 changes: 97 additions & 0 deletions lib/events/complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"

"github.com/gravitational/trace"

"github.com/jonboulle/clockwork"
log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -140,6 +143,10 @@ func (u *UploadCompleter) CheckUploads(ctx context.Context) error {
u.log.Debugf("Completed upload %v.", upload)
completed++
uploadData := u.cfg.Uploader.GetUploadMetadata(upload.SessionID)
err = u.ensureSessionEndEvent(ctx, uploadData)
if err != nil {
return trace.Wrap(err)
}
session := &events.SessionUpload{
Metadata: Metadata{
Type: SessionUploadEvent,
Expand Down Expand Up @@ -167,3 +174,93 @@ func (u *UploadCompleter) Close() error {
u.cancel()
return nil
}

func (u *UploadCompleter) ensureSessionEndEvent(ctx context.Context, uploadData UploadMetadata) error {
var serverID, clusterName, user, login, hostname, namespace, serverAddr string
var interactive bool

// Get session events to find fields for constructed session end
sessionEvents, err := u.cfg.AuditLog.GetSessionEvents(defaults.Namespace, uploadData.SessionID, 0, false)
if err != nil {
return trace.Wrap(err)
}
if len(sessionEvents) == 0 {
return nil
}

// Return if session.end event already exists
for _, event := range sessionEvents {
if event.GetType() == SessionEndEvent {
return nil
}
}

// Session start event is the first of session events
sessionStart := sessionEvents[0]
if sessionStart.GetType() != SessionStartEvent {
return trace.BadParameter("invalid session, session start is not the first event")
}

// Set variables
serverID = sessionStart.GetString(SessionServerHostname)
clusterName = sessionStart.GetString(SessionClusterName)
hostname = sessionStart.GetString(SessionServerHostname)
namespace = sessionStart.GetString(EventNamespace)
serverAddr = sessionStart.GetString(SessionServerAddr)
user = sessionStart.GetString(EventUser)
login = sessionStart.GetString(EventLogin)
if terminalSize := sessionStart.GetString(TerminalSize); terminalSize != "" {
interactive = true
}

// Get last event to get session end time
lastEvent := sessionEvents[len(sessionEvents)-1]

participants := getParticipants(sessionEvents)

sessionEndEvent := &events.SessionEnd{
Metadata: events.Metadata{
Type: SessionEndEvent,
Code: SessionEndCode,
ClusterName: clusterName,
},
ServerMetadata: events.ServerMetadata{
ServerID: serverID,
ServerNamespace: namespace,
ServerHostname: hostname,
ServerAddr: serverAddr,
},
SessionMetadata: events.SessionMetadata{
SessionID: string(uploadData.SessionID),
},
UserMetadata: events.UserMetadata{
User: user,
Login: login,
},
Participants: participants,
Interactive: interactive,
StartTime: sessionStart.GetTime(EventTime),
EndTime: lastEvent.GetTime(EventTime),
}

// Check and set event fields
if err = checkAndSetEventFields(sessionEndEvent, u.cfg.Clock, utils.NewRealUID(), clusterName); err != nil {
return trace.Wrap(err)
}
if err = u.cfg.AuditLog.EmitAuditEvent(ctx, sessionEndEvent); err != nil {
return trace.Wrap(err)
}
return nil
}

func getParticipants(sessionEvents []EventFields) []string {
var participants []string
for _, event := range sessionEvents {
if event.GetType() == SessionJoinEvent || event.GetType() == SessionStartEvent {
participant := event.GetString(EventUser)
participants = append(participants, participant)

}
}
return utils.Deduplicate(participants)
}