Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(postgres): add iam roles anywhere auth profile #3604

Merged
merged 16 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,17 @@ func ParseBuiltinAuthenticationProfile(bi BuiltinAuthenticationProfile, componen

res[i].Metadata = mergedMetadata(bi.Metadata, res[i].Metadata...)

// If component is PostgreSQL, filter out duplicated aws profile fields
if strings.ToLower(componentTitle) == "postgresql" && bi.Name == "aws" {
res[i].Metadata = filterOutDuplicateFields(res[i].Metadata)
// We only need to transition this change on this AWS profile,
// as secret keys and access keys are irrelevant on the other AWS profiles.
if profile.Title == "AWS: Access Key ID and Secret Access Key" {
// If component is PostgreSQL, handle deprecation of aws profile fields that we will remove in Dapr 1.17
if strings.ToLower(componentTitle) == "postgresql" && bi.Name == "aws" {
res[i].Metadata = removeRequiredOnPostgresAWSFields(res[i].Metadata)
}
} else {
if strings.ToLower(componentTitle) == "postgresql" && bi.Name == "aws" {
res[i].Metadata = filterOutDuplicateFields(res[i].Metadata)
}
}

}
Expand All @@ -54,10 +62,14 @@ func mergedMetadata(base []Metadata, add ...Metadata) []Metadata {
return res
}

// filterOutDuplicateFields removes specific duplicated fields from the metadata
func filterOutDuplicateFields(metadata []Metadata) []Metadata {
// removeRequiredOnPostgresAWSFields needs to be removed in Dapr 1.17 as duplicated AWS IAM fields get removed,
// and we standardize on these fields.
// Currently, there are: awsAccessKey, accessKey and awsSecretKey, secretKey fields.
// We normally have accessKey and secretKey fields marked required as it is part of the builtin AWS auth profile fields.
// However, as we rm the aws prefixed ones, we need to then mark the normally required ones as not required only for postgres.
// This way we do not break existing users.
func removeRequiredOnPostgresAWSFields(metadata []Metadata) []Metadata {
duplicateFields := map[string]int{
"awsRegion": 0,
"accessKey": 0,
"secretKey": 0,
}
Expand All @@ -68,14 +80,34 @@ func filterOutDuplicateFields(metadata []Metadata) []Metadata {
if _, exists := duplicateFields[field.Name]; !exists {
filteredMetadata = append(filteredMetadata, field)
} else {
if field.Name == "awsRegion" && duplicateFields["awsRegion"] == 0 {
if field.Name == "accessKey" && duplicateFields["accessKey"] == 0 {
field.Required = false
filteredMetadata = append(filteredMetadata, field)
} else if field.Name == "secretKey" && duplicateFields["secretKey"] == 0 {
field.Required = false
filteredMetadata = append(filteredMetadata, field)
duplicateFields["awsRegion"]++
} else if field.Name != "awsRegion" {
continue
}
}
}

return filteredMetadata
}

// filterOutDuplicateFields removes specific duplicated fields from the metadata
// TODO: This must be removed in Dapr 1.17 as we move away from the custom postgres fields for AWS auth with the aws prefix.
func filterOutDuplicateFields(metadata []Metadata) []Metadata {
duplicateFields := map[string]int{
"awsAccessKey": 0,
"awsSecretKey": 0,
}

filteredMetadata := []Metadata{}

for _, field := range metadata {
if _, exists := duplicateFields[field.Name]; !exists {
filteredMetadata = append(filteredMetadata, field)
}
}

return filteredMetadata
}
5 changes: 4 additions & 1 deletion bindings/aws/dynamodb/dynamodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,8 @@ func (d *DynamoDB) GetComponentMetadata() (metadataInfo metadata.MetadataMap) {
}

func (d *DynamoDB) Close() error {
return d.authProvider.Close()
if d.authProvider != nil {
return d.authProvider.Close()
}
return nil
}
5 changes: 4 additions & 1 deletion bindings/aws/kinesis/kinesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ func (a *AWSKinesis) Close() error {
close(a.closeCh)
}
a.wg.Wait()
return a.authProvider.Close()
if a.authProvider != nil {
return a.authProvider.Close()
}
return nil
}

func (a *AWSKinesis) ensureConsumer(ctx context.Context, streamARN *string) (*string, error) {
Expand Down
5 changes: 4 additions & 1 deletion bindings/aws/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ func (s *AWSS3) Init(ctx context.Context, metadata bindings.Metadata) error {
}

func (s *AWSS3) Close() error {
return s.authProvider.Close()
if s.authProvider != nil {
return s.authProvider.Close()
}
return nil
}

func (s *AWSS3) Operations() []bindings.OperationKind {
Expand Down
5 changes: 4 additions & 1 deletion bindings/aws/ses/ses.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,8 @@ func (a *AWSSES) GetComponentMetadata() (metadataInfo contribMetadata.MetadataMa
}

func (a *AWSSES) Close() error {
return a.authProvider.Close()
if a.authProvider != nil {
return a.authProvider.Close()
}
return nil
}
5 changes: 4 additions & 1 deletion bindings/aws/sns/sns.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,8 @@ func (a *AWSSNS) GetComponentMetadata() (metadataInfo metadata.MetadataMap) {
}

func (a *AWSSNS) Close() error {
return a.authProvider.Close()
if a.authProvider != nil {
return a.authProvider.Close()
}
return nil
}
5 changes: 4 additions & 1 deletion bindings/aws/sqs/sqs.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ func (a *AWSSQS) Close() error {
close(a.closeCh)
}
a.wg.Wait()
return a.authProvider.Close()
if a.authProvider != nil {
return a.authProvider.Close()
}
return nil
}

func (a *AWSSQS) parseSQSMetadata(meta bindings.Metadata) (*sqsMetadata, error) {
Expand Down
2 changes: 1 addition & 1 deletion bindings/postgres/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (

type psqlMetadata struct {
pgauth.PostgresAuthMetadata `mapstructure:",squash"`
aws.AWSIAM `mapstructure:",squash"`
aws.DeprecatedPostgresIAM `mapstructure:",squash"`
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
}

Expand Down
14 changes: 6 additions & 8 deletions bindings/postgres/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,21 @@ builtinAuthenticationProfiles:
example: |
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
type: string
- name: awsRegion
type: string
required: true
description: |
The AWS Region where the AWS Relational Database Service is deployed to.
example: '"us-east-1"'
- name: awsAccessKey
type: string
required: true
required: false
description: |
Deprecated as of Dapr 1.17. Use 'accessKey' instead if using AWS IAM.
If both fields are set, then 'accessKey' value will be used.
AWS access key associated with an IAM account.
example: '"AKIAIOSFODNN7EXAMPLE"'
- name: awsSecretKey
type: string
required: true
required: false
sensitive: true
description: |
Deprecated as of Dapr 1.17. Use 'secretKey' instead if using AWS IAM.
If both fields are set, then 'secretKey' value will be used.
The secret key associated with the access key.
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
authenticationProfiles:
Expand Down
45 changes: 41 additions & 4 deletions bindings/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"github.com/jackc/pgx/v5/pgxpool"

"github.com/dapr/components-contrib/bindings"
awsAuth "github.com/dapr/components-contrib/common/authentication/aws"
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
)
Expand All @@ -45,6 +47,11 @@ type Postgres struct {
logger logger.Logger
db *pgxpool.Pool
closed atomic.Bool

enableAzureAD bool
enableAWSIAM bool

awsAuthProvider awsAuth.Provider
}

// NewPostgres returns a new PostgreSQL output binding.
Expand All @@ -59,18 +66,44 @@ func (p *Postgres) Init(ctx context.Context, meta bindings.Metadata) error {
if p.closed.Load() {
return errors.New("cannot initialize a previously-closed component")
}

opts := pgauth.InitWithMetadataOpts{
AzureADEnabled: p.enableAzureAD,
AWSIAMEnabled: p.enableAWSIAM,
}
m := psqlMetadata{}
err := m.InitWithMetadata(meta.Properties)
if err != nil {
if err := m.InitWithMetadata(meta.Properties); err != nil {
return err
}

var err error
poolConfig, err := m.GetPgxPoolConfig()
if err != nil {
return err
}

if opts.AWSIAMEnabled && m.UseAWSIAM {
region, accessKey, secretKey, validateErr := m.ValidateAwsIamFields()
if validateErr != nil {
return fmt.Errorf("failed to validate AWS IAM authentication fields: %w", validateErr)
}
opts := awsAuth.Options{
Logger: p.logger,
Properties: meta.Properties,
Region: region,
Endpoint: "",
AccessKey: accessKey,
SecretKey: secretKey,
SessionToken: "",
}
var provider awsAuth.Provider
provider, err = awsAuth.NewProvider(ctx, opts, awsAuth.GetConfig(opts))
if err != nil {
return err
}
p.awsAuthProvider = provider
p.awsAuthProvider.UpdatePostgres(ctx, poolConfig)
}

// This context doesn't control the lifetime of the connection pool, and is
// only scoped to postgres creating resources at init.
p.db, err = pgxpool.NewWithConfig(ctx, poolConfig)
Expand Down Expand Up @@ -177,7 +210,11 @@ func (p *Postgres) Close() error {
}
p.db = nil

return nil
errs := make([]error, 1)
if p.awsAuthProvider != nil {
errs[0] = p.awsAuthProvider.Close()
}
return errors.Join(errs...)
}

func (p *Postgres) query(ctx context.Context, sql string, args ...any) (result []byte, err error) {
Expand Down
Loading
Loading