From 8dea0c3d10361c9543e209a437ffe617d53bf4b5 Mon Sep 17 00:00:00 2001 From: Jacob Henner Date: Wed, 26 Jan 2022 20:28:08 -0500 Subject: [PATCH 1/2] Add support for AWS temporary credentials Add support for AWS temporary credentials by allowing session tokens to be specified. Assuming Secrets are kept up-to-date with valid session tokens, scalers using temporary credentials will error once after token expiration. The scaler cache for the corresponding ScaledObject will be cleared, the scaler will be rebuilt using the updated temporary credentials, and the scaler will resume operation. Signed-off-by: Jacob Henner --- pkg/scalers/aws_cloudwatch_scaler.go | 2 +- pkg/scalers/aws_cloudwatch_test.go | 22 ++++++++- pkg/scalers/aws_iam_authorization.go | 2 + pkg/scalers/aws_kinesis_stream_scaler.go | 2 +- pkg/scalers/aws_kinesis_stream_test.go | 60 +++++++++++++++++++++++- pkg/scalers/aws_sqs_queue_scaler.go | 2 +- pkg/scalers/aws_sqs_queue_test.go | 40 ++++++++++++++-- 7 files changed, 121 insertions(+), 9 deletions(-) diff --git a/pkg/scalers/aws_cloudwatch_scaler.go b/pkg/scalers/aws_cloudwatch_scaler.go index fb68e43cc86..3a1eb57d54b 100644 --- a/pkg/scalers/aws_cloudwatch_scaler.go +++ b/pkg/scalers/aws_cloudwatch_scaler.go @@ -112,7 +112,7 @@ func createCloudwatchClient(metadata *awsCloudwatchMetadata) *cloudwatch.CloudWa var cloudwatchClient *cloudwatch.CloudWatch if metadata.awsAuthorization.podIdentityOwner { - creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, "") + creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, metadata.awsAuthorization.awsSessionToken) if metadata.awsAuthorization.awsRoleArn != "" { creds = stscreds.NewCredentials(sess, metadata.awsAuthorization.awsRoleArn) diff --git a/pkg/scalers/aws_cloudwatch_test.go b/pkg/scalers/aws_cloudwatch_test.go index 72c1c034d22..f03cd439d51 100644 --- a/pkg/scalers/aws_cloudwatch_test.go +++ b/pkg/scalers/aws_cloudwatch_test.go @@ -17,6 +17,7 @@ const ( testAWSCloudwatchRoleArn = "none" testAWSCloudwatchAccessKeyID = "none" testAWSCloudwatchSecretAccessKey = "none" + testAWSCloudwatchSessionToken = "none" testAWSCloudwatchErrorMetric = "Error" testAWSCloudwatchNoValueMetric = "NoValue" ) @@ -127,7 +128,7 @@ var testAWSCloudwatchMetadata = []parseAWSCloudwatchMetadataTestData{ testAWSAuthentication, true, "Missing metricName"}, - // with "aws_credentials" from TriggerAuthentication + // with static "aws_credentials" from TriggerAuthentication {map[string]string{ "namespace": "AWS/SQS", "dimensionName": "QueueName", @@ -145,6 +146,25 @@ var testAWSCloudwatchMetadata = []parseAWSCloudwatchMetadataTestData{ }, false, "with AWS Credentials from TriggerAuthentication"}, + // with temporary "aws_credentials" from TriggerAuthentication + {map[string]string{ + "namespace": "AWS/SQS", + "dimensionName": "QueueName", + "dimensionValue": "keda", + "metricName": "ApproximateNumberOfMessagesVisible", + "targetMetricValue": "2", + "minMetricValue": "0", + "metricCollectionTime": "300", + "metricStat": "Average", + "metricStatPeriod": "300", + "awsRegion": "eu-west-1"}, + map[string]string{ + "awsAccessKeyId": testAWSCloudwatchAccessKeyID, + "awsSecretAccessKey": testAWSCloudwatchSecretAccessKey, + "awsSessionToken": testAWSCloudwatchSessionToken, + }, + false, + "with AWS Credentials from TriggerAuthentication"}, // with "aws_role" from TriggerAuthentication {map[string]string{ "namespace": "AWS/SQS", diff --git a/pkg/scalers/aws_iam_authorization.go b/pkg/scalers/aws_iam_authorization.go index 83162952351..b8e15c0c34d 100644 --- a/pkg/scalers/aws_iam_authorization.go +++ b/pkg/scalers/aws_iam_authorization.go @@ -7,6 +7,7 @@ type awsAuthorizationMetadata struct { awsAccessKeyID string awsSecretAccessKey string + awsSessionToken string podIdentityOwner bool } @@ -27,6 +28,7 @@ func getAwsAuthorization(authParams, metadata, resolvedEnv map[string]string) (a meta.awsAccessKeyID = authParams["awsAccessKeyId"] } meta.awsSecretAccessKey = authParams["awsSecretAccessKey"] + meta.awsSessionToken = authParams["awsSessionToken"] default: if metadata["awsAccessKeyID"] != "" { meta.awsAccessKeyID = metadata["awsAccessKeyID"] diff --git a/pkg/scalers/aws_kinesis_stream_scaler.go b/pkg/scalers/aws_kinesis_stream_scaler.go index 4a4c6db059c..3ffedcecb4a 100644 --- a/pkg/scalers/aws_kinesis_stream_scaler.go +++ b/pkg/scalers/aws_kinesis_stream_scaler.go @@ -99,7 +99,7 @@ func createKinesisClient(metadata *awsKinesisStreamMetadata) *kinesis.Kinesis { var kinesisClinent *kinesis.Kinesis if metadata.awsAuthorization.podIdentityOwner { - creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, "") + creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, metadata.awsAuthorization.awsSessionToken) if metadata.awsAuthorization.awsRoleArn != "" { creds = stscreds.NewCredentials(sess, metadata.awsAuthorization.awsRoleArn) diff --git a/pkg/scalers/aws_kinesis_stream_test.go b/pkg/scalers/aws_kinesis_stream_test.go index df5392ff2f4..332884b5fdb 100644 --- a/pkg/scalers/aws_kinesis_stream_test.go +++ b/pkg/scalers/aws_kinesis_stream_test.go @@ -17,6 +17,7 @@ const ( testAWSKinesisRoleArn = "none" testAWSKinesisAccessKeyID = "none" testAWSKinesisSecretAccessKey = "none" + testAWSKinesisSessionToken = "none" testAWSKinesisStreamName = "test" testAWSRegion = "eu-west-1" testAWSKinesisErrorStream = "Error" @@ -161,7 +162,7 @@ var testAWSKinesisMetadata = []parseAWSKinesisMetadataTestData{ }, expected: &awsKinesisStreamMetadata{}, isError: true, - comment: "with AWS Credentials from TriggerAuthentication, missing Access Key Id", + comment: "with AWS static credentials from TriggerAuthentication, missing Access Key Id", scalerIndex: 5, }, {metadata: map[string]string{ @@ -174,7 +175,62 @@ var testAWSKinesisMetadata = []parseAWSKinesisMetadataTestData{ }, expected: &awsKinesisStreamMetadata{}, isError: true, - comment: "with AWS Credentials from TriggerAuthentication, missing Secret Access Key", + comment: "with AWS static credentials from TriggerAuthentication, missing Secret Access Key", + scalerIndex: 6, + }, + { + metadata: map[string]string{ + "streamName": testAWSKinesisStreamName, + "shardCount": "2", + "awsRegion": testAWSRegion}, + authParams: map[string]string{ + "awsAccessKeyID": testAWSKinesisAccessKeyID, + "awsSecretAccessKey": testAWSKinesisSecretAccessKey, + "awsSessionToken": testAWSKinesisSessionToken, + }, + expected: &awsKinesisStreamMetadata{ + targetShardCount: 2, + streamName: testAWSKinesisStreamName, + awsRegion: testAWSRegion, + awsAuthorization: awsAuthorizationMetadata{ + awsAccessKeyID: testAWSKinesisAccessKeyID, + awsSecretAccessKey: testAWSKinesisSecretAccessKey, + awsSessionToken: testAWSKinesisSessionToken, + podIdentityOwner: true, + }, + scalerIndex: 5, + }, + isError: false, + comment: "with AWS temporary credentials from TriggerAuthentication", + scalerIndex: 5, + }, + { + metadata: map[string]string{ + "streamName": testAWSKinesisStreamName, + "shardCount": "2", + "awsRegion": testAWSRegion}, + authParams: map[string]string{ + "awsAccessKeyID": "", + "awsSecretAccessKey": testAWSKinesisSecretAccessKey, + "awsSessionToken": testAWSKinesisSessionToken, + }, + expected: &awsKinesisStreamMetadata{}, + isError: true, + comment: "with AWS temporary credentials from TriggerAuthentication, missing Access Key Id", + scalerIndex: 5, + }, + {metadata: map[string]string{ + "streamName": testAWSKinesisStreamName, + "shardCount": "2", + "awsRegion": testAWSRegion}, + authParams: map[string]string{ + "awsAccessKeyID": testAWSKinesisAccessKeyID, + "awsSecretAccessKey": "", + "awsSessionToken": testAWSKinesisSessionToken, + }, + expected: &awsKinesisStreamMetadata{}, + isError: true, + comment: "with AWS temporary credentials from TriggerAuthentication, missing Secret Access Key", scalerIndex: 6, }, {metadata: map[string]string{ diff --git a/pkg/scalers/aws_sqs_queue_scaler.go b/pkg/scalers/aws_sqs_queue_scaler.go index df24d604978..419229c26fe 100644 --- a/pkg/scalers/aws_sqs_queue_scaler.go +++ b/pkg/scalers/aws_sqs_queue_scaler.go @@ -122,7 +122,7 @@ func createSqsClient(metadata *awsSqsQueueMetadata) *sqs.SQS { var sqsClient *sqs.SQS if metadata.awsAuthorization.podIdentityOwner { - creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, "") + creds := credentials.NewStaticCredentials(metadata.awsAuthorization.awsAccessKeyID, metadata.awsAuthorization.awsSecretAccessKey, metadata.awsAuthorization.awsSessionToken) if metadata.awsAuthorization.awsRoleArn != "" { creds = stscreds.NewCredentials(sess, metadata.awsAuthorization.awsRoleArn) diff --git a/pkg/scalers/aws_sqs_queue_test.go b/pkg/scalers/aws_sqs_queue_test.go index 550b816187d..b3cc43b56e1 100644 --- a/pkg/scalers/aws_sqs_queue_test.go +++ b/pkg/scalers/aws_sqs_queue_test.go @@ -16,6 +16,7 @@ const ( testAWSSQSRoleArn = "none" testAWSSQSAccessKeyID = "none" testAWSSQSSecretAccessKey = "none" + testAWSSQSSessionToken = "none" testAWSSQSProperQueueURL = "https://sqs.eu-west-1.amazonaws.com/account_id/DeleteArtifactQ" testAWSSQSImproperQueueURL1 = "https://sqs.eu-west-1.amazonaws.com/account_id" @@ -125,7 +126,38 @@ var testAWSSQSMetadata = []parseAWSSQSMetadataTestData{ "awsSecretAccessKey": testAWSSQSSecretAccessKey, }, false, - "with AWS Credentials from TriggerAuthentication"}, + "with AWS static credentials from TriggerAuthentication"}, + {map[string]string{ + "queueURL": testAWSSQSProperQueueURL, + "queueLength": "1", + "awsRegion": "eu-west-1"}, + map[string]string{ + "awsAccessKeyId": testAWSSQSAccessKeyID, + "awsSecretAccessKey": testAWSSQSSecretAccessKey, + "awsSessionToken": testAWSSQSSessionToken, + }, + false, + "with AWS temporary credentials from TriggerAuthentication"}, + {map[string]string{ + "queueURL": testAWSSQSProperQueueURL, + "queueLength": "1", + "awsRegion": "eu-west-1"}, + map[string]string{ + "awsAccessKeyId": "", + "awsSecretAccessKey": testAWSSQSSecretAccessKey, + }, + true, + "with AWS static credentials from TriggerAuthentication, missing Access Key Id"}, + {map[string]string{ + "queueURL": testAWSSQSProperQueueURL, + "queueLength": "1", + "awsRegion": "eu-west-1"}, + map[string]string{ + "awsAccessKeyId": testAWSSQSAccessKeyID, + "awsSecretAccessKey": "", + }, + true, + "with AWS temporary credentials from TriggerAuthentication, missing Secret Access Key"}, {map[string]string{ "queueURL": testAWSSQSProperQueueURL, "queueLength": "1", @@ -133,9 +165,10 @@ var testAWSSQSMetadata = []parseAWSSQSMetadataTestData{ map[string]string{ "awsAccessKeyId": "", "awsSecretAccessKey": testAWSSQSSecretAccessKey, + "awsSessionToken": testAWSSQSSessionToken, }, true, - "with AWS Credentials from TriggerAuthentication, missing Access Key Id"}, + "with AWS temporary credentials from TriggerAuthentication, missing Access Key Id"}, {map[string]string{ "queueURL": testAWSSQSProperQueueURL, "queueLength": "1", @@ -143,9 +176,10 @@ var testAWSSQSMetadata = []parseAWSSQSMetadataTestData{ map[string]string{ "awsAccessKeyId": testAWSSQSAccessKeyID, "awsSecretAccessKey": "", + "awsSessionToken": testAWSSQSSessionToken, }, true, - "with AWS Credentials from TriggerAuthentication, missing Secret Access Key"}, + "with AWS static credentials from TriggerAuthentication, missing Secret Access Key"}, {map[string]string{ "queueURL": testAWSSQSProperQueueURL, "queueLength": "1", From 4ea8e3b1ec54c666470c25e4dedb4590493f7d0f Mon Sep 17 00:00:00 2001 From: Jacob Henner Date: Thu, 27 Jan 2022 17:26:39 -0500 Subject: [PATCH 2/2] Add CHANGELOG entry for #2573 Signed-off-by: Jacob Henner --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e713823d52..e256f99ea10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,8 @@ - **Kafka Scaler:** allow flag `topic` to be optional, where lag of all topics within the consumer group will be used for scaling ([#2409](https://github.com/kedacore/keda/pull/2409)) - **General:** fail fast on `buildScalers` when not able to resolve a secret that a deployment is relying on ([#2394](https://github.com/kedacore/keda/pull/2394)) - **CPU Scaler:** Adding e2e test for the cpu scaler ([#2441](https://github.com/kedacore/keda/pull/2441)) -- **AWS SQS Scaler**: allow using simple queue name instead of URL ([#2457](https://github.com/kedacore/keda/pull/2457)) +- **AWS SQS Scaler:** allow using simple queue name instead of URL ([#2457](https://github.com/kedacore/keda/pull/2457)) +- **AWS Scalers:** Support temporary AWS credentials using session tokens ([#2573](https://github.com/kedacore/keda/pull/2573)) ### Breaking Changes