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

Displaying mode and controls to additional participants #46450

Merged
merged 45 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d6b4b65
Displaying mode and controls to additional participants
mvbrock Sep 10, 2024
88b01a6
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 10, 2024
53b4708
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 11, 2024
cae1e58
Moving SessionControlsInfoBroadcast over to kube/proxy
mvbrock Sep 12, 2024
6c2982a
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 12, 2024
612d574
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 16, 2024
d6eea0b
Transitioning to consistent proxy-emitted mode+controls
mvbrock Sep 17, 2024
c5fa24f
Moving message broadcast so new participant wont see it
mvbrock Sep 17, 2024
c173ccc
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 17, 2024
2838c41
Possible unit test fix (cant seem to test locally)
mvbrock Sep 17, 2024
b304f3d
Fixed unit test
mvbrock Sep 17, 2024
b403553
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 17, 2024
ec01f11
Adding a line break before messaging the participant
mvbrock Sep 18, 2024
c471c2c
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 18, 2024
f7b895d
Linter errors
mvbrock Sep 18, 2024
b66ad27
Emitting audit event and controls message for additional parties, i.e…
mvbrock Sep 19, 2024
1b7cfbd
Revert "Emitting audit event and controls message for additional part…
mvbrock Sep 19, 2024
5fa8f70
Add User Tasks resource - protos (#46059)
marcoandredinis Sep 18, 2024
ce6a269
Add notice to web UI that users arent equal to MAU (#46686)
avatus Sep 18, 2024
32efcae
Clarify TLS requirements in the Jira guide (#46484)
ptgott Sep 18, 2024
b8cf72b
Remove TXT record validation of custom DNS zones in VNet (#46709)
ravicious Sep 18, 2024
567cf90
docs: mention the --days flag when executing an audit log query (#45764)
djohns7 Sep 18, 2024
4849fc2
Remove access-graph path resolution, proxy `/enterprise` requests (#4…
ryanclark Sep 18, 2024
36af69c
update e ref (#46726)
nklaassen Sep 18, 2024
74a1e46
fix: tolerate mismatched key PEM headers (#46725)
nklaassen Sep 18, 2024
b357f9b
Use dynamic base path for favicon images (#46719)
avatus Sep 18, 2024
dca5f57
Add Datadog Incident Management plugin support (#46271)
bernardjkim Sep 18, 2024
5099b02
Add AutoUpdate Client/Cache implementation (#46661)
vapopov Sep 19, 2024
98177f3
User Tasks: services and clients implementation (#46131)
marcoandredinis Sep 19, 2024
ad60933
expanding testplan for host user creation (#46729)
eriktate Sep 19, 2024
91f70ff
Fix operator docs reference generator bug (#46732)
ptgott Sep 19, 2024
ec76c7a
[auto] Update AMI IDs for 16.4.0 (#46746)
teleport-post-release-automation[bot] Sep 19, 2024
c8d4563
Remove deprecated HTTP RemoteCluster endpoints (#46756)
strideynet Sep 19, 2024
2881f07
Add `tbot` helm chart to `version.mk` (#46763)
strideynet Sep 19, 2024
a5ec9d0
Remove LockConfiguration.LockName (#46772)
rosstimothy Sep 19, 2024
c76965e
adding a reference to to the host user guide (#46765)
eriktate Sep 19, 2024
cb6d927
Replace more Logrus usage with Slog (#46757)
strideynet Sep 19, 2024
067064e
Update AWS roles ARNs displayed on `tsh app login` for AWS console ap…
gabrielcorado Sep 19, 2024
48fae88
bulk audit event export api (#46399)
fspmarshall Sep 19, 2024
cd1d735
Reverting back to using the emitSessionJoin boolean
mvbrock Sep 19, 2024
88ad804
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 19, 2024
b02528d
Merge remote-tracking branch 'origin/master' into mvbrock/display-con…
mvbrock Sep 19, 2024
34d6adc
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 19, 2024
071190c
Nits and removing a debug log
mvbrock Sep 20, 2024
0eab2ce
Merge branch 'master' into mvbrock/display-controls-on-session-join
mvbrock Sep 20, 2024
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
2 changes: 0 additions & 2 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2165,8 +2165,6 @@ func (tc *TeleportClient) Join(ctx context.Context, mode types.SessionParticipan
}
}

fmt.Printf("Joining session with participant mode: %v. \n\n", mode)

// running shell with a given session means "join" it:
err = nc.RunInteractiveShell(ctx, mode, session, tc.OnChannelRequest, beforeStart)
return trace.Wrap(err)
Expand Down
2 changes: 0 additions & 2 deletions lib/client/kubesession.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ func NewKubeSession(ctx context.Context, tc *TeleportClient, meta types.SessionT
TLSClientConfig: tlsConfig,
}

fmt.Printf("Joining session with participant mode: %v. \n\n", mode)

ws, resp, err := dialer.DialContext(ctx, joinEndpoint, nil)
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
Expand Down
6 changes: 2 additions & 4 deletions lib/kube/proxy/forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ func (f *Forwarder) join(ctx *authContext, w http.ResponseWriter, req *http.Requ
client := &websocketClientStreams{uuid.New(), stream}
party := newParty(*ctx, stream.Mode, client)

err = session.join(party, true /* emitSessionJoinEvent */)
mvbrock marked this conversation as resolved.
Show resolved Hide resolved
err = session.join(party, true)
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -1681,9 +1681,7 @@ func (f *Forwarder) exec(authCtx *authContext, w http.ResponseWriter, req *http.
}

f.setSession(session.id, session)
// When Teleport attaches the original session creator terminal streams to the
// session, we don't want to emit session.join event since it won't be required.
if err = session.join(party, false /* emitSessionJoinEvent */); err != nil {
if err = session.join(party, true); err != nil {
return trace.Wrap(err)
}

Expand Down
16 changes: 11 additions & 5 deletions lib/kube/proxy/sess.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,6 @@ func newSession(ctx authContext, forwarder *Forwarder, req *http.Request, params
s.io.OnReadError = s.disconnectPartyOnErr

s.BroadcastMessage("Creating session with ID: %v...", id.String())
s.BroadcastMessage(srv.SessionControlsInfoBroadcast)

go func() {
if _, open := <-s.io.TerminateNotifier(); open {
Expand Down Expand Up @@ -982,7 +981,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error {
return trace.Wrap(err)
}

// we only want to emit the session.join when someone tries to join a session via
// We only want to emit the session.join when someone tries to join a session via
// tsh kube join and not when the original session owner terminal streams are
// connected to the Kubernetes session.
if emitJoinEvent {
Expand All @@ -993,6 +992,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error {
if _, err := p.Client.stdoutStream().Write(recentWrites); err != nil {
s.log.Warnf("Failed to write history to client: %v.", err)
}
s.BroadcastMessage("User %v joined the session with participant mode: %v.", p.Ctx.User.GetName(), p.Mode)

// increment the party track waitgroup.
// It is decremented when session.leave() finishes its execution.
Expand All @@ -1017,10 +1017,17 @@ func (s *session) join(p *party, emitJoinEvent bool) error {
if p.Mode == types.SessionPeerMode {
s.io.AddReader(stringID, p.Client.stdinStream())
}

s.io.AddWriter(stringID, p.Client.stdoutStream())
s.BroadcastMessage("User %v joined the session with participant mode: %v.", p.Ctx.User.GetName(), p.Mode)
mvbrock marked this conversation as resolved.
Show resolved Hide resolved

// Send the participant mode and controls to the additional participant
if p.Ctx.User.GetName() != s.ctx.User.GetName() {
err := srv.MsgParticipantCtrls(p.Client.stdoutStream(), p.Mode)
if err != nil {
s.log.Errorf("Could not send intro message to participant: %v", err)
}
}

// Allow the moderator to force terminate the session
if p.Mode == types.SessionModeratorMode {
s.weakEventsWaiter.Add(1)
go func() {
Expand Down Expand Up @@ -1095,7 +1102,6 @@ func (s *session) join(p *party, emitJoinEvent bool) error {
s.log.Warnf("Failed to set tracker state to %v", types.SessionState_SessionStateRunning)
}
}

return nil
}

Expand Down
35 changes: 27 additions & 8 deletions lib/srv/sess.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package srv

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -66,10 +67,6 @@ const (
PresenceMaxDifference = time.Minute
)

// SessionControlsInfoBroadcast is sent in tandem with session creation
// to inform any joining users about the session controls.
const SessionControlsInfoBroadcast = "Controls\r\n - CTRL-C: Leave the session\r\n - t: Forcefully terminate the session (moderators only)"

const (
// sessionRecordingWarningMessage is sent when the session recording is
// going to be disabled.
Expand All @@ -86,6 +83,21 @@ var serverSessions = prometheus.NewGauge(
},
)

func MsgParticipantCtrls(w io.Writer, m types.SessionParticipantMode) error {
var modeCtrl bytes.Buffer
modeCtrl.WriteString(fmt.Sprintf("\r\nTeleport > Joining session with participant mode: %s\r\n", string(m)))
modeCtrl.WriteString("Teleport > Controls\r\n")
modeCtrl.WriteString("Teleport > - CTRL-C: Leave the session\r\n")
if m == types.SessionModeratorMode {
modeCtrl.WriteString("Teleport > - t: Forcefully terminate the session\r\n")
}
_, err := w.Write(modeCtrl.Bytes())
if err != nil {
return fmt.Errorf("could not write bytes: %w", err)
}
return nil
}

// SessionRegistry holds a map of all active sessions on a given
// SSH server
type SessionRegistry struct {
Expand Down Expand Up @@ -1291,7 +1303,6 @@ func (s *session) startInteractive(ctx context.Context, scx *ServerContext, p *p
s.io.AddReader("reader", inReader)
s.io.AddWriter(sessionRecorderID, utils.WriteCloserWithContext(scx.srv.Context(), s.Recorder()))
s.BroadcastMessage("Creating session with ID: %v", s.id)
s.BroadcastMessage(SessionControlsInfoBroadcast)
mvbrock marked this conversation as resolved.
Show resolved Hide resolved

if err := s.startTerminal(ctx, scx); err != nil {
return trace.Wrap(err)
Expand Down Expand Up @@ -1941,16 +1952,23 @@ func (s *session) addParty(p *party, mode types.SessionParticipantMode) error {
s.participants[p.id] = p
p.ctx.AddCloser(p)

// Write last chunk (so the newly joined parties won't stare at a blank
// screen).
// Write last chunk (so the newly joined parties won't stare at a blank screen).
if _, err := p.Write(s.io.GetRecentHistory()); err != nil {
return trace.Wrap(err)
}
s.BroadcastMessage("User %v joined the session with participant mode: %v.", p.user, p.mode)

// Register this party as one of the session writers (output will go to it).
s.io.AddWriter(string(p.id), p)

s.BroadcastMessage("User %v joined the session with participant mode: %v.", p.user, p.mode)
// Send the participant mode and controls to the additional participant
if s.login != p.login {
err := MsgParticipantCtrls(p.ch, mode)
if err != nil {
s.log.Errorf("Could not send intro message to participant: %v", err)
}
}

s.log.Infof("New party %v joined the session with participant mode: %v.", p.String(), p.mode)

if mode == types.SessionPeerMode {
Expand Down Expand Up @@ -2009,6 +2027,7 @@ func (s *session) join(ch ssh.Channel, scx *ServerContext, mode types.SessionPar

modes := s.access.CanJoin(accessContext)
if !slices.Contains(modes, mode) {
s.log.Infof("MODES ALLOWED: %v, MODE REQUESTED: %s, ROLES: %v", modes, mode, accessContext.Roles)
return trace.AccessDenied("insufficient permissions to join session %v", s.id)
}

Expand Down
6 changes: 3 additions & 3 deletions lib/web/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7774,7 +7774,7 @@ func waitForOutputWithDuration(r ReaderWithDeadline, substr string, timeout time
timeoutCh := time.After(timeout)

var prev string
out := make([]byte, int64(len(substr)*2))
out := make([]byte, int64(len(substr)*3))
for {
select {
case <-timeoutCh:
Expand Down Expand Up @@ -9860,7 +9860,7 @@ func TestModeratedSession(t *testing.T) {
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, peerTerm.Close()) })

require.NoError(t, waitForOutput(peerTerm, "Teleport > User foo joined the session with participant mode: peer."), "waiting for peer to enter session")
require.NoError(t, waitForOutput(peerTerm, "Teleport > Waiting for required participants..."), "waiting for peer to enter session")

moderatorTerm, err := connectToHost(ctx, connectConfig{
pack: s.authPack(t, "bar", moderatorRole.GetName()),
Expand Down Expand Up @@ -9978,7 +9978,7 @@ func TestModeratedSessionWithMFA(t *testing.T) {
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, peerTerm.Close()) })

require.NoError(t, waitForOutput(peerTerm, "Teleport > User foo joined the session with participant mode: peer."), "waiting for peer to start session")
require.NoError(t, waitForOutput(peerTerm, "Teleport > Waiting for required participants..."), "waiting for peer to start session")

moderatorTerm, err := connectToHost(ctx, connectConfig{
pack: moderator,
Expand Down
Loading