diff --git a/.github/infrastructure/docker-compose-secrets-manager.yml b/.github/infrastructure/docker-compose-secrets-manager.yml new file mode 100644 index 0000000000..4d9911e409 --- /dev/null +++ b/.github/infrastructure/docker-compose-secrets-manager.yml @@ -0,0 +1,15 @@ +version: "3.8" + +services: + localstack: + container_name: "conformance-aws-secrets-manager" + image: localstack/localstack + ports: + - "127.0.0.1:4566:4566" + environment: + - DEBUG=1 + - DOCKER_HOST=unix:///var/run/docker.sock + volumes: + - "${PWD}/.github/scripts/docker-compose-init/init-conformance-state-aws-secrets-manager.sh:/etc/localstack/init/ready.d/init-aws.sh" # ready hook + - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" + - "/var/run/docker.sock:/var/run/docker.sock" \ No newline at end of file diff --git a/.github/infrastructure/terraform/conformance/secretstores/aws/secretsmanager/secretsmanager.tf b/.github/infrastructure/terraform/conformance/secretstores/aws/secretsmanager/secretsmanager.tf new file mode 100644 index 0000000000..7c1f3b2921 --- /dev/null +++ b/.github/infrastructure/terraform/conformance/secretstores/aws/secretsmanager/secretsmanager.tf @@ -0,0 +1,54 @@ +terraform { + required_version = ">=0.13" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + } +} + +variable "TIMESTAMP" { + type = string + description = "Timestamp of the GitHub workflow run." +} + +variable "UNIQUE_ID" { + type = string + description = "Unique ID of the GitHub workflow run." +} + +provider "aws" { + region = "us-east-1" + default_tags { + tags = { + Purpose = "AutomatedConformanceTesting" + Timestamp = "${var.TIMESTAMP}" + } + } +} + +# Create the first secret in AWS Secrets Manager +resource "aws_secretsmanager_secret" "conftestsecret" { + name = "conftestsecret" + description = "Secret for conformance test" + recovery_window_in_days = 0 +} + +resource "aws_secretsmanager_secret_version" "conftestsecret_value" { + secret_id = aws_secretsmanager_secret.conftestsecret.id + secret_string = "abcd" +} + +# Create the second secret in AWS Secrets Manager +resource "aws_secretsmanager_secret" "secondsecret" { + name = "secondsecret" + description = "Another secret for conformance test" + recovery_window_in_days = 0 +} + +resource "aws_secretsmanager_secret_version" "secondsecret_value" { + secret_id = aws_secretsmanager_secret.secondsecret.id + secret_string = "efgh" +} diff --git a/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-destroy.sh b/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-destroy.sh new file mode 100755 index 0000000000..fa23ac24a3 --- /dev/null +++ b/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-destroy.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set +e + +# Navigate to the Terraform directory +cd ".github/infrastructure/terraform/conformance/secretstores/aws/secretsmanager" + +# Run Terraform +terraform destroy -auto-approve -var="UNIQUE_ID=$UNIQUE_ID" -var="TIMESTAMP=$CURRENT_TIME" diff --git a/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-setup.sh b/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-setup.sh new file mode 100755 index 0000000000..759024a7dc --- /dev/null +++ b/.github/scripts/components-scripts/conformance-secretstores.aws.secretsmanager.secretsmanager-setup.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +# Set variables for GitHub Actions +echo "AWS_REGION=us-east-1" >> $GITHUB_ENV + +# Navigate to the Terraform directory +cd ".github/infrastructure/terraform/conformance/secretstores/aws/secretsmanager" + +# Run Terraform +terraform init +terraform validate -no-color +terraform plan -no-color -var="UNIQUE_ID=$UNIQUE_ID" -var="TIMESTAMP=$CURRENT_TIME" +terraform apply -auto-approve -var="UNIQUE_ID=$UNIQUE_ID" -var="TIMESTAMP=$CURRENT_TIME" diff --git a/.github/scripts/docker-compose-init/init-conformance-state-aws-secrets-manager.sh b/.github/scripts/docker-compose-init/init-conformance-state-aws-secrets-manager.sh new file mode 100755 index 0000000000..6cb9f25917 --- /dev/null +++ b/.github/scripts/docker-compose-init/init-conformance-state-aws-secrets-manager.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +awslocal secretsmanager create-secret \ + --name conftestsecret \ + --secret-string "abcd" + +awslocal secretsmanager create-secret \ + --name secondsecret \ + --secret-string "efgh" \ No newline at end of file diff --git a/.github/scripts/test-info.mjs b/.github/scripts/test-info.mjs index 67e7df5a9b..4d16848e4d 100644 --- a/.github/scripts/test-info.mjs +++ b/.github/scripts/test-info.mjs @@ -492,6 +492,17 @@ const components = { conformance: true, certification: true, }, + 'secretstores.aws.secretsmanager.terraform': { + conformance: true, + requireAWSCredentials: true, + requireTerraform: true, + conformanceSetup: 'conformance-secretstores.aws.secretsmanager.secretsmanager-setup.sh', + conformanceDestroy: 'conformance-secretstores.aws.secretsmanager.secretsmanager-destroy.sh', + }, + 'secretstores.aws.secretsmanager.docker': { + conformance: true, + conformanceSetup: 'docker-compose.sh secrets-manager', + }, 'state.aws.dynamodb': { certification: true, requireAWSCredentials: true, diff --git a/secretstores/aws/secretmanager/metadata.yaml b/secretstores/aws/secretmanager/metadata.yaml new file mode 100644 index 0000000000..21bfbd5b2c --- /dev/null +++ b/secretstores/aws/secretmanager/metadata.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: secretstores +name: aws.secretsmanager +version: v1 +status: beta +title: "AWS Secrets manager" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/aws-secret-manager/ +builtinAuthenticationProfiles: + - name: "aws" +metadata: + - name: endpoint + required: false + description: | + The Secrets manager endpoint. The AWS SDK will generate a default endpoint if not specified. Useful for local testing with AWS LocalStack + example: '"http://localhost:4566"' + type: string \ No newline at end of file diff --git a/secretstores/aws/secretmanager/secretmanager.go b/secretstores/aws/secretmanager/secretmanager.go index 33f5a54c9d..54ed329d35 100644 --- a/secretstores/aws/secretmanager/secretmanager.go +++ b/secretstores/aws/secretmanager/secretmanager.go @@ -41,10 +41,11 @@ func NewSecretManager(logger logger.Logger) secretstores.SecretStore { } type SecretManagerMetaData struct { - Region string `json:"region"` - AccessKey string `json:"accessKey"` - SecretKey string `json:"secretKey"` - SessionToken string `json:"sessionToken"` + Region string `json:"region" mapstructure:"region" mdignore:"true"` + AccessKey string `json:"accessKey" mapstructure:"accessKey" mdignore:"true"` + SecretKey string `json:"secretKey" mapstructure:"secretKey" mdignore:"true"` + SessionToken string `json:"sessionToken" mapstructure:"sessionToken" mdignore:"true"` + Endpoint string `json:"endpoint" mapstructure:"endpoint"` } type smSecretStore struct { @@ -136,7 +137,7 @@ func (s *smSecretStore) BulkGetSecret(ctx context.Context, req secretstores.Bulk } func (s *smSecretStore) getClient(metadata *SecretManagerMetaData) (*secretsmanager.SecretsManager, error) { - sess, err := awsAuth.GetClient(metadata.AccessKey, metadata.SecretKey, metadata.SessionToken, metadata.Region, "") + sess, err := awsAuth.GetClient(metadata.AccessKey, metadata.SecretKey, metadata.SessionToken, metadata.Region, metadata.Endpoint) if err != nil { return nil, err } diff --git a/tests/config/secretstores/aws/secretsmanager/docker/secretsmanager.yml b/tests/config/secretstores/aws/secretsmanager/docker/secretsmanager.yml new file mode 100644 index 0000000000..10b8f61787 --- /dev/null +++ b/tests/config/secretstores/aws/secretsmanager/docker/secretsmanager.yml @@ -0,0 +1,16 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: awssecretmanager +spec: + type: secretstores.aws.secretmanager + version: v1 + metadata: + - name: endpoint + value: "http://localhost:4566" # AWS LocalStack address + - name: accessKey + value: "test" # AWS LocalStack placeholder + - name: secretKey + value: "test" # AWS LocalStack placeholder + - name: region + value: "us-east-1" # AWS LocalStack placeholder diff --git a/tests/config/secretstores/aws/secretsmanager/terraform/secretsmanager.yml b/tests/config/secretstores/aws/secretsmanager/terraform/secretsmanager.yml new file mode 100644 index 0000000000..0824f93bf2 --- /dev/null +++ b/tests/config/secretstores/aws/secretsmanager/terraform/secretsmanager.yml @@ -0,0 +1,15 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: awssecretmanager + namespace: default +spec: + type: secretstores.aws.secretmanager + version: v1 + metadata: + - name: accessKey + value: ${{AWS_ACCESS_KEY_ID}} + - name: secretKey + value: ${{AWS_SECRET_ACCESS_KEY}} + - name: region + value: ${{AWS_REGION}} diff --git a/tests/config/secretstores/tests.yml b/tests/config/secretstores/tests.yml index 134abffa08..40ed714c2f 100644 --- a/tests/config/secretstores/tests.yml +++ b/tests/config/secretstores/tests.yml @@ -5,6 +5,10 @@ components: operations: [] - component: local.file operations: [] + - component: aws.secretsmanager.docker + operations: [] + - component: aws.secretsmanager.terraform + operations: [] - component: azure.keyvault.certificate operations: [] - component: azure.keyvault.serviceprincipal diff --git a/tests/conformance/secretstores/secretstores.go b/tests/conformance/secretstores/secretstores.go index b086f1aa93..e680e7500d 100644 --- a/tests/conformance/secretstores/secretstores.go +++ b/tests/conformance/secretstores/secretstores.go @@ -58,7 +58,7 @@ func ConformanceTests(t *testing.T, props map[string]string, store secretstores. t.Run("ping", func(t *testing.T) { err := secretstores.Ping(context.Background(), store) - // TODO: Ideally, all stable components should implenment ping function, + // TODO: Ideally, all stable components should implement a ping function, // so will only assert require.NoError(t, err) finally, i.e. when current implementation // implements ping in existing stable components if err != nil { diff --git a/tests/conformance/secretstores_test.go b/tests/conformance/secretstores_test.go index ee524e13f4..2b9e7c9cd5 100644 --- a/tests/conformance/secretstores_test.go +++ b/tests/conformance/secretstores_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/dapr/components-contrib/secretstores" + ss_aws "github.com/dapr/components-contrib/secretstores/aws/secretmanager" ss_azure "github.com/dapr/components-contrib/secretstores/azure/keyvault" ss_hashicorp_vault "github.com/dapr/components-contrib/secretstores/hashicorp/vault" ss_kubernetes "github.com/dapr/components-contrib/secretstores/kubernetes" @@ -71,6 +72,10 @@ func loadSecretStore(name string) secretstores.SecretStore { return ss_local_file.NewLocalSecretStore(testLogger) case "hashicorp.vault": return ss_hashicorp_vault.NewHashiCorpVaultSecretStore(testLogger) + case "aws.secretsmanager.docker": + return ss_aws.NewSecretManager(testLogger) + case "aws.secretsmanager.terraform": + return ss_aws.NewSecretManager(testLogger) default: return nil }