diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index d60e95fe56b35..03491a3b2f097 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -316,6 +316,8 @@ type ProxySettings struct { TLSRoutingEnabled bool `json:"tls_routing_enabled"` // AssistEnabled is true when Teleport Assist is enabled. AssistEnabled bool `json:"assist_enabled"` + // AccessGraphEnabled is true when Access Graph is enabled. + AccessGraphEnabled bool `json:"access_graph_enabled"` } // KubeProxySettings is kubernetes proxy settings diff --git a/api/client/webclient/webconfig.go b/api/client/webclient/webconfig.go index 91b51128c1bce..8c63bcc0bb552 100644 --- a/api/client/webclient/webconfig.go +++ b/api/client/webclient/webconfig.go @@ -77,6 +77,8 @@ type WebConfig struct { HideInaccessibleFeatures bool `json:"hideInaccessibleFeatures"` // CustomTheme is a string that represents the name of the custom theme that the WebUI should use. CustomTheme string `json:"customTheme"` + // AccessGraphEnabled is true when the access graph feature is enabled. + AccessGraphEnabled bool `json:"accessGraphEnabled"` } // UIConfig provides config options for the web UI served by the proxy service. diff --git a/api/types/constants.go b/api/types/constants.go index bde6e01b7debe..74485a53ec07f 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -436,6 +436,9 @@ const ( // Teleport Assist resources. KindAssistant = "assistant" + // KindAccessGraph is an access to access graph service. + KindAccessGraph = "access_graph" + // KindIntegration is a connection to a 3rd party system API. KindIntegration = "integration" diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 0ac380e52d005..aa556c808499e 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -54,6 +54,7 @@ import ( trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" userloginstatev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/userloginstate/v1" userpreferencespb "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1" + "github.com/gravitational/teleport/api/internalutils/stream" "github.com/gravitational/teleport/api/metadata" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" @@ -377,8 +378,24 @@ func (g *GRPCServer) WatchEvents(watch *authpb.Watch, stream authpb.AuthService_ if err != nil { return trace.Wrap(err) } + + return trace.Wrap(WatchEvents(watch, stream, auth.User.GetName(), auth)) +} + +// WatchEvent is a stream interface for sending events. +type WatchEvent interface { + Context() context.Context + Send(*authpb.Event) error +} + +type Watcher interface { + NewStream(ctx context.Context, watch types.Watch) (stream.Stream[types.Event], error) +} + +// WatchEvents watches for events and streams them to the provided stream. +func WatchEvents(watch *authpb.Watch, stream WatchEvent, componentName string, auth Watcher) error { servicesWatch := types.Watch{ - Name: auth.User.GetName(), + Name: componentName, Kinds: watch.Kinds, AllowPartialSuccess: watch.AllowPartialSuccess, } @@ -418,7 +435,7 @@ func (g *GRPCServer) WatchEvents(watch *authpb.Watch, stream authpb.AuthService_ } } - // defferred cleanup func will inject stream error if needed + // deferred cleanup func will inject stream error if needed return nil } diff --git a/lib/config/configuration.go b/lib/config/configuration.go index bcf8c7095705e..29954a4b81fc8 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -345,6 +345,18 @@ func ApplyFileConfig(fc *FileConfig, cfg *servicecfg.Config) error { if fc.WindowsDesktop.Disabled() { cfg.WindowsDesktop.Enabled = false } + + if fc.AccessGraph.Enabled { + cfg.AccessGraph.Enabled = true + if fc.AccessGraph.Endpoint == "" { + return trace.Errorf("Please, provide access_graph_service.addr configuration variable") + } + cfg.AccessGraph.Addr = fc.AccessGraph.Endpoint + cfg.AccessGraph.CA = fc.AccessGraph.CA + // TODO(tigrato): change this behavior when we drop support for plain text connections + cfg.AccessGraph.Insecure = fc.AccessGraph.Insecure + } + applyString(fc.NodeName, &cfg.Hostname) // apply "advertise_ip" setting: diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index da1f15c9606e3..16ec2e1465ff0 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -96,6 +96,9 @@ type FileConfig struct { // Plugins is the section of the config for configuring the plugin service. Plugins PluginService `yaml:"plugin_service,omitempty"` + + // AccessGraph is the section of the config describing AccessGraph service + AccessGraph AccessGraph `yaml:"access_graph,omitempty"` } // ReadFromFile reads Teleport configuration from a file. Currently only YAML @@ -815,6 +818,18 @@ type PluginService struct { Plugins map[string]string `yaml:"plugins,omitempty"` } +// AccessGraph represents the configuration for the AccessGraph service. +type AccessGraph struct { + // Enabled enables the AccessGraph service. + Enabled bool `yaml:"enabled"` + // Endpoint is the endpoint of the AccessGraph service. + Endpoint string `yaml:"endpoint"` + // CA is the path to the CA certificate for the AccessGraph service. + CA string `yaml:"ca"` + // Insecure is true if the AccessGraph service should not verify the CA. + Insecure bool `yaml:"insecure"` +} + // Opsgenie represents the configuration for the Opsgenie plugin. type Opsgenie struct { // APIKeyFile is the path to a file containing an Opsgenie API key. diff --git a/lib/service/proxy_settings.go b/lib/service/proxy_settings.go index 157077ad64c2a..7abe0056eee50 100644 --- a/lib/service/proxy_settings.go +++ b/lib/service/proxy_settings.go @@ -73,8 +73,9 @@ func (p *proxySettings) GetProxySettings(ctx context.Context) (*webclient.ProxyS // where incoming connections are routed to the proper proxy service based on TLS SNI ALPN routing information. func (p *proxySettings) buildProxySettings(proxyListenerMode types.ProxyListenerMode) *webclient.ProxySettings { proxySettings := webclient.ProxySettings{ - TLSRoutingEnabled: proxyListenerMode == types.ProxyListenerMode_Multiplex, - AssistEnabled: p.cfg.Proxy.AssistAPIKey != "", + TLSRoutingEnabled: proxyListenerMode == types.ProxyListenerMode_Multiplex, + AssistEnabled: p.cfg.Proxy.AssistAPIKey != "", + AccessGraphEnabled: p.cfg.AccessGraph.Enabled, Kube: webclient.KubeProxySettings{ Enabled: p.cfg.Proxy.Kube.Enabled, }, diff --git a/lib/service/service.go b/lib/service/service.go index 6f4a31b36f097..55af1167da13b 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -4027,6 +4027,15 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { traceClt = clt } + var accessGraphAddr utils.NetAddr + if cfg.AccessGraph.Enabled { + addr, err := utils.ParseAddr(cfg.AccessGraph.Addr) + if err != nil { + return trace.Wrap(err) + } + accessGraphAddr = *addr + } + webConfig := web.Config{ Proxy: tsrv, AuthServers: cfg.AuthServerAddresses()[0], @@ -4056,10 +4065,11 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { ctx, err := controller(ctx, sctx, login, localAddr, remoteAddr) return ctx, trace.Wrap(err) }), - PROXYSigner: proxySigner, - OpenAIConfig: cfg.OpenAIConfig, - NodeWatcher: nodeWatcher, - TracerProvider: process.TracingProvider, + PROXYSigner: proxySigner, + OpenAIConfig: cfg.OpenAIConfig, + NodeWatcher: nodeWatcher, + AccessGraphAddr: accessGraphAddr, + TracerProvider: process.TracingProvider, } webHandler, err := web.NewHandler(webConfig) if err != nil { diff --git a/lib/service/servicecfg/config.go b/lib/service/servicecfg/config.go index bc4a49058871b..889d002df8433 100644 --- a/lib/service/servicecfg/config.go +++ b/lib/service/servicecfg/config.go @@ -268,6 +268,9 @@ type Config struct { // Options provide a way to customize behavior of service initialization. Options []Option + // AccessGraph represents AccessGraph server config + AccessGraph AccessGraphConfig + // token is either the token needed to join the auth server, or a path pointing to a file // that contains the token // @@ -308,6 +311,21 @@ func WithKubeMultiplexerIgnoreSelfConnectionsOption() KubeMultiplexerIgnoreSelfC return KubeMultiplexerIgnoreSelfConnectionsOption{} } +// AccessGraphConfig represents TAG server config +type AccessGraphConfig struct { + // Enabled Access Graph reporting enabled + Enabled bool + + // Addr of the Access Graph service addr + Addr string + + // CA is the path to the CA certificate file + CA string + + // Insecure is true if the connection to the Access Graph service should be insecure + Insecure bool +} + // RoleAndIdentityEvent is a role and its corresponding identity event. type RoleAndIdentityEvent struct { // Role is a system role. diff --git a/lib/services/presets.go b/lib/services/presets.go index a9d584bfe2730..21c2d92113112 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -165,6 +165,7 @@ func NewPresetEditorRole() types.Role { types.NewRule(types.KindDiscoveryConfig, RW()), types.NewRule(types.KindSecurityReport, append(RW(), types.VerbUse)), types.NewRule(types.KindAuditQuery, append(RW(), types.VerbUse)), + types.NewRule(types.KindAccessGraph, RW()), }, }, }, diff --git a/lib/services/role_test.go b/lib/services/role_test.go index 4942ad07bbbed..11f9a681a3605 100644 --- a/lib/services/role_test.go +++ b/lib/services/role_test.go @@ -7574,6 +7574,10 @@ func (u mockCurrentUser) GetTraits() map[string][]string { return u.traits } +func (u mockCurrentUser) GetName() string { + return "mockCurrentUser" +} + func TestNewAccessCheckerForRemoteCluster(t *testing.T) { user := mockCurrentUser{ roles: []string{"dev", "admin"}, diff --git a/lib/services/useracl.go b/lib/services/useracl.go index 5b76bd3466d17..6052d6bdc2269 100644 --- a/lib/services/useracl.go +++ b/lib/services/useracl.go @@ -98,6 +98,8 @@ type UserACL struct { AuditQuery ResourceAccess `json:"auditQuery"` // SecurityReport defines access to security reports. SecurityReport ResourceAccess `json:"securityReport"` + // AccessGraph defines access to access graph. + AccessGraph ResourceAccess `json:"accessGraph"` } func hasAccess(roleSet RoleSet, ctx *Context, kind string, verbs ...string) bool { @@ -142,6 +144,7 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des desktopAccess := newAccess(userRoles, ctx, types.KindWindowsDesktop) cnDiagnosticAccess := newAccess(userRoles, ctx, types.KindConnectionDiagnostic) samlIdpServiceProviderAccess := newAccess(userRoles, ctx, types.KindSAMLIdPServiceProvider) + accessGraphAccess := newAccess(userRoles, ctx, types.KindAccessGraph) var assistAccess ResourceAccess if features.Assist { @@ -209,5 +212,6 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des AccessList: accessListAccess, AuditQuery: auditQuery, SecurityReport: securityReports, + AccessGraph: accessGraphAccess, } } diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index f4e7450ecddc8..d1c49028fd2b0 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -278,6 +278,9 @@ type Config struct { // Eg, v13.4.3 // Optional: uses cloud/stable channel when omitted. AutomaticUpgradesVersionURL string + + // AccessGraphAddr is the address of the Access Graph service GRPC API + AccessGraphAddr utils.NetAddr } // SetDefaults ensures proper default values are set if @@ -1009,6 +1012,11 @@ func (h *Handler) PublicProxyAddr() string { return h.cfg.PublicProxyAddr } +// AccessGraphAddr returns the TAG API address +func (h *Handler) AccessGraphAddr() utils.NetAddr { + return h.cfg.AccessGraphAddr +} + func localSettings(cap types.AuthPreference) (webclient.AuthenticationSettings, error) { as := webclient.AuthenticationSettings{ Type: constants.Local, @@ -1499,6 +1507,7 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou // get tunnel address to display on cloud instances tunnelPublicAddr := "" assistEnabled := false // TODO(jakule) remove when plugins are implemented + accessGraphEnabled := false proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(r.Context()) if err != nil { h.log.WithError(err).Warn("Cannot retrieve ProxySettings, tunnel address won't be set in Web UI.") @@ -1516,6 +1525,7 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou // disable if auth doesn't support assist assistEnabled = enabled.Enabled } + accessGraphEnabled = proxyConfig.AccessGraphEnabled } // disable joining sessions if proxy session recording is enabled @@ -1548,6 +1558,7 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou AutomaticUpgrades: automaticUpgradesEnabled, AutomaticUpgradesTargetVersion: automaticUpgradesTargetVersion, AssistEnabled: assistEnabled, + AccessGraphEnabled: accessGraphEnabled, HideInaccessibleFeatures: clusterFeatures.GetFeatureHiding(), CustomTheme: clusterFeatures.GetCustomTheme(), }