Pārlūkot izejas kodu

test(e2e): harden aws parameterstore assume-role denial checks

Moritz Johner 2 mēneši atpakaļ
vecāks
revīzija
c63f4f3828

+ 31 - 5
e2e/suites/provider/cases/aws/parameterstore/provider_support_v2.go

@@ -32,6 +32,7 @@ import (
 	ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
 	"github.com/aws/aws-sdk-go-v2/service/sts"
 	ststypes "github.com/aws/aws-sdk-go-v2/service/sts/types"
+	"github.com/aws/smithy-go"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -48,10 +49,11 @@ import (
 )
 
 const (
-	awsProviderAPIVersion = "provider.external-secrets.io/v2alpha1"
-	defaultV2WaitTimeout  = 60 * time.Second
-	defaultV2PollInterval = 2 * time.Second
-	assumeRoleSessionName = "eso-e2e-probe"
+	awsProviderAPIVersion  = "provider.external-secrets.io/v2alpha1"
+	defaultV2WaitTimeout   = 60 * time.Second
+	defaultV2PollInterval  = 2 * time.Second
+	assumeRoleSessionName  = "eso-e2e-probe"
+	assumeRoleProbeTimeout = 15 * time.Second
 )
 
 type awsV2AuthProfile string
@@ -165,7 +167,9 @@ func skipIfAWSAssumeRoleProbeDenied(access awsV2AccessConfig, profile awsV2AuthP
 	cfg, err := loadParameterStoreAWSConfig(access)
 	Expect(err).NotTo(HaveOccurred())
 
-	err = probeAssumeRoleAccess(context.Background(), sts.NewFromConfig(cfg), access, profile)
+	probeCtx, cancel := context.WithTimeout(context.Background(), assumeRoleProbeTimeout)
+	defer cancel()
+	err = probeAssumeRoleAccess(probeCtx, sts.NewFromConfig(cfg), access, profile)
 	assumeRoleV2ProbeCache.Store(cacheKey, assumeRoleV2ProbeResult{err: err})
 	handleAssumeRoleV2ProbeResult(access, profile, err)
 }
@@ -235,10 +239,32 @@ func isAssumeRoleAccessDenied(err error) bool {
 		return false
 	}
 
+	var apiErr smithy.APIError
+	if errors.As(err, &apiErr) {
+		if !strings.Contains(strings.ToLower(apiErr.ErrorCode()), "accessdenied") {
+			return false
+		}
+		if hasAssumeRoleActionContext(strings.ToLower(apiErr.ErrorMessage())) {
+			return true
+		}
+
+		var opErr *smithy.OperationError
+		if errors.As(err, &opErr) && strings.EqualFold(opErr.Service(), "STS") && strings.EqualFold(opErr.Operation(), "AssumeRole") {
+			return true
+		}
+
+		msg := strings.ToLower(err.Error())
+		return hasAssumeRoleActionContext(msg)
+	}
+
 	msg := strings.ToLower(err.Error())
 	if !strings.Contains(msg, "accessdenied") {
 		return false
 	}
+	return hasAssumeRoleActionContext(msg)
+}
+
+func hasAssumeRoleActionContext(msg string) bool {
 	return strings.Contains(msg, "sts:assumerole") || strings.Contains(msg, "sts:tagsession")
 }
 

+ 48 - 0
e2e/suites/provider/cases/aws/parameterstore/provider_support_v2_test.go

@@ -24,6 +24,7 @@ import (
 
 	"github.com/aws/aws-sdk-go-v2/aws"
 	"github.com/aws/aws-sdk-go-v2/service/sts"
+	"github.com/aws/smithy-go"
 	awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	awsv2alpha1 "github.com/external-secrets/external-secrets/apis/provider/aws/v2alpha1"
@@ -230,6 +231,53 @@ func TestIsAssumeRoleAccessDeniedRecognizesTagSessionErrors(t *testing.T) {
 	}
 }
 
+func TestIsAssumeRoleAccessDeniedReturnsFalseOnNilError(t *testing.T) {
+	t.Parallel()
+
+	if isAssumeRoleAccessDenied(nil) {
+		t.Fatal("expected nil error to not be treated as assume-role access denied")
+	}
+}
+
+func TestIsAssumeRoleAccessDeniedReturnsFalseForNonAccessDeniedError(t *testing.T) {
+	t.Parallel()
+
+	err := errors.New("api error ThrottlingException: slow down")
+	if isAssumeRoleAccessDenied(err) {
+		t.Fatal("expected non-access-denied error to not be treated as assume-role access denied")
+	}
+}
+
+func TestIsAssumeRoleAccessDeniedReturnsFalseWithoutAssumeRoleContext(t *testing.T) {
+	t.Parallel()
+
+	err := &smithy.GenericAPIError{
+		Code:    "AccessDeniedException",
+		Message: "request denied",
+		Fault:   smithy.FaultClient,
+	}
+	if isAssumeRoleAccessDenied(err) {
+		t.Fatal("expected access denied error without assume-role context to return false")
+	}
+}
+
+func TestIsAssumeRoleAccessDeniedRecognizesStructuredSTSAssumeRoleError(t *testing.T) {
+	t.Parallel()
+
+	err := &smithy.OperationError{
+		ServiceID:     "STS",
+		OperationName: "AssumeRole",
+		Err: &smithy.GenericAPIError{
+			Code:    "AccessDeniedException",
+			Message: "caller is not authorized",
+			Fault:   smithy.FaultClient,
+		},
+	}
+	if !isAssumeRoleAccessDenied(err) {
+		t.Fatal("expected structured STS AccessDenied error to be recognized")
+	}
+}
+
 func TestParameterStoreConfigForMountedIRSAUsesEmptyAWSAuth(t *testing.T) {
 	t.Parallel()