diff --git a/e b/e index fbb63ffef17c3..393cd15422b15 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit fbb63ffef17c3ff3bf3a1163d5aadb0c64e402fb +Subproject commit 393cd15422b155ce1b1f2fbe56ddb3e1b567136a diff --git a/integration/integration_test.go b/integration/integration_test.go index 7f5550ae53b92..9d7a1b89944b3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -53,6 +53,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/defaults" + apidefaults "github.com/gravitational/teleport/api/defaults" tracessh "github.com/gravitational/teleport/api/observability/tracing/ssh" "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/types" @@ -384,7 +385,7 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { require.NoError(t, err) // should have no sessions: - sessions, err := site.GetSessions(ctx, defaults.Namespace) + sessions, err := site.GetActiveSessionTrackers(ctx) require.NoError(t, err) require.Empty(t, sessions) @@ -408,27 +409,27 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { }() // wait until we've found the session in the audit log - getSession := func(site auth.ClientI) (*session.Session, error) { + getSession := func(site auth.ClientI) (types.SessionTracker, error) { timeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() sessions, err := waitForSessionToBeEstablished(timeout, defaults.Namespace, site) if err != nil { return nil, trace.Wrap(err) } - return &sessions[0], nil + return sessions[0], nil } - session, err := getSession(site) + tracker, err := getSession(site) require.NoError(t, err) - sessionID := session.ID + sessionID := tracker.GetSessionID() // wait for the user to join this session: - for len(session.Parties) == 0 { + for len(tracker.GetParticipants()) == 0 { time.Sleep(time.Millisecond * 5) - session, err = site.GetSession(ctx, defaults.Namespace, sessionID) + tracker, err = site.GetSessionTracker(ctx, tracker.GetSessionID()) require.NoError(t, err) } // make sure it's us who joined! :) - require.Equal(t, suite.Me.Username, session.Parties[0].User) + require.Equal(t, suite.Me.Username, tracker.GetParticipants()[0].User) // lets type "echo hi" followed by "enter" and then "exit" + "enter": @@ -447,15 +448,15 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { for { select { case event := <-teleport.UploadEventsC: - if event.SessionID != string(session.ID) { - t.Logf("Skipping mismatching session %v, expecting upload of %v.", event.SessionID, session.ID) + if event.SessionID != tracker.GetSessionID() { + t.Logf("Skipping mismatching session %v, expecting upload of %v.", event.SessionID, tracker.GetSessionID()) continue } break loop case <-timeoutC: dumpGoroutineProfile() t.Fatalf("%s: Timeout waiting for upload of session %v to complete to %v", - tt.comment, session.ID, tt.auditSessionsURI) + tt.comment, tracker.GetSessionID(), tt.auditSessionsURI) } } @@ -463,7 +464,7 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { // everything because the session is closing) var sessionStream []byte for i := 0; i < 6; i++ { - sessionStream, err = site.GetSessionChunk(defaults.Namespace, session.ID, 0, events.MaxChunkBytes) + sessionStream, err = site.GetSessionChunk(apidefaults.Namespace, session.ID(tracker.GetSessionID()), 0, events.MaxChunkBytes) require.NoError(t, err) if strings.Contains(string(sessionStream), "exit") { break @@ -495,7 +496,7 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { select { case <-tickCh: // Get all session events from the backend. - sessionEvents, err := site.GetSessionEvents(defaults.Namespace, session.ID, 0, false) + sessionEvents, err := site.GetSessionEvents(apidefaults.Namespace, session.ID(tracker.GetSessionID()), 0, false) if err != nil { return nil, trace.Wrap(err) } @@ -554,7 +555,7 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { start := findByType(events.SessionStartEvent) require.Equal(t, first, start) require.Equal(t, 0, start.GetInt("bytes")) - require.Equal(t, string(sessionID), start.GetString(events.SessionEventID)) + require.Equal(t, sessionID, start.GetString(events.SessionEventID)) require.NotEmpty(t, start.GetString(events.TerminalSize)) // make sure data is recorded properly @@ -570,13 +571,13 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { end := findByType(events.SessionEndEvent) require.NotNil(t, end) require.Equal(t, 0, end.GetInt("bytes")) - require.Equal(t, string(sessionID), end.GetString(events.SessionEventID)) + require.Equal(t, sessionID, end.GetString(events.SessionEventID)) // there should always be 'session.leave' event leave := findByType(events.SessionLeaveEvent) require.NotNil(t, leave) require.Equal(t, 0, leave.GetInt("bytes")) - require.Equal(t, string(sessionID), leave.GetString(events.SessionEventID)) + require.Equal(t, sessionID, leave.GetString(events.SessionEventID)) // all of them should have a proper time for _, e := range history { @@ -1256,7 +1257,7 @@ func verifySessionJoin(t *testing.T, username string, teleport *helpers.TeleInst return } - sessionID := string(sessions[0].ID) + sessionID := sessions[0].GetSessionID() cl, err := teleport.NewClient(helpers.ClientConfig{ Login: username, Cluster: helpers.Site, @@ -3811,7 +3812,7 @@ func testAuditOff(t *testing.T, suite *integrationTestSuite) { require.NotNil(t, site) // should have no sessions in it to start with - sessions, _ := site.GetSessions(ctx, defaults.Namespace) + sessions, _ := site.GetActiveSessionTrackers(ctx) require.Len(t, sessions, 0) // create interactive session (this goroutine is this user's terminal time) @@ -3837,16 +3838,16 @@ func testAuditOff(t *testing.T, suite *integrationTestSuite) { defer cancel() sessions, err = waitForSessionToBeEstablished(timeoutCtx, defaults.Namespace, site) require.NoError(t, err) - session := &sessions[0] + tracker := sessions[0] // wait for the user to join this session - for len(session.Parties) == 0 { + for len(tracker.GetParticipants()) == 0 { time.Sleep(time.Millisecond * 5) - session, err = site.GetSession(ctx, defaults.Namespace, sessions[0].ID) + tracker, err = site.GetSessionTracker(ctx, sessions[0].GetSessionID()) require.NoError(t, err) } // make sure it's us who joined! :) - require.Equal(t, suite.Me.Username, session.Parties[0].User) + require.Equal(t, suite.Me.Username, tracker.GetParticipants()[0].User) // lets type "echo hi" followed by "enter" and then "exit" + "enter": myTerm.Type("\aecho hi\n\r\aexit\n\r\a") @@ -3863,7 +3864,7 @@ func testAuditOff(t *testing.T, suite *integrationTestSuite) { // however, attempts to read the actual sessions should fail because it was // not actually recorded - _, err = site.GetSessionChunk(defaults.Namespace, session.ID, 0, events.MaxChunkBytes) + _, err = site.GetSessionChunk(apidefaults.Namespace, session.ID(tracker.GetSessionID()), 0, events.MaxChunkBytes) require.Error(t, err) } @@ -4697,7 +4698,7 @@ func testWindowChange(t *testing.T, suite *integrationTestSuite) { defer cancel() sessions, err := waitForSessionToBeEstablished(timeoutCtx, defaults.Namespace, site) require.NoError(t, err) - sessionID := string(sessions[0].ID) + sessionID := sessions[0].GetSessionID() cl, err := teleport.NewClient(helpers.ClientConfig{ Login: suite.Me.Username, diff --git a/integration/port_forwarding_test.go b/integration/port_forwarding_test.go index 28fcaae282597..3809ac3c603b6 100644 --- a/integration/port_forwarding_test.go +++ b/integration/port_forwarding_test.go @@ -31,7 +31,6 @@ import ( "github.com/gravitational/teleport/integration/helpers" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/client" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -49,7 +48,7 @@ func extractPort(svr *httptest.Server) (int, error) { return n, nil } -func waitForSessionToBeEstablished(ctx context.Context, namespace string, site auth.ClientI) ([]session.Session, error) { +func waitForSessionToBeEstablished(ctx context.Context, namespace string, site auth.ClientI) ([]types.SessionTracker, error) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() @@ -60,7 +59,7 @@ func waitForSessionToBeEstablished(ctx context.Context, namespace string, site a return nil, ctx.Err() case <-ticker.C: - ss, err := site.GetSessions(ctx, namespace) + ss, err := site.GetActiveSessionTrackers(ctx) if err != nil { return nil, trace.Wrap(err) } diff --git a/integration/utmp_integration_test.go b/integration/utmp_integration_test.go index abd536ae0c23f..acc63efcad353 100644 --- a/integration/utmp_integration_test.go +++ b/integration/utmp_integration_test.go @@ -268,7 +268,6 @@ func newSrvCtx(ctx context.Context, t *testing.T) *SrvCtx { regular.SetNamespace(apidefaults.Namespace), regular.SetEmitter(s.nodeClient), regular.SetShell("/bin/sh"), - regular.SetSessionServer(s.nodeClient), regular.SetPAMConfig(&pam.Config{Enabled: false}), regular.SetLabels( map[string]string{"foo": "bar"}, diff --git a/lib/auth/apiserver.go b/lib/auth/apiserver.go index 61b9852aad5d3..7cbaba7ec4735 100644 --- a/lib/auth/apiserver.go +++ b/lib/auth/apiserver.go @@ -45,7 +45,6 @@ import ( type APIConfig struct { PluginRegistry plugin.Registry AuthServer *Server - SessionService session.Service AuditLog events.IAuditLog Authorizer Authorizer Emitter apievents.Emitter @@ -154,11 +153,6 @@ func NewAPIServer(config *APIConfig) (http.Handler, error) { srv.POST("/:version/tokens/register", srv.withAuth(srv.registerUsingToken)) // Active sessions - srv.POST("/:version/namespaces/:namespace/sessions", srv.withAuth(srv.createSession)) - srv.PUT("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.updateSession)) - srv.DELETE("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.deleteSession)) - srv.GET("/:version/namespaces/:namespace/sessions", srv.withAuth(srv.getSessions)) - srv.GET("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.getSession)) srv.GET("/:version/namespaces/:namespace/sessions/:id/stream", srv.withAuth(srv.getSessionChunk)) srv.GET("/:version/namespaces/:namespace/sessions/:id/events", srv.withAuth(srv.getSessionEvents)) @@ -229,7 +223,6 @@ func (s *APIServer) withAuth(handler HandlerWithAuthFunc) httprouter.Handle { auth := &ServerWithRoles{ authServer: s.AuthServer, context: *authContext, - sessions: s.SessionService, alog: s.AuthServer, } version := p.ByName("version") @@ -867,82 +860,6 @@ func (s *APIServer) deleteCertAuthority(auth ClientI, w http.ResponseWriter, r * return message(fmt.Sprintf("cert '%v' deleted", id)), nil } -type createSessionReq struct { - Session session.Session `json:"session"` -} - -func (s *APIServer) createSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { - var req *createSessionReq - if err := httplib.ReadJSON(r, &req); err != nil { - return nil, trace.Wrap(err) - } - namespace := p.ByName("namespace") - if !types.IsValidNamespace(namespace) { - return nil, trace.BadParameter("invalid namespace %q", namespace) - } - req.Session.Namespace = namespace - if err := auth.CreateSession(r.Context(), req.Session); err != nil { - return nil, trace.Wrap(err) - } - return message("ok"), nil -} - -type updateSessionReq struct { - Update session.UpdateRequest `json:"update"` -} - -func (s *APIServer) updateSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { - var req *updateSessionReq - if err := httplib.ReadJSON(r, &req); err != nil { - return nil, trace.Wrap(err) - } - namespace := p.ByName("namespace") - if !types.IsValidNamespace(namespace) { - return nil, trace.BadParameter("invalid namespace %q", namespace) - } - req.Update.Namespace = namespace - if err := auth.UpdateSession(r.Context(), req.Update); err != nil { - return nil, trace.Wrap(err) - } - return message("ok"), nil -} - -func (s *APIServer) deleteSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { - err := auth.DeleteSession(r.Context(), p.ByName("namespace"), session.ID(p.ByName("id"))) - if err != nil { - return nil, trace.Wrap(err) - } - return message("ok"), nil -} - -func (s *APIServer) getSessions(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { - namespace := p.ByName("namespace") - if !types.IsValidNamespace(namespace) { - return nil, trace.BadParameter("invalid namespace %q", namespace) - } - sessions, err := auth.GetSessions(r.Context(), namespace) - if err != nil { - return nil, trace.Wrap(err) - } - return sessions, nil -} - -func (s *APIServer) getSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { - sid, err := session.ParseID(p.ByName("id")) - if err != nil { - return nil, trace.Wrap(err) - } - namespace := p.ByName("namespace") - if !types.IsValidNamespace(namespace) { - return nil, trace.BadParameter("invalid namespace %q", namespace) - } - se, err := auth.GetSession(r.Context(), namespace, *sid) - if err != nil { - return nil, trace.Wrap(err) - } - return se, nil -} - type createOIDCAuthRequestReq struct { Req types.OIDCAuthRequest `json:"req"` } diff --git a/lib/auth/apiserver_active_sessions_test.go b/lib/auth/apiserver_active_sessions_test.go deleted file mode 100644 index 72a63f910df38..0000000000000 --- a/lib/auth/apiserver_active_sessions_test.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2021 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 auth - -import ( - "context" - "sort" - "testing" - "time" - - apidefaults "github.com/gravitational/teleport/api/defaults" - "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/services" - "github.com/gravitational/teleport/lib/session" - - "github.com/google/go-cmp/cmp" - "github.com/gravitational/trace" - "github.com/stretchr/testify/require" -) - -func TestAPIServer_activeSessions_whereConditions(t *testing.T) { - t.Parallel() - - ctx := context.Background() - tlsServer := newTestTLSServer(t) - authServer := tlsServer.Auth() - - // - "admin" has permissions to access all active sessions - // - "alpaca" has permissions to access only their own active sessions - // Each user is assigned its corresponding role, plus whatever extra - // permissions are needed to run the scenario. - const admin = "admin" - const alpaca = "alpaca" - alpacaRole := services.RoleForUser(&types.UserV2{Metadata: types.Metadata{Name: alpaca}}) - alpacaRole.SetLogins(types.Allow, []string{alpaca}) - alpacaRole.SetRules(types.Allow, append(alpacaRole.GetRules(types.Allow), types.Rule{ - Resources: []string{"ssh_session"}, - // Allow all ssh_session verbs, deny rule below takes precedence. - Verbs: []string{"*"}, - })) - alpacaRole.SetRules(types.Deny, append(alpacaRole.GetRules(types.Deny), types.Rule{ - Resources: []string{"ssh_session"}, - Verbs: []string{"list", "read", "update", "delete"}, - Where: "!contains(ssh_session.participants, user.metadata.name)", - })) - _, err := CreateUser(authServer, alpaca, alpacaRole) - require.NoError(t, err) - - // Prepare clients. - adminClient, err := tlsServer.NewClient(TestAdmin()) - require.NoError(t, err) - alpacaClient, err := tlsServer.NewClient(TestUser(alpaca)) - require.NoError(t, err) - - // Prepare one session per user. - createSession := func(clt ClientI, user string) session.ID { - id := session.NewID() - now := time.Now() - - // Create initial session. - require.NoError(t, clt.CreateSession(ctx, session.Session{ - ID: id, - Namespace: apidefaults.Namespace, - TerminalParams: session.TerminalParams{ - W: 100, - H: 100, - }, - Login: user, - Created: now, - LastActive: now, - })) - - // Add parties, must be done via update. - // Usually the Node does this, in the test we are taking a shortcut and - // using admin due to its powerful permissions. - require.NoError(t, adminClient.UpdateSession(ctx, session.UpdateRequest{ - ID: id, - Namespace: apidefaults.Namespace, - Parties: &[]session.Party{ - {ID: session.NewID(), User: user}, - }, - })) - return id - } - adminSessionID := createSession(adminClient, admin) - alpacaSessionID := createSession(alpacaClient, alpaca) - - t.Run("GetSessions respects role conditions", func(t *testing.T) { - tests := []struct { - name string - clt ClientI - wantIDs []session.ID - }{ - { - name: admin, - clt: adminClient, - wantIDs: []session.ID{adminSessionID, alpacaSessionID}, - }, - { - name: alpaca, - clt: alpacaClient, - wantIDs: []session.ID{alpacaSessionID}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - sessions, err := test.clt.GetSessions(ctx, apidefaults.Namespace) - require.NoError(t, err) - - got := make([]session.ID, len(sessions)) - for i, s := range sessions { - got[i] = s.ID - } - want := test.wantIDs - sort.Slice(got, func(i, j int) bool { return got[i] < got[j] }) - sort.Slice(want, func(i, j int) bool { return want[i] < want[j] }) - if diff := cmp.Diff(test.wantIDs, got); diff != "" { - t.Errorf("GetSessions() mismatch (-want +got):\n%s", diff) - } - }) - } - }) - - // Helper functions used by test cases below. - getSession := func(clt ClientI) func(id session.ID) error { - return func(id session.ID) error { - _, err := clt.GetSession(ctx, apidefaults.Namespace, id) - return err - } - } - updateSession := func(clt ClientI) func(id session.ID) error { - return func(id session.ID) error { - return clt.UpdateSession(ctx, session.UpdateRequest{ - ID: id, - Namespace: apidefaults.Namespace, - TerminalParams: &session.TerminalParams{W: 150, H: 150}, - }) - } - } - deleteSession := func(clt ClientI) func(id session.ID) error { - return func(id session.ID) error { - return clt.UpdateSession(ctx, session.UpdateRequest{ - ID: id, - Namespace: apidefaults.Namespace, - TerminalParams: &session.TerminalParams{W: 150, H: 150}, - }) - } - } - - t.Run("users can't interact with denied sessions", func(t *testing.T) { - clt := alpacaClient - sessionID := adminSessionID - tests := []struct { - name string - fn func(id session.ID) error - }{ - { - name: "GetSession", - fn: getSession(clt), - }, - { - name: "UpdateSession", - fn: updateSession(clt), - }, - { - name: "DeleteSession", - fn: deleteSession(clt), - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := test.fn(sessionID) - require.True(t, trace.IsAccessDenied(err), "unexpected err: %v (want access denied)", err) - }) - } - }) - - t.Run("users can interact with allowed sessions", func(t *testing.T) { - tests := []struct { - name string - fn func(session.ID) error - sessionID session.ID - }{ - { - name: "admin reads own session", - fn: getSession(adminClient), - sessionID: adminSessionID, - }, - { - name: "admin updates own session", - fn: updateSession(adminClient), - sessionID: adminSessionID, - }, - { - name: "admin deletes own session", - fn: deleteSession(adminClient), - sessionID: adminSessionID, - }, - { - name: "admin reads alpaca session", - fn: getSession(adminClient), - sessionID: alpacaSessionID, - }, - { - name: "admin updates alpaca session", - fn: updateSession(adminClient), - sessionID: alpacaSessionID, - }, - - { - name: "alpaca reads own session", - fn: getSession(alpacaClient), - sessionID: alpacaSessionID, - }, - { - name: "alpaca updates own session", - fn: updateSession(alpacaClient), - sessionID: alpacaSessionID, - }, - { - name: "alpaca deletes own session", - fn: deleteSession(alpacaClient), - sessionID: alpacaSessionID, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - require.NoError(t, test.fn(test.sessionID)) - }) - } - }) -} diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 25763acf56e0b..aab76a13a0bae 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -52,7 +52,6 @@ import ( // methods that focuses on authorizing every request type ServerWithRoles struct { authServer *Server - sessions session.Service alog events.IAuditLog // context holds authorization context context Context @@ -186,18 +185,6 @@ func (a *ServerWithRoles) actionForKindSession(namespace, verb string, sid sessi return trace.Wrap(a.actionWithExtendedContext(namespace, types.KindSession, verb, extendContext)) } -// actionForKindSSHSession is a special checker that grants access to active SSH -// sessions. It can allow access to a specific session based on the `where` -// section of the user's access rule for kind `ssh_session`. -func (a *ServerWithRoles) actionForKindSSHSession(ctx context.Context, namespace, verb string, sid session.ID) error { - extendContext := func(serviceContext *services.Context) error { - session, err := a.sessions.GetSession(ctx, namespace, sid) - serviceContext.SSHSession = session - return trace.Wrap(err) - } - return trace.Wrap(a.actionWithExtendedContext(namespace, types.KindSSHSession, verb, extendContext)) -} - // serverAction returns an access denied error if the role is not one of the builtin server roles. func (a *ServerWithRoles) serverAction() error { role, ok := a.context.Identity.(BuiltinRole) @@ -505,62 +492,6 @@ func (a *ServerWithRoles) AuthenticateSSHUser(ctx context.Context, req Authentic return a.authServer.AuthenticateSSHUser(ctx, req) } -func (a *ServerWithRoles) GetSessions(ctx context.Context, namespace string) ([]session.Session, error) { - cond, err := a.actionForListWithCondition(namespace, types.KindSSHSession, services.SSHSessionIdentifier) - if err != nil { - return nil, trace.Wrap(err) - } - - sessions, err := a.sessions.GetSessions(ctx, namespace) - if err != nil { - return nil, trace.Wrap(err) - } - if cond == nil { - return sessions, nil - } - - // Filter sessions according to cond. - filteredSessions := make([]session.Session, 0, len(sessions)) - ruleCtx := &services.Context{User: a.context.User} - for _, s := range sessions { - ruleCtx.SSHSession = &s - if err := a.context.Checker.CheckAccessToRule(ruleCtx, namespace, types.KindSSHSession, types.VerbList, true /* silent */); err != nil { - continue - } - filteredSessions = append(filteredSessions, s) - } - return filteredSessions, nil -} - -func (a *ServerWithRoles) GetSession(ctx context.Context, namespace string, id session.ID) (*session.Session, error) { - if err := a.actionForKindSSHSession(ctx, namespace, types.VerbRead, id); err != nil { - return nil, trace.Wrap(err) - } - return a.sessions.GetSession(ctx, namespace, id) -} - -func (a *ServerWithRoles) CreateSession(ctx context.Context, s session.Session) error { - if err := a.action(s.Namespace, types.KindSSHSession, types.VerbCreate); err != nil { - return trace.Wrap(err) - } - return a.sessions.CreateSession(ctx, s) -} - -func (a *ServerWithRoles) UpdateSession(ctx context.Context, req session.UpdateRequest) error { - if err := a.actionForKindSSHSession(ctx, req.Namespace, types.VerbUpdate, req.ID); err != nil { - return trace.Wrap(err) - } - return a.sessions.UpdateSession(ctx, req) -} - -// DeleteSession removes an active session from the backend. -func (a *ServerWithRoles) DeleteSession(ctx context.Context, namespace string, id session.ID) error { - if err := a.actionForKindSSHSession(ctx, namespace, types.VerbDelete, id); err != nil { - return trace.Wrap(err) - } - return a.sessions.DeleteSession(ctx, namespace, id) -} - // CreateCertAuthority not implemented: can only be called locally. func (a *ServerWithRoles) CreateCertAuthority(ca types.CertAuthority) error { return trace.NotImplemented(notImplementedMessage) @@ -5035,7 +4966,7 @@ func (a *ServerWithRoles) MaintainSessionPresence(ctx context.Context) (proto.Au // NewAdminAuthServer returns auth server authorized as admin, // used for auth server cached access -func NewAdminAuthServer(authServer *Server, sessions session.Service, alog events.IAuditLog) (ClientI, error) { +func NewAdminAuthServer(authServer *Server, alog events.IAuditLog) (ClientI, error) { ctx, err := NewAdminContext() if err != nil { return nil, trace.Wrap(err) @@ -5044,7 +4975,6 @@ func NewAdminAuthServer(authServer *Server, sessions session.Service, alog event authServer: authServer, context: *ctx, alog: alog, - sessions: sessions, }, nil } diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 06c8f20e5032d..8bdeb6bdd5969 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -1474,7 +1474,6 @@ func serverWithAllowRules(t *testing.T, srv *TestAuthServer, allowRules []types. return &ServerWithRoles{ authServer: srv.AuthServer, - sessions: srv.SessionServer, alog: srv.AuditLog, context: *authContext, } @@ -2137,7 +2136,6 @@ func TestReplaceRemoteLocksRBAC(t *testing.T) { s := &ServerWithRoles{ authServer: srv.AuthServer, - sessions: srv.SessionServer, alog: srv.AuditLog, context: *authContext, } @@ -2331,7 +2329,6 @@ func TestKindClusterConfig(t *testing.T) { require.NoError(t, err, trace.DebugReport(err)) s := &ServerWithRoles{ authServer: srv.AuthServer, - sessions: srv.SessionServer, alog: srv.AuditLog, context: *authContext, } @@ -2891,7 +2888,6 @@ func TestListResources_KindKubernetesCluster(t *testing.T) { s := &ServerWithRoles{ authServer: srv.AuthServer, - sessions: srv.SessionServer, alog: srv.AuditLog, context: *authContext, } diff --git a/lib/auth/clt.go b/lib/auth/clt.go index 84058ee6fb588..3277d0b25d142 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -287,70 +287,6 @@ func (c *Client) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) { return &re, nil } -// GetSessions returns a list of active sessions in the cluster as reported by -// the auth server. -func (c *Client) GetSessions(ctx context.Context, namespace string) ([]session.Session, error) { - if namespace == "" { - return nil, trace.BadParameter(MissingNamespaceError) - } - out, err := c.Get(ctx, c.Endpoint("namespaces", namespace, "sessions"), url.Values{}) - if err != nil { - return nil, trace.Wrap(err) - } - var sessions []session.Session - if err := json.Unmarshal(out.Bytes(), &sessions); err != nil { - return nil, err - } - return sessions, nil -} - -// GetSession returns a session by ID -func (c *Client) GetSession(ctx context.Context, namespace string, id session.ID) (*session.Session, error) { - if namespace == "" { - return nil, trace.BadParameter(MissingNamespaceError) - } - // saving extra round-trip - if err := id.Check(); err != nil { - return nil, trace.Wrap(err) - } - out, err := c.Get(ctx, c.Endpoint("namespaces", namespace, "sessions", string(id)), url.Values{}) - if err != nil { - return nil, trace.Wrap(err) - } - var sess *session.Session - if err := json.Unmarshal(out.Bytes(), &sess); err != nil { - return nil, trace.Wrap(err) - } - return sess, nil -} - -// DeleteSession removes an active session from the backend. -func (c *Client) DeleteSession(ctx context.Context, namespace string, id session.ID) error { - if namespace == "" { - return trace.BadParameter(MissingNamespaceError) - } - _, err := c.Delete(ctx, c.Endpoint("namespaces", namespace, "sessions", string(id))) - return trace.Wrap(err) -} - -// CreateSession creates new session -func (c *Client) CreateSession(ctx context.Context, sess session.Session) error { - if sess.Namespace == "" { - return trace.BadParameter(MissingNamespaceError) - } - _, err := c.PostJSON(ctx, c.Endpoint("namespaces", sess.Namespace, "sessions"), createSessionReq{Session: sess}) - return trace.Wrap(err) -} - -// UpdateSession updates existing session -func (c *Client) UpdateSession(ctx context.Context, req session.UpdateRequest) error { - if err := req.Check(); err != nil { - return trace.Wrap(err) - } - _, err := c.PutJSON(ctx, c.Endpoint("namespaces", req.Namespace, "sessions", string(req.ID)), updateSessionReq{Update: req}) - return trace.Wrap(err) -} - func (c *Client) Close() error { c.HTTPClient.Close() return c.APIClient.Close() @@ -1680,7 +1616,6 @@ type ClientI interface { services.Databases services.WindowsDesktops WebService - session.Service services.ClusterConfiguration services.SessionTrackerService services.ConnectionsDiagnostic diff --git a/lib/auth/clt_test.go b/lib/auth/clt_test.go index dcb844a9c5fa6..500417b16bd5d 100644 --- a/lib/auth/clt_test.go +++ b/lib/auth/clt_test.go @@ -32,7 +32,6 @@ import ( apiclient "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/testauthority" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/tlsca" ) @@ -77,7 +76,8 @@ func TestClient_DialTimeout(t *testing.T) { errChan := make(chan error, 1) go func() { // try to create a session - this will timeout after the DialTimeout threshold is exceeded - errChan <- clt.CreateSession(context.Background(), session.Session{Namespace: "test"}) + _, err := clt.CreateSessionTracker(context.Background(), &types.SessionTrackerV1{}) + errChan <- err }() select { diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 68c64a7ff99ad..0b269833c544d 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -4304,7 +4304,6 @@ func serverWithNopRole(cfg GRPCServerConfig) (*ServerWithRoles, error) { return &ServerWithRoles{ authServer: cfg.AuthServer, context: *nopCtx, - sessions: cfg.SessionService, alog: cfg.AuthServer, }, nil } @@ -4339,7 +4338,6 @@ func (g *GRPCServer) authenticate(ctx context.Context) (*grpcContext, error) { ServerWithRoles: &ServerWithRoles{ authServer: g.AuthServer, context: *authContext, - sessions: g.SessionService, alog: g.AuthServer, }, }, nil diff --git a/lib/auth/helpers.go b/lib/auth/helpers.go index a37aa2472c973..892efa9f10430 100644 --- a/lib/auth/helpers.go +++ b/lib/auth/helpers.go @@ -48,7 +48,6 @@ import ( "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" "github.com/gravitational/teleport/lib/services/suite" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" ) @@ -182,8 +181,6 @@ type TestAuthServer struct { AuthServer *Server // AuditLog is an event audit log AuditLog events.IAuditLog - // SessionServer is a session service - SessionServer session.Service // Backend is a backend for auth server Backend backend.Backend // Authorizer is an authorizer used in tests @@ -229,11 +226,6 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) { srv.AuditLog = localLog } - srv.SessionServer, err = session.New(srv.Backend) - if err != nil { - return nil, trace.Wrap(err) - } - access := local.NewAccessService(srv.Backend) identity := local.NewIdentityService(srv.Backend) @@ -541,11 +533,10 @@ func (a *TestAuthServer) Trust(ctx context.Context, remote *TestAuthServer, role // NewTestTLSServer returns new test TLS server func (a *TestAuthServer) NewTestTLSServer() (*TestTLSServer, error) { apiConfig := &APIConfig{ - AuthServer: a.AuthServer, - Authorizer: a.Authorizer, - SessionService: a.SessionServer, - AuditLog: a.AuditLog, - Emitter: a.AuthServer.emitter, + AuthServer: a.AuthServer, + Authorizer: a.Authorizer, + AuditLog: a.AuditLog, + Emitter: a.AuthServer.emitter, } srv, err := NewTestTLSServer(TestTLSServerConfig{ APIConfig: apiConfig, @@ -658,7 +649,7 @@ func NewTestTLSServer(cfg TestTLSServerConfig) (*TestTLSServer, error) { } tlsConfig.Time = cfg.AuthServer.Clock().Now - accessPoint, err := NewAdminAuthServer(srv.AuthServer.AuthServer, srv.AuthServer.SessionServer, srv.AuthServer.AuditLog) + accessPoint, err := NewAdminAuthServer(srv.AuthServer.AuthServer, srv.AuthServer.AuditLog) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/service/service.go b/lib/service/service.go index 416eecfc6c863..ce6349bf147ac 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -97,7 +97,6 @@ import ( "github.com/gravitational/teleport/lib/reversetunnel" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/srv/alpnproxy" alpnproxyauth "github.com/gravitational/teleport/lib/srv/alpnproxy/auth" @@ -509,10 +508,11 @@ func (process *TeleportProcess) addConnector(connector *Connector) { // the resulting payload as a *Connector. Returns (nil, nil) when the // ExitContext is done, so error checking should happen on the connector rather // than the error: -// conn, err := process.waitForConnector("FooIdentity", log) -// if conn == nil { -// return trace.Wrap(err) -// } +// +// conn, err := process.waitForConnector("FooIdentity", log) +// if conn == nil { +// return trace.Wrap(err) +// } func (process *TeleportProcess) waitForConnector(identityEvent string, log logrus.FieldLogger) (*Connector, error) { event, err := process.WaitForEvent(process.ExitContext(), identityEvent) if err != nil { @@ -1624,17 +1624,12 @@ func (process *TeleportProcess) initAuthService() error { // second, create the API Server: it's actually a collection of API servers, // each serving requests for a "role" which is assigned to every connected // client based on their certificate (user, server, admin, etc) - sessionService, err := session.New(b) - if err != nil { - return trace.Wrap(err) - } authorizer, err := auth.NewAuthorizer(clusterName, authServer, lockWatcher) if err != nil { return trace.Wrap(err) } apiConf := &auth.APIConfig{ AuthServer: authServer, - SessionService: sessionService, Authorizer: authorizer, AuditLog: process.auditLog, PluginRegistry: process.PluginRegistry, @@ -2270,7 +2265,6 @@ func (process *TeleportProcess) initSSH() error { regular.SetLimiter(limiter), regular.SetShell(cfg.SSH.Shell), regular.SetEmitter(&events.StreamerAndEmitter{Emitter: asyncEmitter, Streamer: streamer}), - regular.SetSessionServer(conn.Client), regular.SetLabels(cfg.SSH.Labels, cfg.SSH.CmdLabels, process.cloudLabels), regular.SetNamespace(namespace), regular.SetPermitUserEnvironment(cfg.SSH.PermitUserEnvironment), @@ -2843,10 +2837,10 @@ func (process *TeleportProcess) getAdditionalPrincipals(role types.SystemRole) ( // initProxy gets called if teleport runs with 'proxy' role enabled. // this means it will do four things: -// 1. serve a web UI -// 2. proxy SSH connections to nodes running with 'node' role -// 3. take care of reverse tunnels -// 4. optionally proxy kubernetes connections +// 1. serve a web UI +// 2. proxy SSH connections to nodes running with 'node' role +// 3. take care of reverse tunnels +// 4. optionally proxy kubernetes connections func (process *TeleportProcess) initProxy() error { // If no TLS key was provided for the web listener, generate a self-signed cert if len(process.Config.Proxy.KeyPairs) == 0 && @@ -3497,7 +3491,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { conn.Client, regular.SetLimiter(proxyLimiter), regular.SetProxyMode(peerAddrString, tsrv, accessPoint), - regular.SetSessionServer(conn.Client), regular.SetCiphers(cfg.Ciphers), regular.SetKEXAlgorithms(cfg.KEXAlgorithms), regular.SetMACAlgorithms(cfg.MACAlgorithms), diff --git a/lib/session/session.go b/lib/session/session.go index 0fbc54b5ecaab..f87c722345cc3 100644 --- a/lib/session/session.go +++ b/lib/session/session.go @@ -19,21 +19,15 @@ limitations under the License. package session import ( - "context" - "encoding/json" "fmt" - "sort" "strconv" "strings" "time" "github.com/google/uuid" - "github.com/jonboulle/clockwork" "github.com/moby/term" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/backend" - "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/trace" ) @@ -199,293 +193,10 @@ func (p *TerminalParams) Winsize() *term.Winsize { } } -// UpdateRequest is a session update request -type UpdateRequest struct { - ID ID `json:"id"` - Namespace string `json:"namespace"` - TerminalParams *TerminalParams `json:"terminal_params"` - - // Parties allows to update the list of session parties. nil means - // "do not update", empty list means "everybody is gone" - Parties *[]Party `json:"parties"` -} - -// Check returns nil if request is valid, error otherwize -func (u *UpdateRequest) Check() error { - if err := u.ID.Check(); err != nil { - return trace.Wrap(err) - } - if u.Namespace == "" { - return trace.BadParameter("missing parameter Namespace") - } - if u.TerminalParams != nil { - _, err := NewTerminalParamsFromInt(u.TerminalParams.W, u.TerminalParams.H) - if err != nil { - return trace.Wrap(err) - } - } - return nil -} - // MaxSessionSliceLength is the maximum number of sessions per time window // that the backend will return. const MaxSessionSliceLength = 1000 -// Service is a realtime SSH session service that has information about -// sessions that are in-flight in the cluster at the moment. -type Service interface { - // GetSessions returns a list of currently active sessions matching - // the given condition. - GetSessions(ctx context.Context, namespace string) ([]Session, error) - - // GetSession returns a session with its parties by ID. - GetSession(ctx context.Context, namespace string, id ID) (*Session, error) - - // CreateSession creates a new active session and it's parameters if term is - // skipped, terminal size won't be recorded. - CreateSession(ctx context.Context, sess Session) error - - // UpdateSession updates certain session parameters (last_active, terminal - // parameters) other parameters will not be updated. - UpdateSession(ctx context.Context, req UpdateRequest) error - - // DeleteSession removes an active session from the backend. - DeleteSession(ctx context.Context, namespace string, id ID) error -} - -type server struct { - bk backend.Backend - activeSessionTTL time.Duration - clock clockwork.Clock -} - -// New returns new session server that uses sqlite to manage -// active sessions -func New(bk backend.Backend) (Service, error) { - s := &server{ - bk: bk, - clock: clockwork.NewRealClock(), - } - if s.activeSessionTTL == 0 { - s.activeSessionTTL = defaults.ActiveSessionTTL - } - return s, nil -} - -func activePrefix(namespace string) []byte { - return backend.Key("namespaces", namespace, "sessions", "active") -} - -func activeKey(namespace string, key string) []byte { - return backend.Key("namespaces", namespace, "sessions", "active", key) -} - -// GetSessions returns a list of active sessions. -// Returns an empty slice if no sessions are active -func (s *server) GetSessions(ctx context.Context, namespace string) ([]Session, error) { - prefix := activePrefix(namespace) - result, err := s.bk.GetRange(ctx, prefix, backend.RangeEnd(prefix), MaxSessionSliceLength) - if err != nil { - return nil, trace.Wrap(err) - } - - sessions := make(Sessions, len(result.Items)) - for i, item := range result.Items { - if err := json.Unmarshal(item.Value, &sessions[i]); err != nil { - return nil, trace.Wrap(err) - } - } - - sort.Stable(sessions) - return sessions, nil -} - -// Sessions type is created over []Session to implement sort.Interface to -// be able to sort sessions by creation time -type Sessions []Session - -// Swap is part of sort.Interface implementation for []Session -func (slice Sessions) Swap(i, j int) { - s := slice[i] - slice[i] = slice[j] - slice[j] = s -} - -// Less is part of sort.Interface implementation for []Session -func (slice Sessions) Less(i, j int) bool { - return slice[i].Created.Before(slice[j].Created) -} - -// Len is part of sort.Interface implementation for []Session -func (slice Sessions) Len() int { - return len(slice) -} - -// GetSession returns the session by its id. Returns NotFound if a session -// is not found -func (s *server) GetSession(ctx context.Context, namespace string, id ID) (*Session, error) { - item, err := s.bk.Get(ctx, activeKey(namespace, string(id))) - if err != nil { - if trace.IsNotFound(err) { - return nil, trace.NotFound("session(%v, %v) is not found", namespace, id) - } - return nil, trace.Wrap(err) - } - var sess Session - if err := json.Unmarshal(item.Value, &sess); err != nil { - return nil, trace.Wrap(err) - } - return &sess, nil -} - -// CreateSession creates a new session if it does not exist, if the session -// exists the function will return AlreadyExists error -// The session will be marked as active for TTL period of time -func (s *server) CreateSession(ctx context.Context, sess Session) error { - if err := sess.ID.Check(); err != nil { - return trace.Wrap(err) - } - if sess.Namespace == "" { - return trace.BadParameter("session namespace can not be empty") - } - if sess.Login == "" { - return trace.BadParameter("session login can not be empty") - } - if sess.Created.IsZero() { - return trace.BadParameter("created can not be empty") - } - if sess.LastActive.IsZero() { - return trace.BadParameter("last_active can not be empty") - } - _, err := NewTerminalParamsFromInt(sess.TerminalParams.W, sess.TerminalParams.H) - if err != nil { - return trace.Wrap(err) - } - sess.Parties = nil - data, err := json.Marshal(sess) - if err != nil { - return trace.Wrap(err) - } - item := backend.Item{ - Key: activeKey(sess.Namespace, string(sess.ID)), - Value: data, - Expires: s.clock.Now().UTC().Add(s.activeSessionTTL), - } - _, err = s.bk.Create(ctx, item) - if err != nil { - return trace.Wrap(err) - } - return nil -} - -const ( - sessionUpdateAttempts = 10 - sessionUpdateRetryPeriod = 20 * time.Millisecond -) - -// UpdateSession updates session parameters - can mark it as inactive and update its terminal parameters -func (s *server) UpdateSession(ctx context.Context, req UpdateRequest) error { - if err := req.Check(); err != nil { - return trace.Wrap(err) - } - - key := activeKey(req.Namespace, string(req.ID)) - - // Try several times, then give up - for i := 0; i < sessionUpdateAttempts; i++ { - item, err := s.bk.Get(ctx, key) - if err != nil { - return trace.Wrap(err) - } - - var session Session - if err := json.Unmarshal(item.Value, &session); err != nil { - return trace.Wrap(err) - } - - if req.TerminalParams != nil { - session.TerminalParams = *req.TerminalParams - } - if req.Parties != nil { - session.Parties = *req.Parties - } - newValue, err := json.Marshal(session) - if err != nil { - return trace.Wrap(err) - } - newItem := backend.Item{ - Key: key, - Value: newValue, - Expires: s.clock.Now().UTC().Add(s.activeSessionTTL), - } - - _, err = s.bk.CompareAndSwap(ctx, *item, newItem) - if err != nil { - if trace.IsCompareFailed(err) || trace.IsConnectionProblem(err) { - s.clock.Sleep(sessionUpdateRetryPeriod) - continue - } - return trace.Wrap(err) - } - return nil - } - return trace.ConnectionProblem(nil, "failed concurrently update the session") -} - -// DeleteSession removes an active session from the backend. -func (s *server) DeleteSession(ctx context.Context, namespace string, id ID) error { - if !types.IsValidNamespace(namespace) { - return trace.BadParameter("invalid namespace %q", namespace) - } - err := id.Check() - if err != nil { - return trace.Wrap(err) - } - - err = s.bk.Delete(ctx, activeKey(namespace, string(id))) - if err != nil { - return trace.Wrap(err) - } - - return nil -} - -// discardSessionServer discards all information about sessions given to it. -type discardSessionServer struct { -} - -// NewDiscardSessionServer returns a new discarding session server. It's used -// with the recording proxy so that nodes don't register active sessions to -// the backend. -func NewDiscardSessionServer() Service { - return &discardSessionServer{} -} - -// GetSessions returns an empty list of sessions. -func (d *discardSessionServer) GetSessions(ctx context.Context, namespace string) ([]Session, error) { - return []Session{}, nil -} - -// GetSession always returns a zero session. -func (d *discardSessionServer) GetSession(ctx context.Context, namespace string, id ID) (*Session, error) { - return &Session{}, nil -} - -// CreateSession always returns nil, does nothing. -func (d *discardSessionServer) CreateSession(ctx context.Context, sess Session) error { - return nil -} - -// UpdateSession always returns nil, does nothing. -func (d *discardSessionServer) UpdateSession(ctx context.Context, req UpdateRequest) error { - return nil -} - -// DeleteSession removes an active session from the backend. -func (d *discardSessionServer) DeleteSession(ctx context.Context, namespace string, id ID) error { - return nil -} - // NewTerminalParamsFromUint32 returns new terminal parameters from uint32 width and height func NewTerminalParamsFromUint32(w uint32, h uint32) (*TerminalParams, error) { if w > maxSize || w < minSize { diff --git a/lib/session/session_test.go b/lib/session/session_test.go deleted file mode 100644 index 83e7b7e4fd45e..0000000000000 --- a/lib/session/session_test.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright 2015 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 - -import ( - "context" - "os" - "testing" - "time" - - apidefaults "github.com/gravitational/teleport/api/defaults" - "github.com/gravitational/teleport/lib/backend" - "github.com/gravitational/teleport/lib/backend/lite" - "github.com/gravitational/teleport/lib/defaults" - "github.com/gravitational/teleport/lib/utils" - - "github.com/jonboulle/clockwork" - "github.com/stretchr/testify/require" - - "github.com/gravitational/trace" -) - -func TestMain(m *testing.M) { - utils.InitLoggerForTests() - os.Exit(m.Run()) -} - -func TestSessions(t *testing.T) { - s := newsessionSuite(t) - t.Cleanup(func() { s.TearDown(t) }) - - t.Run("TestID", s.TestID) - t.Run("TestSessionsCRUD", s.TestSessionsCRUD) - t.Run("TestSessionsInactivity", s.TestSessionsInactivity) - t.Run("TestPartiesCRUD", s.TestPartiesCRUD) -} - -type sessionSuite struct { - dir string - srv *server - bk backend.Backend - clock clockwork.FakeClock -} - -func newsessionSuite(t *testing.T) *sessionSuite { - var err error - s := &sessionSuite{} - - s.clock = clockwork.NewFakeClockAt(time.Date(2016, 9, 8, 7, 6, 5, 0, time.UTC)) - s.dir = t.TempDir() - s.bk, err = lite.NewWithConfig(context.TODO(), - lite.Config{ - Path: s.dir, - Clock: s.clock, - }, - ) - require.NoError(t, err) - - srv, err := New(s.bk) - require.NoError(t, err) - srv.(*server).clock = s.clock - s.srv = srv.(*server) - return s -} - -func (s *sessionSuite) TearDown(t *testing.T) { - require.NoError(t, s.bk.Close()) -} - -func (s *sessionSuite) TestID(t *testing.T) { - id := NewID() - id2, err := ParseID(id.String()) - require.NoError(t, err) - require.Equal(t, id, *id2) - - for _, val := range []string{"garbage", "", " ", string(id) + "extra"} { - id := ID(val) - require.Error(t, id.Check()) - } -} - -func (s *sessionSuite) TestSessionsCRUD(t *testing.T) { - ctx := context.Background() - out, err := s.srv.GetSessions(ctx, apidefaults.Namespace) - require.NoError(t, err) - require.Empty(t, out) - - // Create session. - sess := Session{ - ID: NewID(), - Namespace: apidefaults.Namespace, - TerminalParams: TerminalParams{W: 100, H: 100}, - Login: "bob", - LastActive: s.clock.Now().UTC(), - Created: s.clock.Now().UTC(), - } - require.NoError(t, s.srv.CreateSession(ctx, sess)) - - // Make sure only one session exists. - out, err = s.srv.GetSessions(ctx, apidefaults.Namespace) - require.NoError(t, err) - require.Equal(t, out, []Session{sess}) - - // Make sure the session is the one created above. - s2, err := s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.NoError(t, err) - require.Equal(t, s2, &sess) - - // Update session terminal parameter - err = s.srv.UpdateSession(ctx, UpdateRequest{ - ID: sess.ID, - Namespace: apidefaults.Namespace, - TerminalParams: &TerminalParams{W: 101, H: 101}, - }) - require.NoError(t, err) - - // Verify update was applied. - sess.TerminalParams = TerminalParams{W: 101, H: 101} - s2, err = s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.NoError(t, err) - require.Equal(t, s2, &sess) - - // Remove the session. - err = s.srv.DeleteSession(ctx, apidefaults.Namespace, sess.ID) - require.NoError(t, err) - - // Make sure session no longer exists. - _, err = s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.Error(t, err) -} - -// TestSessionsInactivity makes sure that session will be marked -// as inactive after period of inactivity -func (s *sessionSuite) TestSessionsInactivity(t *testing.T) { - ctx := context.Background() - sess := Session{ - ID: NewID(), - Namespace: apidefaults.Namespace, - TerminalParams: TerminalParams{W: 100, H: 100}, - Login: "bob", - LastActive: s.clock.Now().UTC(), - Created: s.clock.Now().UTC(), - } - require.NoError(t, s.srv.CreateSession(ctx, sess)) - - // move forward in time: - s.clock.Advance(defaults.ActiveSessionTTL + time.Second) - - // should not be in active sessions: - s2, err := s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.IsType(t, trace.NotFound(""), err) - require.Nil(t, s2) -} - -func (s *sessionSuite) TestPartiesCRUD(t *testing.T) { - ctx := context.Background() - - // create session: - sess := Session{ - ID: NewID(), - Namespace: apidefaults.Namespace, - TerminalParams: TerminalParams{W: 100, H: 100}, - Login: "vincent", - LastActive: s.clock.Now().UTC(), - Created: s.clock.Now().UTC(), - } - err := s.srv.CreateSession(ctx, sess) - require.NoError(t, err) - // add two people: - parties := []Party{ - { - ID: NewID(), - RemoteAddr: "1_remote_addr", - User: "first", - ServerID: "luna", - LastActive: s.clock.Now().UTC(), - }, - { - ID: NewID(), - RemoteAddr: "2_remote_addr", - User: "second", - ServerID: "luna", - LastActive: s.clock.Now().UTC(), - }, - } - err = s.srv.UpdateSession(ctx, UpdateRequest{ - ID: sess.ID, - Namespace: apidefaults.Namespace, - Parties: &parties, - }) - require.NoError(t, err) - // verify they're in the session: - copy, err := s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.NoError(t, err) - require.Len(t, copy.Parties, 2) - - // empty update (list of parties must not change) - err = s.srv.UpdateSession(ctx, UpdateRequest{ID: sess.ID, Namespace: apidefaults.Namespace}) - require.NoError(t, err) - copy, _ = s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.Len(t, copy.Parties, 2) - - // remove the 2nd party: - deleted := copy.RemoveParty(parties[1].ID) - require.True(t, deleted) - err = s.srv.UpdateSession(ctx, UpdateRequest{ID: copy.ID, Parties: ©.Parties, Namespace: apidefaults.Namespace}) - require.NoError(t, err) - copy, _ = s.srv.GetSession(ctx, apidefaults.Namespace, sess.ID) - require.Len(t, copy.Parties, 1) - - // we still have the 1st party in: - require.Equal(t, parties[0].ID, copy.Parties[0].ID) -} diff --git a/lib/srv/ctx.go b/lib/srv/ctx.go index 32ed4aeecb137..20a3aa71768f2 100644 --- a/lib/srv/ctx.go +++ b/lib/srv/ctx.go @@ -132,9 +132,6 @@ type Server interface { // GetAccessPoint returns an AccessPoint for this cluster. GetAccessPoint() AccessPoint - // GetSessionServer returns a session server. - GetSessionServer() rsession.Service - // GetDataDir returns data directory of the server GetDataDir() string diff --git a/lib/srv/forward/sshserver.go b/lib/srv/forward/sshserver.go index efc100f476e63..a7af5ca41cf13 100644 --- a/lib/srv/forward/sshserver.go +++ b/lib/srv/forward/sshserver.go @@ -38,7 +38,6 @@ import ( "github.com/gravitational/teleport/lib/pam" restricted "github.com/gravitational/teleport/lib/restrictedsession" "github.com/gravitational/teleport/lib/services" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/sshutils/x11" @@ -140,7 +139,6 @@ type Server struct { authClient auth.ClientI authService srv.AccessPoint sessionRegistry *srv.SessionRegistry - sessionServer session.Service dataDir string clock clockwork.Clock @@ -300,7 +298,6 @@ func New(c ServerConfig) (*Server, error) { address: c.Address, authClient: c.AuthClient, authService: c.AuthClient, - sessionServer: c.AuthClient, dataDir: c.DataDir, clock: c.Clock, hostUUID: c.HostUUID, @@ -414,11 +411,6 @@ func (s *Server) GetAccessPoint() srv.AccessPoint { return s.authService } -// GetSessionServer returns a session server. -func (s *Server) GetSessionServer() session.Service { - return s.sessionServer -} - // GetPAM returns the PAM configuration for a server. Because the forwarding // server runs in-memory, it does not support PAM. func (s *Server) GetPAM() (*pam.Config, error) { diff --git a/lib/srv/mock.go b/lib/srv/mock.go index 04840e1ea0cc0..d04ea13dfec4e 100644 --- a/lib/srv/mock.go +++ b/lib/srv/mock.go @@ -38,7 +38,6 @@ import ( "github.com/gravitational/teleport/lib/pam" restricted "github.com/gravitational/teleport/lib/restrictedsession" "github.com/gravitational/teleport/lib/services" - rsession "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/trace" @@ -178,11 +177,6 @@ func (m *mockServer) GetAccessPoint() AccessPoint { return m.auth } -// GetSessionServer returns a session server. -func (m *mockServer) GetSessionServer() rsession.Service { - return rsession.NewDiscardSessionServer() -} - // GetDataDir returns data directory of the server func (m *mockServer) GetDataDir() string { return "testDataDir" diff --git a/lib/srv/regular/sshserver.go b/lib/srv/regular/sshserver.go index 89e84b8365bcd..44cb535de44bd 100644 --- a/lib/srv/regular/sshserver.go +++ b/lib/srv/regular/sshserver.go @@ -50,7 +50,6 @@ import ( "github.com/gravitational/teleport/lib/reversetunnel" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" - rsession "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/sshutils/x11" @@ -92,13 +91,12 @@ type Server struct { addr utils.NetAddr hostname string - srv *sshutils.Server - shell string - getRotation services.RotationGetter - authService srv.AccessPoint - reg *srv.SessionRegistry - sessionServer rsession.Service - limiter *limiter.Limiter + srv *sshutils.Server + shell string + getRotation services.RotationGetter + authService srv.AccessPoint + reg *srv.SessionRegistry + limiter *limiter.Limiter inventoryHandle inventory.DownstreamHandle @@ -253,13 +251,6 @@ func (s *Server) GetAccessPoint() srv.AccessPoint { return s.authService } -func (s *Server) GetSessionServer() rsession.Service { - if s.isAuditedAtProxy() { - return rsession.NewDiscardSessionServer() - } - return s.sessionServer -} - // GetUtmpPath returns the optional override of the utmp and wtmp path. func (s *Server) GetUtmpPath() (string, string) { return s.utmpPath, s.wtmpPath @@ -303,24 +294,6 @@ func (s *Server) GetHostUsers() srv.HostUsers { return s.users } -// isAuditedAtProxy returns true if sessions are being recorded at the proxy -// and this is a Teleport node. -func (s *Server) isAuditedAtProxy() bool { - // always be safe, better to double record than not record at all - recConfig, err := s.GetAccessPoint().GetSessionRecordingConfig(s.ctx) - if err != nil { - return false - } - - isRecordAtProxy := services.IsRecordAtProxy(recConfig.GetMode()) - isTeleportNode := s.Component() == teleport.ComponentNode - - if isRecordAtProxy && isTeleportNode { - return true - } - return false -} - // ServerOption is a functional option passed to the server type ServerOption func(s *Server) error @@ -444,14 +417,6 @@ func SetShell(shell string) ServerOption { } } -// SetSessionServer represents realtime session registry server -func SetSessionServer(sessionServer rsession.Service) ServerOption { - return func(s *Server) error { - s.sessionServer = sessionServer - return nil - } -} - // SetProxyMode starts this server in SSH proxying mode func SetProxyMode(peerAddr string, tsrv reversetunnel.Tunnel, ap auth.ReadProxyAccessPoint) ServerOption { return func(s *Server) error { diff --git a/lib/srv/regular/sshserver_test.go b/lib/srv/regular/sshserver_test.go index 9167df01bcf27..fcbbd7127c7f1 100644 --- a/lib/srv/regular/sshserver_test.go +++ b/lib/srv/regular/sshserver_test.go @@ -185,7 +185,6 @@ func newCustomFixture(t *testing.T, mutateCfg func(*auth.TestServerConfig), sshO SetNamespace(apidefaults.Namespace), SetEmitter(nodeClient), SetShell("/bin/sh"), - SetSessionServer(nodeClient), SetPAMConfig(&pam.Config{Enabled: false}), SetLabels( map[string]string{"foo": "bar"}, @@ -1193,7 +1192,6 @@ func TestProxyRoundRobin(t *testing.T) { utils.NetAddr{}, proxyClient, SetProxyMode("", reverseTunnelServer, proxyClient), - SetSessionServer(proxyClient), SetEmitter(nodeClient), SetNamespace(apidefaults.Namespace), SetPAMConfig(&pam.Config{Enabled: false}), @@ -1314,7 +1312,6 @@ func TestProxyDirectAccess(t *testing.T) { utils.NetAddr{}, proxyClient, SetProxyMode("", reverseTunnelServer, proxyClient), - SetSessionServer(proxyClient), SetEmitter(nodeClient), SetNamespace(apidefaults.Namespace), SetPAMConfig(&pam.Config{Enabled: false}), @@ -1485,7 +1482,6 @@ func TestLimiter(t *testing.T) { nodeClient, SetLimiter(limiter), SetShell("/bin/sh"), - SetSessionServer(nodeClient), SetEmitter(nodeClient), SetNamespace(apidefaults.Namespace), SetPAMConfig(&pam.Config{Enabled: false}), @@ -1973,7 +1969,6 @@ func TestIgnorePuTTYSimpleChannel(t *testing.T) { utils.NetAddr{}, proxyClient, SetProxyMode("", reverseTunnelServer, proxyClient), - SetSessionServer(proxyClient), SetEmitter(nodeClient), SetNamespace(apidefaults.Namespace), SetPAMConfig(&pam.Config{Enabled: false}), diff --git a/lib/srv/sess.go b/lib/srv/sess.go index 45b2cc9c8a25c..04c8eaaaf93ea 100644 --- a/lib/srv/sess.go +++ b/lib/srv/sess.go @@ -115,10 +115,6 @@ func (sc *SessionRegistryConfig) CheckAndSetDefaults() error { return trace.BadParameter("server is required") } - if sc.Srv.GetSessionServer() == nil { - return trace.BadParameter("session server is required") - } - if sc.clock == nil { sc.clock = sc.Srv.GetClock() } @@ -528,32 +524,6 @@ func newSession(ctx context.Context, id rsession.ID, r *SessionRegistry, scx *Se rsess.TerminalParams.H = int(winsize.Height) } - // get the session server where session information lives. if the recording - // proxy is being used and this is a node, then a discard session server will - // be returned here. - sessionServer := r.Srv.GetSessionServer() - - err := sessionServer.CreateSession(ctx, rsess) - if err != nil { - if trace.IsAlreadyExists(err) { - // if session already exists, make sure they are compatible - // Login matches existing login - existing, err := sessionServer.GetSession(ctx, r.Srv.GetNamespace(), id) - if err != nil { - return nil, trace.Wrap(err) - } - if existing.Login != rsess.Login && rsess.Login != teleport.SSHSessionJoinPrincipal { - return nil, trace.AccessDenied( - "can't switch users from %v to %v for session %v", - rsess.Login, existing.Login, id) - } - } - // return nil, trace.Wrap(err) - // No need to abort. Perhaps the auth server is down? - // Log the error and continue: - r.log.Errorf("Failed to create new session: %v.", err) - } - policySets := scx.Identity.AccessChecker.SessionPolicySets() sess := &session{ @@ -589,6 +559,7 @@ func newSession(ctx context.Context, id rsession.ID, r *SessionRegistry, scx *Se } }() + var err error if err = sess.trackSession(scx.Identity.TeleportUser, policySets); err != nil { if trace.IsNotImplemented(err) { return nil, trace.NotImplemented("Attempted to use Moderated Sessions with an Auth Server below the minimum version of 9.0.0.") @@ -686,16 +657,6 @@ func (s *session) Close() error { s.registry.removeSession(s) - // Remove the session from the backend. - if s.scx.srv.GetSessionServer() != nil { - err := s.scx.srv.GetSessionServer().DeleteSession(s.serverCtx, s.scx.srv.GetNamespace(), s.id) - if err != nil { - s.log.Errorf("Failed to remove active session: %v: %v. "+ - "Access to backend may be degraded, check connectivity to backend.", - s.id, err) - } - } - // Complete the session recording if recorder := s.Recorder(); recorder != nil { if err := recorder.Complete(s.serverCtx); err != nil { @@ -1032,10 +993,6 @@ func (s *session) startInteractive(ctx context.Context, ch ssh.Channel, scx *Ser scx.Debug("Got continue signal") - // Start a heartbeat that marks this session as active with current members - // of party in the backend. - go s.heartbeat(ctx, scx) - // wait for exec.Cmd (or receipt of "exit-status" for a forwarding node), // once it is received wait for the io.Copy above to finish, then broadcast // the "exit-status" to the client. @@ -1391,71 +1348,6 @@ func (s *session) lingerAndDie(ctx context.Context, party *party) { } } -// exportPartyMembers exports participants in the in-memory map of party -// members. -func (s *session) exportPartyMembers() []rsession.Party { - s.mu.Lock() - defer s.mu.Unlock() - - var partyList []rsession.Party - for _, p := range s.parties { - partyList = append(partyList, rsession.Party{ - ID: p.id, - User: p.user, - ServerID: p.serverID, - RemoteAddr: p.site, - LastActive: p.getLastActive(), - }) - } - - return partyList -} - -// heartbeat will loop as long as the session is not closed and mark it as -// active and update the list of party members. If the session are recorded at -// the proxy, then this function does nothing as it's counterpart -// in the proxy will do this work. -func (s *session) heartbeat(ctx context.Context, scx *ServerContext) { - // If sessions are being recorded at the proxy, an identical version of this - // goroutine is running in the proxy, which means it does not need to run here. - if services.IsRecordAtProxy(scx.SessionRecordingConfig.GetMode()) && - s.registry.Srv.Component() == teleport.ComponentNode { - return - } - - // If no session server (endpoint interface for active sessions) is passed in - // (for example Teleconsole does this) then nothing to sync. - sessionServer := s.registry.Srv.GetSessionServer() - if sessionServer == nil { - return - } - - s.log.Debugf("Starting poll and sync of terminal size to all parties.") - defer s.log.Debugf("Stopping poll and sync of terminal size to all parties.") - - tickerCh := time.NewTicker(defaults.SessionRefreshPeriod) - defer tickerCh.Stop() - - // Loop as long as the session is active, updating the session in the backend. - for { - select { - case <-tickerCh.C: - partyList := s.exportPartyMembers() - - err := sessionServer.UpdateSession(ctx, rsession.UpdateRequest{ - Namespace: scx.srv.GetNamespace(), - ID: s.id, - Parties: &partyList, - }) - if err != nil { - s.log.Warnf("Unable to update session %v as active: %v", s.id, err) - } - case <-s.stopC: - return - } - } -} - func (s *session) checkPresence() error { s.mu.Lock() defer s.mu.Unlock() @@ -1679,12 +1571,6 @@ func (p *party) updateActivity() { p.lastActive = time.Now() } -func (p *party) getLastActive() time.Time { - p.Lock() - defer p.Unlock() - return p.lastActive -} - func (p *party) Read(bytes []byte) (int, error) { p.updateActivity() return p.ch.Read(bytes) @@ -1745,7 +1631,13 @@ func (s *session) trackSession(teleportUser string, policySet []*types.SessionTr s.log.Debug("Creating session tracker") var err error - s.tracker, err = NewSessionTracker(s.serverCtx, trackerSpec, s.registry.SessionTrackerService) + + // if doing proxy recording, don't propagate tracker to the cluster level + if s.registry.Srv.Component() == teleport.ComponentNode && services.IsRecordAtProxy(s.scx.SessionRecordingConfig.GetMode()) { + s.tracker, err = NewSessionTracker(s.serverCtx, trackerSpec, nil) + } else { + s.tracker, err = NewSessionTracker(s.serverCtx, trackerSpec, s.registry.SessionTrackerService) + } if err != nil { return trace.Wrap(err) } diff --git a/lib/srv/sessiontracker.go b/lib/srv/sessiontracker.go index 51be96a7829a9..872b0f9ae1137 100644 --- a/lib/srv/sessiontracker.go +++ b/lib/srv/sessiontracker.go @@ -45,17 +45,15 @@ type SessionTracker struct { // NewSessionTracker returns a new SessionTracker for the given types.SessionTracker func NewSessionTracker(ctx context.Context, trackerSpec types.SessionTrackerSpecV1, service services.SessionTrackerService) (*SessionTracker, error) { - if service == nil { - return nil, trace.BadParameter("missing parameter service") - } - t, err := types.NewSessionTracker(trackerSpec) if err != nil { return nil, trace.Wrap(err) } - if t, err = service.CreateSessionTracker(ctx, t); err != nil { - return nil, trace.Wrap(err) + if service != nil { + if t, err = service.CreateSessionTracker(ctx, t); err != nil { + return nil, trace.Wrap(err) + } } return &SessionTracker{ @@ -106,15 +104,20 @@ func (s *SessionTracker) UpdateExpiration(ctx context.Context, expiry time.Time) s.tracker.SetExpiry(expiry) s.trackerCond.Broadcast() - err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ - SessionID: s.tracker.GetSessionID(), - Update: &proto.UpdateSessionTrackerRequest_UpdateExpiry{ - UpdateExpiry: &proto.SessionTrackerUpdateExpiry{ - Expires: &expiry, + if s.service != nil { + err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ + SessionID: s.tracker.GetSessionID(), + Update: &proto.UpdateSessionTrackerRequest_UpdateExpiry{ + UpdateExpiry: &proto.SessionTrackerUpdateExpiry{ + Expires: &expiry, + }, }, - }, - }) - return trace.Wrap(err) + }) + + return trace.Wrap(err) + } + + return nil } func (s *SessionTracker) AddParticipant(ctx context.Context, p *types.Participant) error { @@ -123,15 +126,20 @@ func (s *SessionTracker) AddParticipant(ctx context.Context, p *types.Participan s.tracker.AddParticipant(*p) s.trackerCond.Broadcast() - err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ - SessionID: s.tracker.GetSessionID(), - Update: &proto.UpdateSessionTrackerRequest_AddParticipant{ - AddParticipant: &proto.SessionTrackerAddParticipant{ - Participant: p, + if s.service != nil { + err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ + SessionID: s.tracker.GetSessionID(), + Update: &proto.UpdateSessionTrackerRequest_AddParticipant{ + AddParticipant: &proto.SessionTrackerAddParticipant{ + Participant: p, + }, }, - }, - }) - return trace.Wrap(err) + }) + + return trace.Wrap(err) + } + + return nil } func (s *SessionTracker) RemoveParticipant(ctx context.Context, participantID string) error { @@ -140,15 +148,20 @@ func (s *SessionTracker) RemoveParticipant(ctx context.Context, participantID st s.tracker.RemoveParticipant(participantID) s.trackerCond.Broadcast() - err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ - SessionID: s.tracker.GetSessionID(), - Update: &proto.UpdateSessionTrackerRequest_RemoveParticipant{ - RemoveParticipant: &proto.SessionTrackerRemoveParticipant{ - ParticipantID: participantID, + if s.service != nil { + err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ + SessionID: s.tracker.GetSessionID(), + Update: &proto.UpdateSessionTrackerRequest_RemoveParticipant{ + RemoveParticipant: &proto.SessionTrackerRemoveParticipant{ + ParticipantID: participantID, + }, }, - }, - }) - return trace.Wrap(err) + }) + + return trace.Wrap(err) + } + + return nil } func (s *SessionTracker) UpdateState(ctx context.Context, state types.SessionState) error { @@ -157,15 +170,20 @@ func (s *SessionTracker) UpdateState(ctx context.Context, state types.SessionSta s.tracker.SetState(state) s.trackerCond.Broadcast() - err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ - SessionID: s.tracker.GetSessionID(), - Update: &proto.UpdateSessionTrackerRequest_UpdateState{ - UpdateState: &proto.SessionTrackerUpdateState{ - State: state, + if s.service != nil { + err := s.service.UpdateSessionTracker(ctx, &proto.UpdateSessionTrackerRequest{ + SessionID: s.tracker.GetSessionID(), + Update: &proto.UpdateSessionTrackerRequest_UpdateState{ + UpdateState: &proto.SessionTrackerUpdateState{ + State: state, + }, }, - }, - }) - return trace.Wrap(err) + }) + + return trace.Wrap(err) + } + + return nil } // WaitForStateUpdate waits for the tracker's state to be updated and returns the new state. diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index bb187071d9c97..ccbae66d7e307 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -234,7 +234,6 @@ func newWebSuite(t *testing.T) *WebSuite { regular.SetUUID(nodeID), regular.SetNamespace(apidefaults.Namespace), regular.SetShell("/bin/sh"), - regular.SetSessionServer(nodeClient), regular.SetEmitter(nodeClient), regular.SetPAMConfig(&pam.Config{Enabled: false}), regular.SetBPF(&bpf.NOP{}), @@ -319,7 +318,6 @@ func newWebSuite(t *testing.T) *WebSuite { s.proxyClient, regular.SetUUID(proxyID), regular.SetProxyMode("", revTunServer, s.proxyClient), - regular.SetSessionServer(s.proxyClient), regular.SetEmitter(s.proxyClient), regular.SetNamespace(apidefaults.Namespace), regular.SetBPF(&bpf.NOP{}), @@ -3991,7 +3989,6 @@ func newWebPack(t *testing.T, numProxies int) *webPack { regular.SetUUID(nodeID), regular.SetNamespace(apidefaults.Namespace), regular.SetShell("/bin/sh"), - regular.SetSessionServer(nodeClient), regular.SetEmitter(nodeClient), regular.SetPAMConfig(&pam.Config{Enabled: false}), regular.SetBPF(&bpf.NOP{}), @@ -4107,7 +4104,6 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula client, regular.SetUUID(proxyID), regular.SetProxyMode("", revTunServer, client), - regular.SetSessionServer(client), regular.SetEmitter(client), regular.SetNamespace(apidefaults.Namespace), regular.SetBPF(&bpf.NOP{}), diff --git a/webassets b/webassets index 065c868116f71..614306f1095be 160000 --- a/webassets +++ b/webassets @@ -1 +1 @@ -Subproject commit 065c868116f71f6101976e89966002264104148d +Subproject commit 614306f1095be9da0893dd8d6a4e3c1d94799a39