diff --git a/lib/auth/auth.go b/lib/auth/auth.go
index f6497fd1e2c8..d07263676e9f 100644
--- a/lib/auth/auth.go
+++ b/lib/auth/auth.go
@@ -193,6 +193,12 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) {
}
cfg.ClusterConfiguration = clusterConfig
}
+ if cfg.AutoUpdateService == nil {
+ cfg.AutoUpdateService, err = local.NewAutoUpdateService(cfg.Backend)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ }
if cfg.Restrictions == nil {
cfg.Restrictions = local.NewRestrictionsService(cfg.Backend)
}
@@ -398,6 +404,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) {
Access: cfg.Access,
DynamicAccessExt: cfg.DynamicAccessExt,
ClusterConfiguration: cfg.ClusterConfiguration,
+ AutoUpdateService: cfg.AutoUpdateService,
Restrictions: cfg.Restrictions,
Apps: cfg.Apps,
Kubernetes: cfg.Kubernetes,
@@ -629,6 +636,7 @@ type Services struct {
services.SPIFFEFederations
services.AccessGraphSecretsGetter
services.DevicesGetter
+ services.AutoUpdateService
}
// GetWebSession returns existing web session described by req.
diff --git a/lib/auth/autoupdate/autoupdatev1/service.go b/lib/auth/autoupdate/autoupdatev1/service.go
new file mode 100644
index 000000000000..555a03506d55
--- /dev/null
+++ b/lib/auth/autoupdate/autoupdatev1/service.go
@@ -0,0 +1,270 @@
+/*
+ * Teleport
+ * Copyright (C) 2024 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package autoupdatev1
+
+import (
+ "context"
+
+ "github.com/gravitational/trace"
+ "google.golang.org/protobuf/types/known/emptypb"
+
+ "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/authz"
+ "github.com/gravitational/teleport/lib/services"
+)
+
+// Cache defines only read-only service methods.
+type Cache interface {
+ // GetAutoUpdateConfig gets the autoupdate configuration from the backend.
+ GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error)
+
+ // GetAutoUpdateVersion gets the autoupdate version from the backend.
+ GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error)
+}
+
+// ServiceConfig holds configuration options for the autoupdate gRPC service.
+type ServiceConfig struct {
+ // Authorizer is the authorizer used to check access to resources.
+ Authorizer authz.Authorizer
+ // Backend is the backend used to store autoupdate resources.
+ Backend services.AutoUpdateService
+ // Cache is the cache used to store autoupdate resources.
+ Cache Cache
+}
+
+// Service implements the gRPC API layer for the Autoupdate.
+type Service struct {
+ autoupdate.UnimplementedAutoUpdateServiceServer
+
+ authorizer authz.Authorizer
+ backend services.AutoUpdateService
+ cache Cache
+}
+
+// NewService returns a new Autoupdate API service using the given storage layer and authorizer.
+func NewService(cfg ServiceConfig) (*Service, error) {
+ switch {
+ case cfg.Backend == nil:
+ return nil, trace.BadParameter("backend is required")
+ case cfg.Authorizer == nil:
+ return nil, trace.BadParameter("authorizer is required")
+ case cfg.Cache == nil:
+ return nil, trace.BadParameter("cache is required")
+ }
+ return &Service{
+ authorizer: cfg.Authorizer,
+ backend: cfg.Backend,
+ cache: cfg.Cache,
+ }, nil
+}
+
+// GetAutoUpdateConfig gets the current autoupdate config singleton.
+func (s *Service) GetAutoUpdateConfig(ctx context.Context, req *autoupdate.GetAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbRead); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ config, err := s.cache.GetAutoUpdateConfig(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ return config, nil
+}
+
+// CreateAutoUpdateConfig creates autoupdate config singleton.
+func (s *Service) CreateAutoUpdateConfig(ctx context.Context, req *autoupdate.CreateAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbCreate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ config, err := s.backend.CreateAutoUpdateConfig(ctx, req.Config)
+ return config, trace.Wrap(err)
+}
+
+// UpdateAutoUpdateConfig updates autoupdate config singleton.
+func (s *Service) UpdateAutoUpdateConfig(ctx context.Context, req *autoupdate.UpdateAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbUpdate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ config, err := s.backend.UpdateAutoUpdateConfig(ctx, req.Config)
+ return config, trace.Wrap(err)
+}
+
+// UpsertAutoUpdateConfig updates or creates autoupdate config singleton.
+func (s *Service) UpsertAutoUpdateConfig(ctx context.Context, req *autoupdate.UpsertAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbCreate, types.VerbUpdate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ config, err := s.backend.UpsertAutoUpdateConfig(ctx, req.Config)
+ return config, trace.Wrap(err)
+}
+
+// DeleteAutoUpdateConfig deletes autoupdate config singleton.
+func (s *Service) DeleteAutoUpdateConfig(ctx context.Context, req *autoupdate.DeleteAutoUpdateConfigRequest) (*emptypb.Empty, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbDelete); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminAction(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := s.backend.DeleteAutoUpdateConfig(ctx); err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return &emptypb.Empty{}, nil
+}
+
+// GetAutoUpdateVersion gets the current autoupdate version singleton.
+func (s *Service) GetAutoUpdateVersion(ctx context.Context, req *autoupdate.GetAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbRead); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ version, err := s.cache.GetAutoUpdateVersion(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ return version, nil
+}
+
+// CreateAutoUpdateVersion creates autoupdate version singleton.
+func (s *Service) CreateAutoUpdateVersion(ctx context.Context, req *autoupdate.CreateAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ autoupdateVersion, err := s.backend.CreateAutoUpdateVersion(ctx, req.Version)
+ return autoupdateVersion, trace.Wrap(err)
+}
+
+// UpdateAutoUpdateVersion updates autoupdate version singleton.
+func (s *Service) UpdateAutoUpdateVersion(ctx context.Context, req *autoupdate.UpdateAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbUpdate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ autoupdateVersion, err := s.backend.UpdateAutoUpdateVersion(ctx, req.Version)
+ return autoupdateVersion, trace.Wrap(err)
+}
+
+// UpsertAutoUpdateVersion updates or creates autoupdate version singleton.
+func (s *Service) UpsertAutoUpdateVersion(ctx context.Context, req *autoupdate.UpsertAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate, types.VerbUpdate); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ autoupdateVersion, err := s.backend.UpsertAutoUpdateVersion(ctx, req.Version)
+ return autoupdateVersion, trace.Wrap(err)
+}
+
+// DeleteAutoUpdateVersion deletes autoupdate version singleton.
+func (s *Service) DeleteAutoUpdateVersion(ctx context.Context, req *autoupdate.DeleteAutoUpdateVersionRequest) (*emptypb.Empty, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbDelete); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := authCtx.AuthorizeAdminAction(); err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if err := s.backend.DeleteAutoUpdateVersion(ctx); err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return &emptypb.Empty{}, nil
+}
diff --git a/lib/auth/autoupdate/autoupdatev1/service_test.go b/lib/auth/autoupdate/autoupdatev1/service_test.go
new file mode 100644
index 000000000000..840fd9bbf94c
--- /dev/null
+++ b/lib/auth/autoupdate/autoupdatev1/service_test.go
@@ -0,0 +1,278 @@
+// Teleport
+// Copyright (C) 2024 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package autoupdatev1
+
+import (
+ "context"
+ "fmt"
+ "slices"
+ "testing"
+
+ "github.com/gravitational/trace"
+ "github.com/stretchr/testify/require"
+
+ "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/authz"
+ "github.com/gravitational/teleport/lib/backend/memory"
+ "github.com/gravitational/teleport/lib/services"
+ "github.com/gravitational/teleport/lib/services/local"
+ "github.com/gravitational/teleport/lib/utils"
+)
+
+var allAdminStates = map[authz.AdminActionAuthState]string{
+ authz.AdminActionAuthUnauthorized: "Unauthorized",
+ authz.AdminActionAuthNotRequired: "NotRequired",
+ authz.AdminActionAuthMFAVerified: "MFAVerified",
+ authz.AdminActionAuthMFAVerifiedWithReuse: "MFAVerifiedWithReuse",
+}
+
+func stateToString(state authz.AdminActionAuthState) string {
+ str, ok := allAdminStates[state]
+ if !ok {
+ return fmt.Sprintf("unknown(%v)", state)
+ }
+ return str
+}
+
+// otherAdminStates returns all admin states except for those passed in
+func otherAdminStates(states []authz.AdminActionAuthState) []authz.AdminActionAuthState {
+ var out []authz.AdminActionAuthState
+ for state := range allAdminStates {
+ found := slices.Index(states, state) != -1
+ if !found {
+ out = append(out, state)
+ }
+ }
+ return out
+}
+
+// callMethod calls a method with given name in the DatabaseObjectService service
+func callMethod(t *testing.T, service *Service, method string) error {
+ for _, desc := range autoupdate.AutoUpdateService_ServiceDesc.Methods {
+ if desc.MethodName == method {
+ _, err := desc.Handler(service, context.Background(), func(_ any) error { return nil }, nil)
+ return err
+ }
+ }
+ require.FailNow(t, "method %v not found", method)
+ panic("this line should never be reached: FailNow() should interrupt the test")
+}
+
+func TestServiceAccess(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ name string
+ allowedVerbs []string
+ allowedStates []authz.AdminActionAuthState
+ disallowedStates []authz.AdminActionAuthState
+ }{
+ {
+ name: "CreateAutoUpdateConfig",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbCreate},
+ },
+ {
+ name: "UpdateAutoUpdateConfig",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbUpdate},
+ },
+ {
+ name: "UpsertAutoUpdateConfig",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbUpdate, types.VerbCreate},
+ },
+ {
+ name: "GetAutoUpdateConfig",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthUnauthorized,
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbRead},
+ },
+ {
+ name: "DeleteAutoUpdateConfig",
+ allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified},
+ allowedVerbs: []string{types.VerbDelete},
+ },
+ // AutoUpdate version check.
+ {
+ name: "CreateAutoUpdateVersion",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbCreate},
+ },
+ {
+ name: "UpdateAutoUpdateVersion",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbUpdate},
+ },
+ {
+ name: "UpsertAutoUpdateVersion",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbUpdate, types.VerbCreate},
+ },
+ {
+ name: "GetAutoUpdateVersion",
+ allowedStates: []authz.AdminActionAuthState{
+ authz.AdminActionAuthUnauthorized,
+ authz.AdminActionAuthNotRequired,
+ authz.AdminActionAuthMFAVerified,
+ authz.AdminActionAuthMFAVerifiedWithReuse,
+ },
+ allowedVerbs: []string{types.VerbRead},
+ },
+ {
+ name: "DeleteAutoUpdateVersion",
+ allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified},
+ allowedVerbs: []string{types.VerbDelete},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ // test the method with allowed admin states, each one separately.
+ t.Run("allowed admin states", func(t *testing.T) {
+ for _, state := range tt.allowedStates {
+ t.Run(stateToString(state), func(t *testing.T) {
+ for _, verbs := range utils.Combinations(tt.allowedVerbs) {
+ t.Run(fmt.Sprintf("verbs=%v", verbs), func(t *testing.T) {
+ service := newService(t, state, fakeChecker{allowedVerbs: verbs})
+ err := callMethod(t, service, tt.name)
+ // expect access denied except with full set of verbs.
+ if len(verbs) == len(tt.allowedVerbs) {
+ require.False(t, trace.IsAccessDenied(err))
+ } else {
+ require.True(t, trace.IsAccessDenied(err), "expected access denied for verbs %v, got err=%v", verbs, err)
+ }
+ })
+ }
+ })
+ }
+ })
+
+ // test the method with disallowed admin states; expect failures.
+ t.Run("disallowed admin states", func(t *testing.T) {
+ disallowedStates := otherAdminStates(tt.allowedStates)
+ if tt.disallowedStates != nil {
+ disallowedStates = tt.disallowedStates
+ }
+ for _, state := range disallowedStates {
+ t.Run(stateToString(state), func(t *testing.T) {
+ // it is enough to test against tt.allowedVerbs,
+ // this is the only different data point compared to the test cases above.
+ service := newService(t, state, fakeChecker{allowedVerbs: tt.allowedVerbs})
+ err := callMethod(t, service, tt.name)
+ require.True(t, trace.IsAccessDenied(err))
+ })
+ }
+ })
+ })
+ }
+
+ // verify that all declared methods have matching test cases
+ t.Run("verify coverage", func(t *testing.T) {
+ for _, method := range autoupdate.AutoUpdateService_ServiceDesc.Methods {
+ t.Run(method.MethodName, func(t *testing.T) {
+ match := false
+ for _, testCase := range testCases {
+ match = match || testCase.name == method.MethodName
+ }
+ require.True(t, match, "method %v without coverage, no matching tests", method.MethodName)
+ })
+ }
+ })
+}
+
+type fakeChecker struct {
+ allowedVerbs []string
+ services.AccessChecker
+}
+
+func (f fakeChecker) CheckAccessToRule(_ services.RuleContext, _ string, resource string, verb string) error {
+ if resource == types.KindAutoUpdateConfig || resource == types.KindAutoUpdateVersion {
+ for _, allowedVerb := range f.allowedVerbs {
+ if allowedVerb == verb {
+ return nil
+ }
+ }
+ }
+
+ return trace.AccessDenied("access denied to rule=%v/verb=%v", resource, verb)
+}
+
+func newService(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker) *Service {
+ t.Helper()
+
+ bk, err := memory.New(memory.Config{})
+ require.NoError(t, err)
+
+ storage, err := local.NewAutoUpdateService(bk)
+ require.NoError(t, err)
+
+ return newServiceWithStorage(t, authState, checker, storage)
+}
+
+func newServiceWithStorage(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker, storage services.AutoUpdateService) *Service {
+ t.Helper()
+
+ authorizer := authz.AuthorizerFunc(func(ctx context.Context) (*authz.Context, error) {
+ user, err := types.NewUser("alice")
+ if err != nil {
+ return nil, err
+ }
+ return &authz.Context{
+ User: user,
+ Checker: checker,
+ AdminActionAuthState: authState,
+ }, nil
+ })
+
+ service, err := NewService(ServiceConfig{
+ Authorizer: authorizer,
+ Backend: storage,
+ Cache: storage,
+ })
+ require.NoError(t, err)
+ return service
+}
diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go
index 6e63918f9985..3b5676369300 100644
--- a/lib/auth/grpcserver.go
+++ b/lib/auth/grpcserver.go
@@ -52,6 +52,7 @@ import (
assistv1pb "github.com/gravitational/teleport/api/gen/proto/go/assist/v1"
accessmonitoringrulesv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1"
auditlogv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1"
+ autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
clusterconfigv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
crownjewelv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1"
dbobjectv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1"
@@ -77,6 +78,7 @@ import (
"github.com/gravitational/teleport/lib/auth/accessmonitoringrules/accessmonitoringrulesv1"
"github.com/gravitational/teleport/lib/auth/assist/assistv1"
"github.com/gravitational/teleport/lib/auth/authclient"
+ "github.com/gravitational/teleport/lib/auth/autoupdate/autoupdatev1"
"github.com/gravitational/teleport/lib/auth/clusterconfig/clusterconfigv1"
"github.com/gravitational/teleport/lib/auth/crownjewel/crownjewelv1"
"github.com/gravitational/teleport/lib/auth/dbobject/dbobjectv1"
@@ -6022,6 +6024,16 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
}
accessmonitoringrulesv1pb.RegisterAccessMonitoringRulesServiceServer(server, accessMonitoringRuleServer)
+ autoUpdateServiceServer, err := autoupdatev1.NewService(autoupdatev1.ServiceConfig{
+ Authorizer: cfg.Authorizer,
+ Backend: cfg.AuthServer.Services,
+ Cache: cfg.AuthServer.Services,
+ })
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ autoupdatev1pb.RegisterAutoUpdateServiceServer(server, autoUpdateServiceServer)
+
// Only register the service if this is an open source build. Enterprise builds
// register the actual service via an auth plugin, if we register here then all
// Enterprise builds would fail with a duplicate service registered error.
diff --git a/lib/auth/init.go b/lib/auth/init.go
index 1f146d7f2b3c..72ac84cd200b 100644
--- a/lib/auth/init.go
+++ b/lib/auth/init.go
@@ -157,6 +157,9 @@ type InitConfig struct {
// ClusterConfiguration is a services that holds cluster wide configuration.
ClusterConfiguration services.ClusterConfiguration
+ // AutoUpdateService is a service of autoupdate configuration and version.
+ AutoUpdateService services.AutoUpdateService
+
// Restrictions is a service to access network restrictions, etc
Restrictions services.Restrictions
diff --git a/lib/service/service.go b/lib/service/service.go
index e2777bf79df0..51157db4fd55 100644
--- a/lib/service/service.go
+++ b/lib/service/service.go
@@ -1999,6 +1999,7 @@ func (process *TeleportProcess) initAuthService() error {
VersionStorage: process.storage,
Authority: cfg.Keygen,
ClusterConfiguration: cfg.ClusterConfiguration,
+ AutoUpdateService: cfg.AutoUpdateService,
ClusterAuditConfig: cfg.Auth.AuditConfig,
ClusterNetworkingConfig: cfg.Auth.NetworkingConfig,
SessionRecordingConfig: cfg.Auth.SessionRecordingConfig,
diff --git a/lib/service/servicecfg/config.go b/lib/service/servicecfg/config.go
index c15f61c9d272..566990eb70be 100644
--- a/lib/service/servicecfg/config.go
+++ b/lib/service/servicecfg/config.go
@@ -170,6 +170,9 @@ type Config struct {
// ClusterConfiguration is a service that provides cluster configuration
ClusterConfiguration services.ClusterConfiguration
+ // AutoUpdateService is a service that provides auto update configuration and version.
+ AutoUpdateService services.AutoUpdateService
+
// CipherSuites is a list of TLS ciphersuites that Teleport supports. If
// omitted, a Teleport selected list of defaults will be used.
CipherSuites []uint16
diff --git a/lib/services/local/autoupdate.go b/lib/services/local/autoupdate.go
index 21b4172767cc..a1a04a38ea65 100644
--- a/lib/services/local/autoupdate.go
+++ b/lib/services/local/autoupdate.go
@@ -36,14 +36,14 @@ const (
autoUpdateVersionPrefix = "auto_update_version"
)
-// AutoupdateService is responsible for managing auto update configuration and version.
-type AutoupdateService struct {
+// AutoUpdateService is responsible for managing auto update configuration and version.
+type AutoUpdateService struct {
config *generic.ServiceWrapper[*autoupdate.AutoUpdateConfig]
version *generic.ServiceWrapper[*autoupdate.AutoUpdateVersion]
}
-// NewAutoupdateService returns a new AutoupdateService.
-func NewAutoupdateService(backend backend.Backend) (*AutoupdateService, error) {
+// NewAutoUpdateService returns a new AutoUpdateService.
+func NewAutoUpdateService(backend backend.Backend) (*AutoUpdateService, error) {
config, err := generic.NewServiceWrapper(
generic.ServiceWrapperConfig[*autoupdate.AutoUpdateConfig]{
Backend: backend,
@@ -75,14 +75,14 @@ func NewAutoupdateService(backend backend.Backend) (*AutoupdateService, error) {
return nil, trace.Wrap(err)
}
- return &AutoupdateService{
+ return &AutoUpdateService{
config: config,
version: version,
}, nil
}
// CreateAutoUpdateConfig creates an auto update configuration singleton.
-func (s *AutoupdateService) CreateAutoUpdateConfig(
+func (s *AutoUpdateService) CreateAutoUpdateConfig(
ctx context.Context,
c *autoupdate.AutoUpdateConfig,
) (*autoupdate.AutoUpdateConfig, error) {
@@ -91,7 +91,7 @@ func (s *AutoupdateService) CreateAutoUpdateConfig(
}
// UpdateAutoUpdateConfig updates an auto update configuration singleton.
-func (s *AutoupdateService) UpdateAutoUpdateConfig(
+func (s *AutoUpdateService) UpdateAutoUpdateConfig(
ctx context.Context,
c *autoupdate.AutoUpdateConfig,
) (*autoupdate.AutoUpdateConfig, error) {
@@ -100,7 +100,7 @@ func (s *AutoupdateService) UpdateAutoUpdateConfig(
}
// UpsertAutoUpdateConfig sets an auto update configuration.
-func (s *AutoupdateService) UpsertAutoUpdateConfig(
+func (s *AutoUpdateService) UpsertAutoUpdateConfig(
ctx context.Context,
c *autoupdate.AutoUpdateConfig,
) (*autoupdate.AutoUpdateConfig, error) {
@@ -109,18 +109,18 @@ func (s *AutoupdateService) UpsertAutoUpdateConfig(
}
// GetAutoUpdateConfig gets the auto update configuration from the backend.
-func (s *AutoupdateService) GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error) {
+func (s *AutoUpdateService) GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error) {
config, err := s.config.GetResource(ctx, types.MetaNameAutoUpdateConfig)
return config, trace.Wrap(err)
}
// DeleteAutoUpdateConfig deletes the auto update configuration from the backend.
-func (s *AutoupdateService) DeleteAutoUpdateConfig(ctx context.Context) error {
+func (s *AutoUpdateService) DeleteAutoUpdateConfig(ctx context.Context) error {
return trace.Wrap(s.config.DeleteResource(ctx, types.MetaNameAutoUpdateConfig))
}
// CreateAutoUpdateVersion creates an autoupdate version resource.
-func (s *AutoupdateService) CreateAutoUpdateVersion(
+func (s *AutoUpdateService) CreateAutoUpdateVersion(
ctx context.Context,
v *autoupdate.AutoUpdateVersion,
) (*autoupdate.AutoUpdateVersion, error) {
@@ -129,7 +129,7 @@ func (s *AutoupdateService) CreateAutoUpdateVersion(
}
// UpdateAutoUpdateVersion updates an autoupdate version resource.
-func (s *AutoupdateService) UpdateAutoUpdateVersion(
+func (s *AutoUpdateService) UpdateAutoUpdateVersion(
ctx context.Context,
v *autoupdate.AutoUpdateVersion,
) (*autoupdate.AutoUpdateVersion, error) {
@@ -138,7 +138,7 @@ func (s *AutoupdateService) UpdateAutoUpdateVersion(
}
// UpsertAutoUpdateVersion sets autoupdate version resource.
-func (s *AutoupdateService) UpsertAutoUpdateVersion(
+func (s *AutoUpdateService) UpsertAutoUpdateVersion(
ctx context.Context,
v *autoupdate.AutoUpdateVersion,
) (*autoupdate.AutoUpdateVersion, error) {
@@ -147,12 +147,12 @@ func (s *AutoupdateService) UpsertAutoUpdateVersion(
}
// GetAutoUpdateVersion gets the auto update version from the backend.
-func (s *AutoupdateService) GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error) {
+func (s *AutoUpdateService) GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error) {
version, err := s.version.GetResource(ctx, types.MetaNameAutoUpdateVersion)
return version, trace.Wrap(err)
}
// DeleteAutoUpdateVersion deletes the auto update version from the backend.
-func (s *AutoupdateService) DeleteAutoUpdateVersion(ctx context.Context) error {
+func (s *AutoUpdateService) DeleteAutoUpdateVersion(ctx context.Context) error {
return trace.Wrap(s.version.DeleteResource(ctx, types.MetaNameAutoUpdateVersion))
}
diff --git a/lib/services/local/autoupdate_test.go b/lib/services/local/autoupdate_test.go
index a27af676589d..0a6f4e5bd2b8 100644
--- a/lib/services/local/autoupdate_test.go
+++ b/lib/services/local/autoupdate_test.go
@@ -43,7 +43,7 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) {
bk, err := memory.New(memory.Config{})
require.NoError(t, err)
- service, err := NewAutoupdateService(bk)
+ service, err := NewAutoUpdateService(bk)
require.NoError(t, err)
ctx := context.Background()
@@ -99,7 +99,7 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) {
bk, err := memory.New(memory.Config{})
require.NoError(t, err)
- service, err := NewAutoupdateService(bk)
+ service, err := NewAutoUpdateService(bk)
require.NoError(t, err)
ctx := context.Background()
@@ -155,7 +155,7 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) {
bk, err := memory.New(memory.Config{})
require.NoError(t, err)
- service, err := NewAutoupdateService(bk)
+ service, err := NewAutoUpdateService(bk)
require.NoError(t, err)
ctx := context.Background()
@@ -190,7 +190,7 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) {
bk, err := memory.New(memory.Config{})
require.NoError(t, err)
- service, err := NewAutoupdateService(bk)
+ service, err := NewAutoUpdateService(bk)
require.NoError(t, err)
ctx := context.Background()