Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/types/discoveryconfig/discoveryconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,15 @@ func (a *DiscoveryConfig) MatchSearch(values []string) bool {
return types.MatchSearch(fieldVals, values, nil)
}

// IsMatchersEmpty returns true if all matchers are empty.
func (a *DiscoveryConfig) IsMatchersEmpty() bool {
return len(a.Spec.AWS) == 0 &&
len(a.Spec.Azure) == 0 &&
len(a.Spec.GCP) == 0 &&
len(a.Spec.Kube) == 0 &&
(a.Spec.AccessGraph == nil || len(a.Spec.AccessGraph.AWS) == 0)
}

// CloneResource returns a copy of the resource as types.ResourceWithLabels.
func (a *DiscoveryConfig) CloneResource() types.ResourceWithLabels {
var copy *DiscoveryConfig
Expand Down
107 changes: 107 additions & 0 deletions api/types/discoveryconfig/discoveryconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,110 @@ func TestNewDiscoveryConfig(t *testing.T) {
})
}
}

func TestDiscoveryConfig_IsMatchersEmpty(t *testing.T) {
for _, tt := range []struct {
name string
config *DiscoveryConfig
expected bool
}{
{
name: "empty config",
config: &DiscoveryConfig{
Spec: Spec{},
},
expected: true,
},
{
name: "has AWS matchers",
config: &DiscoveryConfig{
Spec: Spec{
AWS: []types.AWSMatcher{{
Types: []string{"ec2"},
Regions: []string{"us-west-2"},
}},
},
},
expected: false,
},
{
name: "has Azure matchers",
config: &DiscoveryConfig{
Spec: Spec{
Azure: []types.AzureMatcher{{
Types: []string{"vm"},
Regions: []string{"europe-west-2"},
}},
},
},
expected: false,
},
{
name: "has GCP matchers",
config: &DiscoveryConfig{
Spec: Spec{
GCP: []types.GCPMatcher{{
Types: []string{"gce"},
ProjectIDs: []string{"p1"},
}},
},
},
expected: false,
},
{
name: "has Kube matchers",
config: &DiscoveryConfig{
Spec: Spec{
Kube: []types.KubernetesMatcher{{
Types: []string{"app"},
}},
},
},
expected: false,
},
{
name: "has AccessGraph with AWS",
config: &DiscoveryConfig{
Spec: Spec{
AccessGraph: &types.AccessGraphSync{
AWS: []*types.AccessGraphAWSSync{{
Integration: "integration1",
Regions: []string{"us-west-2"},
}},
},
},
},
expected: false,
},
{
name: "has AccessGraph but no AWS",
config: &DiscoveryConfig{
Spec: Spec{
AccessGraph: &types.AccessGraphSync{},
},
},
expected: true,
},
{
name: "has multiple matcher types",
config: &DiscoveryConfig{
Spec: Spec{
AWS: []types.AWSMatcher{{
Types: []string{"ec2"},
Regions: []string{"us-west-2"},
}},
Azure: []types.AzureMatcher{{
Types: []string{"vm"},
Regions: []string{"europe-west-2"},
}},
},
},
expected: false,
},
} {
t.Run(tt.name, func(t *testing.T) {
got := tt.config.IsMatchersEmpty()
require.Equal(t, tt.expected, got)
})
}
}
3 changes: 3 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,9 @@ type Cache interface {

// UserLoginStatesGetter defines methods for fetching user login states.
services.UserLoginStatesGetter

// DiscoveryConfigsGetter defines methods for fetching discovery configs.
services.DiscoveryConfigsGetter
}

