Skip to content

Commit

Permalink
[v14] Add Access Monitoring Ping Auth Response Feature flag (#33585)
Browse files Browse the repository at this point in the history
  • Loading branch information
smallinsky committed Oct 17, 2023
1 parent 5d109b1 commit 967165c
Show file tree
Hide file tree
Showing 12 changed files with 954 additions and 847 deletions.
1,668 changes: 857 additions & 811 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ message Features {
AccessRequestsFeature AccessRequests = 21 [(gogoproto.jsontag) = "access_requests,omitempty"];
// CustomTheme holds the name of WebUI custom theme.
string CustomTheme = 22 [(gogoproto.jsontag) = "custom_theme,omitempty"];
// IdentityGovernance holds the Identity Governance feature settings (Access Monitoring)
bool IdentityGovernance = 23 [(gogoproto.jsontag) = "identity_governance,omitempty"];
}

// DeviceTrustFeature holds the Device Trust feature general and usage-based
Expand Down
53 changes: 31 additions & 22 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,27 +342,28 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) {

closeCtx, cancelFunc := context.WithCancel(context.TODO())
as := Server{
bk: cfg.Backend,
clock: cfg.Clock,
limiter: limiter,
Authority: cfg.Authority,
AuthServiceName: cfg.AuthServiceName,
ServerID: cfg.HostUUID,
githubClients: make(map[string]*githubClient),
cancelFunc: cancelFunc,
closeCtx: closeCtx,
emitter: cfg.Emitter,
Streamer: cfg.Streamer,
Unstable: local.NewUnstableService(cfg.Backend, cfg.AssertionReplayService),
Services: services,
Cache: services,
keyStore: keyStore,
traceClient: cfg.TraceClient,
fips: cfg.FIPS,
loadAllCAs: cfg.LoadAllCAs,
httpClientForAWSSTS: cfg.HTTPClientForAWSSTS,
embeddingsRetriever: cfg.EmbeddingRetriever,
embedder: cfg.EmbeddingClient,
bk: cfg.Backend,
clock: cfg.Clock,
limiter: limiter,
Authority: cfg.Authority,
AuthServiceName: cfg.AuthServiceName,
ServerID: cfg.HostUUID,
githubClients: make(map[string]*githubClient),
cancelFunc: cancelFunc,
closeCtx: closeCtx,
emitter: cfg.Emitter,
Streamer: cfg.Streamer,
Unstable: local.NewUnstableService(cfg.Backend, cfg.AssertionReplayService),
Services: services,
Cache: services,
keyStore: keyStore,
traceClient: cfg.TraceClient,
fips: cfg.FIPS,
loadAllCAs: cfg.LoadAllCAs,
httpClientForAWSSTS: cfg.HTTPClientForAWSSTS,
embeddingsRetriever: cfg.EmbeddingRetriever,
embedder: cfg.EmbeddingClient,
accessMonitoringEnabled: cfg.AccessMonitoringEnabled,
}
as.inventory = inventory.NewController(&as, services, inventory.WithAuthServerID(cfg.HostUUID))
for _, o := range opts {
Expand Down Expand Up @@ -787,6 +788,9 @@ type Server struct {

// embedder is an embedder client used to generate embeddings.
embedder embedding.Embedder

// accessMonitoringEnabled is a flag that indicates whether access monitoring is enabled.
accessMonitoringEnabled bool
}

// SetSAMLService registers svc as the SAMLService that provides the SAML
Expand Down Expand Up @@ -5052,11 +5056,16 @@ func (a *Server) Ping(ctx context.Context) (proto.PingResponse, error) {
if err != nil {
return proto.PingResponse{}, trace.Wrap(err)
}
features := modules.GetModules().Features().ToProto()

if a.accessMonitoringEnabled {
features.IdentityGovernance = a.accessMonitoringEnabled
}

return proto.PingResponse{
ClusterName: cn.GetClusterName(),
ServerVersion: teleport.Version,
ServerFeatures: modules.GetModules().Features().ToProto(),
ServerFeatures: features,
ProxyPublicAddr: a.getProxyPublicAddr(),
IsBoring: modules.GetModules().IsBoringBinary(),
LoadAllCAs: a.loadAllCAs,
Expand Down
3 changes: 3 additions & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ type InitConfig struct {

// Tracer used to create spans.
Tracer oteltrace.Tracer

// AccessMonitoringEnabled is true if access monitoring is enabled.
AccessMonitoringEnabled bool
}

// Init instantiates and configures an instance of AuthServer
Expand Down
1 change: 1 addition & 0 deletions lib/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,7 @@ func (process *TeleportProcess) initAuthService() error {
TraceClient: traceClt,
FIPS: cfg.FIPS,
LoadAllCAs: cfg.Auth.LoadAllCAs,
AccessMonitoringEnabled: cfg.Auth.IsAccessMonitoringEnabled(),
Clock: cfg.Clock,
HTTPClientForAWSSTS: cfg.Auth.HTTPClientForAWSSTS,
EmbeddingRetriever: embeddingsRetriever,
Expand Down
5 changes: 5 additions & 0 deletions lib/service/servicecfg/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ type AccessMonitoringOptions struct {
ReportResults string `yaml:"report_results,omitempty"`
}

// IsAccessMonitoringEnabled returns true if access monitoring is enabled.
func (a *AuthConfig) IsAccessMonitoringEnabled() bool {
return a.AccessMonitoring != nil && a.AccessMonitoring.Enabled
}

// CheckAndSetDefaults checks and sets default values for any missing fields.
func (a *AccessMonitoringOptions) CheckAndSetDefaults() error {
var err error
Expand Down
11 changes: 8 additions & 3 deletions lib/services/useracl.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func newAccess(roleSet RoleSet, ctx *Context, kind string) ResourceAccess {
}

// NewUserACL builds an ACL for a user based on their roles.
func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, desktopRecordingEnabled bool) UserACL {
func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, desktopRecordingEnabled, accessMonitoringEnabled bool) UserACL {
ctx := &Context{User: user}
recordedSessionAccess := newAccess(userRoles, ctx, types.KindSession)
activeSessionAccess := newAccess(userRoles, ctx, types.KindSSHSession)
Expand Down Expand Up @@ -168,8 +168,13 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des
discoveryConfigsAccess := newAccess(userRoles, ctx, types.KindDiscoveryConfig)
lockAccess := newAccess(userRoles, ctx, types.KindLock)
accessListAccess := newAccess(userRoles, ctx, types.KindAccessList)
auditQuery := newAccess(userRoles, ctx, types.KindAuditQuery)
securityReports := newAccess(userRoles, ctx, types.KindSecurityReport)

var auditQuery ResourceAccess
var securityReports ResourceAccess
if accessMonitoringEnabled {
auditQuery = newAccess(userRoles, ctx, types.KindAuditQuery)
securityReports = newAccess(userRoles, ctx, types.KindSecurityReport)
}

return UserACL{
AccessRequests: requestAccess,
Expand Down
38 changes: 34 additions & 4 deletions lib/services/useracl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func TestNewUserACL(t *testing.T) {
})

roleSet := []types.Role{role1, role2}
userContext := NewUserACL(user, roleSet, proto.Features{}, true)
userContext := NewUserACL(user, roleSet, proto.Features{}, true, false)

allowedRW := ResourceAccess{true, true, true, true, true, false}
denied := ResourceAccess{false, false, false, false, false, false}
Expand Down Expand Up @@ -102,11 +102,11 @@ func TestNewUserACL(t *testing.T) {
// test enabling of the 'Use' verb
require.Empty(t, cmp.Diff(userContext.Integrations, ResourceAccess{true, true, true, true, true, true}))

userContext = NewUserACL(user, roleSet, proto.Features{Cloud: true}, true)
userContext = NewUserACL(user, roleSet, proto.Features{Cloud: true}, true, false)
require.Empty(t, cmp.Diff(userContext.Billing, ResourceAccess{true, true, false, false, false, false}))

// test that desktopRecordingEnabled being false overrides the roleSet.RecordDesktopSession() returning true
userContext = NewUserACL(user, roleSet, proto.Features{}, false)
userContext = NewUserACL(user, roleSet, proto.Features{}, false, false)
require.Equal(t, userContext.DesktopSessionRecording, false)
}

Expand All @@ -133,7 +133,7 @@ func TestNewUserACLCloud(t *testing.T) {

allowedRW := ResourceAccess{true, true, true, true, true, false}

userContext := NewUserACL(user, roleSet, proto.Features{Cloud: true}, true)
userContext := NewUserACL(user, roleSet, proto.Features{Cloud: true}, true, false)

require.Empty(t, cmp.Diff(userContext.AuthConnectors, allowedRW))
require.Empty(t, cmp.Diff(userContext.TrustedClusters, allowedRW))
Expand All @@ -156,3 +156,33 @@ func TestNewUserACLCloud(t *testing.T) {
require.Empty(t, cmp.Diff(userContext.Billing, allowedRW))
require.Empty(t, cmp.Diff(userContext.Desktops, allowedRW))
}

func TestNewAccessMonitoring(t *testing.T) {
t.Parallel()
user := &types.UserV2{
Metadata: types.Metadata{},
}
role := &types.RoleV6{}
role.SetNamespaces(types.Allow, []string{"*"})
role.SetRules(types.Allow, []types.Rule{
{
Resources: []string{"*"},
Verbs: append(RW(), types.VerbUse),
},
})

roleSet := []types.Role{role}

t.Run("access monitoring enabled", func(t *testing.T) {
allowed := ResourceAccess{true, true, true, true, true, true}
userContext := NewUserACL(user, roleSet, proto.Features{}, false, true)
require.Empty(t, cmp.Diff(userContext.AuditQuery, allowed))
require.Empty(t, cmp.Diff(userContext.SecurityReport, allowed))
})
t.Run("access monitoring disabled", func(t *testing.T) {
allowed := ResourceAccess{false, false, false, false, false, false}
userContext := NewUserACL(user, roleSet, proto.Features{}, false, false)
require.Empty(t, cmp.Diff(userContext.AuditQuery, allowed))
require.Empty(t, cmp.Diff(userContext.SecurityReport, allowed))
})
}
2 changes: 1 addition & 1 deletion lib/teleterm/clusters/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (c *Cluster) GetWithDetails(ctx context.Context) (*ClusterWithDetails, erro
}

roleSet := services.NewRoleSet(roles...)
userACL := services.NewUserACL(user, roleSet, *authPingResponse.ServerFeatures, false)
userACL := services.NewUserACL(user, roleSet, *authPingResponse.ServerFeatures, false, false)

acl = &api.ACL{
RecordedSessions: convertToAPIResourceAccess(userACL.RecordedSessions),
Expand Down
8 changes: 7 additions & 1 deletion lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,13 @@ func (h *Handler) getUserContext(w http.ResponseWriter, r *http.Request, p httpr
}
desktopRecordingEnabled := recConfig.GetMode() != types.RecordOff

userContext, err := ui.NewUserContext(user, accessChecker.Roles(), h.ClusterFeatures, desktopRecordingEnabled)
pingResp, err := clt.Ping(r.Context())
if err != nil {
return nil, trace.Wrap(err)
}
accessMonitoringEnabled := pingResp.ServerFeatures != nil && pingResp.ServerFeatures.IdentityGovernance

userContext, err := ui.NewUserContext(user, accessChecker.Roles(), h.ClusterFeatures, desktopRecordingEnabled, accessMonitoringEnabled)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
4 changes: 2 additions & 2 deletions lib/web/ui/usercontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ func getAccessStrategy(roleset services.RoleSet) accessStrategy {
}

// NewUserContext returns user context
func NewUserContext(user types.User, userRoles services.RoleSet, features proto.Features, desktopRecordingEnabled bool) (*UserContext, error) {
acl := services.NewUserACL(user, userRoles, features, desktopRecordingEnabled)
func NewUserContext(user types.User, userRoles services.RoleSet, features proto.Features, desktopRecordingEnabled, accessMonitoringEnabled bool) (*UserContext, error) {
acl := services.NewUserACL(user, userRoles, features, desktopRecordingEnabled, accessMonitoringEnabled)
accessStrategy := getAccessStrategy(userRoles)

// local user
Expand Down
6 changes: 3 additions & 3 deletions lib/web/ui/usercontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestNewUserContext(t *testing.T) {
role2.SetNamespaces(types.Allow, []string{apidefaults.Namespace})

roleSet := []types.Role{role1, role2}
userContext, err := NewUserContext(user, roleSet, proto.Features{}, true)
userContext, err := NewUserContext(user, roleSet, proto.Features{}, true, false)
require.NoError(t, err)

// test user name
Expand All @@ -59,7 +59,7 @@ func TestNewUserContext(t *testing.T) {

// test sso auth type
user.Spec.GithubIdentities = []types.ExternalIdentity{{ConnectorID: "foo", Username: "bar"}}
userContext, err = NewUserContext(user, roleSet, proto.Features{}, true)
userContext, err = NewUserContext(user, roleSet, proto.Features{}, true, false)
require.NoError(t, err)
require.Equal(t, userContext.AuthType, authSSO)
}
Expand All @@ -78,7 +78,7 @@ func TestNewUserContextCloud(t *testing.T) {

roleSet := []types.Role{role}

userContext, err := NewUserContext(user, roleSet, proto.Features{Cloud: true}, true)
userContext, err := NewUserContext(user, roleSet, proto.Features{Cloud: true}, true, false)
require.NoError(t, err)

require.Equal(t, userContext.Name, "root")
Expand Down

0 comments on commit 967165c

Please sign in to comment.