type NodeWrapper struct {
Expand Down
98 changes: 98 additions & 0 deletions lib/auth/integration/integrationv1/awsoidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ package integrationv1
import (
"context"
"log/slog"
"slices"
"strings"

"github.com/google/uuid"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/defaults"
integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
awsutils "github.com/gravitational/teleport/api/utils/aws"
"github.com/gravitational/teleport/api/utils/clientutils"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/integrations/awsoidc"
"github.com/gravitational/teleport/lib/modules"
Expand Down Expand Up @@ -85,6 +90,99 @@ type AWSOIDCServiceConfig struct {
Logger *slog.Logger
}

// deleteAWSOIDCAssociatedResources deletes associated discovery_configs and
// app_servers created by the integration being deleted. Should only be used by
// methods that check access for VerbDelete on KindIntegration
func (s *Service) deleteAWSOIDCAssociatedResources(ctx context.Context, authCtx *authz.Context, ig types.Integration) error {
// TODO(alexhemard): follow up work needed to add explicit labels for
// resources created by integration rather than rely on implicit rules

// Delete discovery_configs created by this integration
var configsRequireCleanup []string
var configsToDelete []string

for config, err := range clientutils.Resources(ctx, s.cache.ListDiscoveryConfigs) {
if err != nil {
return trace.Wrap(err)
}

awsMatchers := config.Spec.AWS

config.Spec.AWS = slices.DeleteFunc(config.Spec.AWS, func(matcher types.AWSMatcher) bool {
return matcher.Integration == ig.GetName()
})

if len(awsMatchers) == len(config.Spec.AWS) {
continue
}

// discovery_configs can be assumed to be created by the integration
// and deleted if
// 1. only has matchers referencing this integration
// 2. has valid uuid name
if config.IsMatchersEmpty() {
_, err := uuid.Parse(config.GetName())

if err == nil {
configsToDelete = append(configsToDelete, config.GetName())
continue
}
}

configsRequireCleanup = append(configsRequireCleanup, config.GetName())
}

if len(configsRequireCleanup) > 0 {
var qualifiedConfigs []string
for _, config := range configsRequireCleanup {
qualifiedConfigs = append(qualifiedConfigs, "discovery_config/"+config)
}

return trace.BadParameter("cannot delete integration, "+
"Discovery Configs referencing this integration must be removed first: %s\n\n"+
"Use `tsh rm %s` to remove them.",
strings.Join(configsRequireCleanup, ", "),
strings.Join(qualifiedConfigs, " "))
}

for _, configName := range configsToDelete {
s.logger.DebugContext(ctx, "Deleting discovery_config associated with integration",
"discovery_config", configName,
"integration", ig.GetName())

err := s.backend.DeleteDiscoveryConfig(ctx, configName)

if err != nil && !trace.IsNotFound(err) {
return trace.Wrap(err)
}
}

// Delete AWS access app_server associated with this integration
appServers, err := s.cache.GetApplicationServers(ctx, defaults.Namespace)
if err != nil {
return trace.Wrap(err)
}

for _, appServer := range appServers {
if appServer.GetApp().GetIntegration() == ig.GetName() {
s.logger.DebugContext(ctx, "Deleting app_server associated with integration",
"app_server", appServer.GetName(),
"integration", ig.GetName())

err := s.backend.DeleteApplicationServer(ctx,
appServer.GetNamespace(), appServer.GetHostID(), appServer.GetName())

if err != nil && !trace.IsNotFound(err) {
return trace.Wrap(err)
}

break
}
}

return nil
}

// CheckAndSetDefaults checks the AWSOIDCServiceConfig fields and returns an error if a required param is not provided.
// Authorizer and IntegrationService are required params.
func (s *AWSOIDCServiceConfig) CheckAndSetDefaults() error {
Expand Down
10 changes: 10 additions & 0 deletions lib/auth/integration/integrationv1/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ type Cache interface {
// IntegrationsGetter defines methods to access Integration resources.
services.IntegrationsGetter

// DiscoveryConfigsGetter defines methods to access DiscoveryConfig resources.
services.DiscoveryConfigsGetter

// AppServersGetter defines methods to access application servers.
services.AppServersGetter

// GetPluginStaticCredentialsByLabels will get a list of plugin static credentials resource by matching labels.
GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error)
}
Expand All @@ -82,6 +88,8 @@ type Backend interface {
services.Integrations
services.PluginStaticCredentials
services.GitServers
services.DiscoveryConfigs
services.Presence
}

// ServiceConfig holds configuration options for
Expand Down Expand Up @@ -495,6 +503,8 @@ func (s *Service) ensureNoGitHubAssociatedResources(ctx context.Context, ig type

func (s *Service) deleteAssociatedResources(ctx context.Context, authCtx *authz.Context, ig types.Integration) error {
switch ig.GetSubKind() {
case types.IntegrationSubKindAWSOIDC:
return trace.Wrap(s.deleteAWSOIDCAssociatedResources(ctx, authCtx, ig))
case types.IntegrationSubKindGitHub:
return trace.Wrap(s.deleteGitHubAssociatedResources(ctx, authCtx, ig))
default:
Expand Down
Loading
Loading