feat(postgres): add iam roles anywhere auth profile (#3604)
Signed-off-by: Samantha Coyle <sam@diagrid.io> Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
This commit is contained in:
parent
1e095ed25a
commit
72c92fb1fe
|
@ -38,7 +38,7 @@ func ParseBuiltinAuthenticationProfile(bi BuiltinAuthenticationProfile, componen
|
||||||
metadataPtr[j] = &profile.Metadata[j]
|
metadataPtr[j] = &profile.Metadata[j]
|
||||||
}
|
}
|
||||||
|
|
||||||
if componentTitle == "Apache Kafka" {
|
if componentTitle == "Apache Kafka" || strings.ToLower(componentTitle) == "postgresql" {
|
||||||
removeRequiredOnSomeAWSFields(&metadataPtr)
|
removeRequiredOnSomeAWSFields(&metadataPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,17 +55,17 @@ func ParseBuiltinAuthenticationProfile(bi BuiltinAuthenticationProfile, componen
|
||||||
// Note: We must apply the removal of deprecated fields after the merge!!
|
// Note: We must apply the removal of deprecated fields after the merge!!
|
||||||
|
|
||||||
// Here, we remove some deprecated fields as we support the transition to a new auth profile
|
// Here, we remove some deprecated fields as we support the transition to a new auth profile
|
||||||
if profile.Title == "AWS: Assume specific IAM Role" && componentTitle == "Apache Kafka" {
|
if profile.Title == "AWS: Assume IAM Role" && componentTitle == "Apache Kafka" || profile.Title == "AWS: Assume IAM Role" && strings.ToLower(componentTitle) == "postgresql" {
|
||||||
merged = removeSomeDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
merged = removeSomeDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here, there are no metadata fields that need deprecating
|
// Here, there are no metadata fields that need deprecating
|
||||||
if profile.Title == "AWS: Credentials from Environment Variables" && componentTitle == "Apache Kafka" {
|
if profile.Title == "AWS: Credentials from Environment Variables" && componentTitle == "Apache Kafka" || profile.Title == "AWS: Credentials from Environment Variables" && strings.ToLower(componentTitle) == "postgresql" {
|
||||||
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here, this is a new auth profile, so rm all deprecating fields as unrelated.
|
// Here, this is a new auth profile, so rm all deprecating fields as unrelated.
|
||||||
if profile.Title == "AWS: IAM Roles Anywhere" && componentTitle == "Apache Kafka" {
|
if profile.Title == "AWS: IAM Roles Anywhere" && componentTitle == "Apache Kafka" || profile.Title == "AWS: IAM Roles Anywhere" && strings.ToLower(componentTitle) == "postgresql" {
|
||||||
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ func removeSomeDeprecatedFieldsOnUnrelatedAuthProfiles(metadata []Metadata) []Me
|
||||||
filteredMetadata := []Metadata{}
|
filteredMetadata := []Metadata{}
|
||||||
|
|
||||||
for _, field := range metadata {
|
for _, field := range metadata {
|
||||||
if field.Name == "awsAccessKey" || field.Name == "awsSecretKey" || field.Name == "awsSessionToken" {
|
if field.Name == "awsAccessKey" || field.Name == "awsSecretKey" || field.Name == "awsSessionToken" || field.Name == "awsRegion" {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
filteredMetadata = append(filteredMetadata, field)
|
filteredMetadata = append(filteredMetadata, field)
|
||||||
|
|
|
@ -35,7 +35,7 @@ builtinAuthenticationProfiles:
|
||||||
description: |
|
description: |
|
||||||
This maintains backwards compatibility with existing fields.
|
This maintains backwards compatibility with existing fields.
|
||||||
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
The AWS Region where the AWS Relational Database Service is deployed to.
|
The AWS Region where the AWS service is deployed to.
|
||||||
example: '"us-east-1"'
|
example: '"us-east-1"'
|
||||||
- name: awsAccessKey
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
|
@ -82,7 +82,7 @@ builtinAuthenticationProfiles:
|
||||||
If both fields are set, then 'sessionName' value will be used.
|
If both fields are set, then 'sessionName' value will be used.
|
||||||
Represents the session name for assuming a role.
|
Represents the session name for assuming a role.
|
||||||
example: '"MyAppSession"'
|
example: '"MyAppSession"'
|
||||||
default: '"MSKSASLDefaultSession"'
|
default: '"DaprDefaultSession"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "OIDC Authentication"
|
- title: "OIDC Authentication"
|
||||||
description: |
|
description: |
|
||||||
|
|
|
@ -28,7 +28,7 @@ const (
|
||||||
|
|
||||||
type psqlMetadata struct {
|
type psqlMetadata struct {
|
||||||
pgauth.PostgresAuthMetadata `mapstructure:",squash"`
|
pgauth.PostgresAuthMetadata `mapstructure:",squash"`
|
||||||
aws.AWSIAM `mapstructure:",squash"`
|
aws.DeprecatedPostgresIAM `mapstructure:",squash"`
|
||||||
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,25 +56,31 @@ builtinAuthenticationProfiles:
|
||||||
example: |
|
example: |
|
||||||
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
||||||
type: string
|
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
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
description: |
|
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.
|
AWS access key associated with an IAM account.
|
||||||
example: '"AKIAIOSFODNN7EXAMPLE"'
|
example: '"AKIAIOSFODNN7EXAMPLE"'
|
||||||
- name: awsSecretKey
|
- name: awsSecretKey
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
sensitive: true
|
sensitive: true
|
||||||
description: |
|
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.
|
The secret key associated with the access key.
|
||||||
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
||||||
|
- name: awsRegion
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
This maintains backwards compatibility with existing fields.
|
||||||
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
|
The AWS Region where the AWS service is deployed to.
|
||||||
|
example: '"us-east-1"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "Connection string"
|
- title: "Connection string"
|
||||||
description: "Authenticate using a Connection String"
|
description: "Authenticate using a Connection String"
|
||||||
|
|
|
@ -26,6 +26,8 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
"github.com/dapr/components-contrib/bindings"
|
"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/components-contrib/metadata"
|
||||||
"github.com/dapr/kit/logger"
|
"github.com/dapr/kit/logger"
|
||||||
)
|
)
|
||||||
|
@ -45,6 +47,11 @@ type Postgres struct {
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
db *pgxpool.Pool
|
db *pgxpool.Pool
|
||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
|
|
||||||
|
enableAzureAD bool
|
||||||
|
enableAWSIAM bool
|
||||||
|
|
||||||
|
awsAuthProvider awsAuth.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPostgres returns a new PostgreSQL output binding.
|
// NewPostgres returns a new PostgreSQL output binding.
|
||||||
|
@ -59,17 +66,35 @@ func (p *Postgres) Init(ctx context.Context, meta bindings.Metadata) error {
|
||||||
if p.closed.Load() {
|
if p.closed.Load() {
|
||||||
return errors.New("cannot initialize a previously-closed component")
|
return errors.New("cannot initialize a previously-closed component")
|
||||||
}
|
}
|
||||||
|
opts := pgauth.InitWithMetadataOpts{
|
||||||
|
AzureADEnabled: p.enableAzureAD,
|
||||||
|
AWSIAMEnabled: p.enableAWSIAM,
|
||||||
|
}
|
||||||
m := psqlMetadata{}
|
m := psqlMetadata{}
|
||||||
err := m.InitWithMetadata(meta.Properties)
|
if err := m.InitWithMetadata(meta.Properties); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
poolConfig, err := m.GetPgxPoolConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
poolConfig, err := m.GetPgxPoolConfig()
|
if opts.AWSIAMEnabled && m.UseAWSIAM {
|
||||||
|
opts, validateErr := m.BuildAwsIamOptions(p.logger, meta.Properties)
|
||||||
|
if validateErr != nil {
|
||||||
|
return fmt.Errorf("failed to validate AWS IAM authentication fields: %w", validateErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var provider awsAuth.Provider
|
||||||
|
provider, err = awsAuth.NewProvider(ctx, *opts, awsAuth.GetConfig(*opts))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
p.awsAuthProvider = provider
|
||||||
|
p.awsAuthProvider.UpdatePostgres(ctx, poolConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// This context doesn't control the lifetime of the connection pool, and is
|
// This context doesn't control the lifetime of the connection pool, and is
|
||||||
// only scoped to postgres creating resources at init.
|
// only scoped to postgres creating resources at init.
|
||||||
|
@ -186,7 +211,11 @@ func (p *Postgres) Close() error {
|
||||||
}
|
}
|
||||||
p.db = nil
|
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) {
|
func (p *Postgres) query(ctx context.Context, sql string, args ...any) (result []byte, err error) {
|
||||||
|
|
|
@ -15,16 +15,8 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
|
||||||
v2creds "github.com/aws/aws-sdk-go-v2/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
"github.com/dapr/kit/logger"
|
"github.com/dapr/kit/logger"
|
||||||
|
@ -34,16 +26,6 @@ type EnvironmentSettings struct {
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AWSIAM struct {
|
|
||||||
// Ignored by metadata parser because included in built-in authentication profile
|
|
||||||
// Access key to use for accessing PostgreSQL.
|
|
||||||
AWSAccessKey string `json:"awsAccessKey" mapstructure:"awsAccessKey"`
|
|
||||||
// Secret key to use for accessing PostgreSQL.
|
|
||||||
AWSSecretKey string `json:"awsSecretKey" mapstructure:"awsSecretKey"`
|
|
||||||
// AWS region in which PostgreSQL is deployed.
|
|
||||||
AWSRegion string `json:"awsRegion" mapstructure:"awsRegion"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Delete in Dapr 1.17 so we can move all IAM fields to use the defaults of:
|
// TODO: Delete in Dapr 1.17 so we can move all IAM fields to use the defaults of:
|
||||||
// accessKey and secretKey and region as noted in the docs, and Options struct above.
|
// accessKey and secretKey and region as noted in the docs, and Options struct above.
|
||||||
type DeprecatedKafkaIAM struct {
|
type DeprecatedKafkaIAM struct {
|
||||||
|
@ -55,14 +37,6 @@ type DeprecatedKafkaIAM struct {
|
||||||
StsSessionName string `json:"awsStsSessionName" mapstructure:"awsStsSessionName"`
|
StsSessionName string `json:"awsStsSessionName" mapstructure:"awsStsSessionName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AWSIAMAuthOptions struct {
|
|
||||||
PoolConfig *pgxpool.Config `json:"poolConfig" mapstructure:"poolConfig"`
|
|
||||||
ConnectionString string `json:"connectionString" mapstructure:"connectionString"`
|
|
||||||
Region string `json:"region" mapstructure:"region"`
|
|
||||||
AccessKey string `json:"accessKey" mapstructure:"accessKey"`
|
|
||||||
SecretKey string `json:"secretKey" mapstructure:"secretKey"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Logger logger.Logger
|
Logger logger.Logger
|
||||||
Properties map[string]string
|
Properties map[string]string
|
||||||
|
@ -75,11 +49,20 @@ type Options struct {
|
||||||
Region string `json:"region" mapstructure:"region" mapstructurealiases:"awsRegion"`
|
Region string `json:"region" mapstructure:"region" mapstructurealiases:"awsRegion"`
|
||||||
AccessKey string `json:"accessKey" mapstructure:"accessKey"`
|
AccessKey string `json:"accessKey" mapstructure:"accessKey"`
|
||||||
SecretKey string `json:"secretKey" mapstructure:"secretKey"`
|
SecretKey string `json:"secretKey" mapstructure:"secretKey"`
|
||||||
SessionName string `mapstructure:"sessionName"`
|
SessionName string `json:"sessionName" mapstructure:"sessionName"`
|
||||||
AssumeRoleARN string `mapstructure:"assumeRoleArn"`
|
AssumeRoleARN string `json:"assumeRoleArn" mapstructure:"assumeRoleArn"`
|
||||||
|
SessionToken string `json:"sessionToken" mapstructure:"sessionToken"`
|
||||||
|
|
||||||
Endpoint string
|
Endpoint string
|
||||||
SessionToken string
|
}
|
||||||
|
|
||||||
|
// TODO: Delete in Dapr 1.17 so we can move all IAM fields to use the defaults of:
|
||||||
|
// accessKey and secretKey and region as noted in the docs, and Options struct above.
|
||||||
|
type DeprecatedPostgresIAM struct {
|
||||||
|
// Access key to use for accessing PostgreSQL.
|
||||||
|
AccessKey string `json:"awsAccessKey" mapstructure:"awsAccessKey"`
|
||||||
|
// Secret key to use for accessing PostgreSQL.
|
||||||
|
SecretKey string `json:"awsSecretKey" mapstructure:"awsSecretKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfig(opts Options) *aws.Config {
|
func GetConfig(opts Options) *aws.Config {
|
||||||
|
@ -106,9 +89,14 @@ type Provider interface {
|
||||||
ParameterStore() *ParameterStoreClients
|
ParameterStore() *ParameterStoreClients
|
||||||
Kinesis() *KinesisClients
|
Kinesis() *KinesisClients
|
||||||
Ses() *SesClients
|
Ses() *SesClients
|
||||||
|
|
||||||
Kafka(KafkaOptions) (*KafkaClients, error)
|
Kafka(KafkaOptions) (*KafkaClients, error)
|
||||||
|
|
||||||
|
// Postgres is an outlier to the others in the sense that we can update only it's config,
|
||||||
|
// as we use a max connection time of 8 minutes.
|
||||||
|
// This means that we can just update the config session credentials,
|
||||||
|
// and then in 8 minutes it will update to a new session automatically for us.
|
||||||
|
UpdatePostgres(context.Context, *pgxpool.Config)
|
||||||
|
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,69 +116,6 @@ func NewEnvironmentSettings(md map[string]string) (EnvironmentSettings, error) {
|
||||||
return es, nil
|
return es, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *Options) GetAccessToken(ctx context.Context) (string, error) {
|
|
||||||
dbEndpoint := opts.PoolConfig.ConnConfig.Host + ":" + strconv.Itoa(int(opts.PoolConfig.ConnConfig.Port))
|
|
||||||
var authenticationToken string
|
|
||||||
|
|
||||||
// https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.Connecting.Go.html
|
|
||||||
// Default to load default config through aws credentials file (~/.aws/credentials)
|
|
||||||
awsCfg, err := config.LoadDefaultConfig(ctx)
|
|
||||||
// Note: in the event of an error with invalid config or failed to load config,
|
|
||||||
// then we fall back to using the access key and secret key.
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, config.SharedConfigAssumeRoleError{}.Err),
|
|
||||||
errors.Is(err, config.SharedConfigLoadError{}.Err),
|
|
||||||
errors.Is(err, config.SharedConfigProfileNotExistError{}.Err):
|
|
||||||
// Validate if access key and secret access key are provided
|
|
||||||
if opts.AccessKey == "" || opts.SecretKey == "" {
|
|
||||||
return "", fmt.Errorf("failed to load default configuration for AWS using accessKey and secretKey: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set credentials explicitly
|
|
||||||
awsCfg := v2creds.NewStaticCredentialsProvider(opts.AccessKey, opts.SecretKey, "")
|
|
||||||
authenticationToken, err = auth.BuildAuthToken(
|
|
||||||
ctx, dbEndpoint, opts.Region, opts.PoolConfig.ConnConfig.User, awsCfg)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticationToken, nil
|
|
||||||
case err != nil:
|
|
||||||
return "", errors.New("failed to load default AWS authentication configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticationToken, err = auth.BuildAuthToken(
|
|
||||||
ctx, dbEndpoint, opts.Region, opts.PoolConfig.ConnConfig.User, awsCfg.Credentials)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticationToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts *Options) InitiateAWSIAMAuth() error {
|
|
||||||
// Set max connection lifetime to 8 minutes in postgres connection pool configuration.
|
|
||||||
// Note: this will refresh connections before the 15 min expiration on the IAM AWS auth token,
|
|
||||||
// while leveraging the BeforeConnect hook to recreate the token in time dynamically.
|
|
||||||
opts.PoolConfig.MaxConnLifetime = time.Minute * 8
|
|
||||||
|
|
||||||
// Setup connection pool config needed for AWS IAM authentication
|
|
||||||
opts.PoolConfig.BeforeConnect = func(ctx context.Context, pgConfig *pgx.ConnConfig) error {
|
|
||||||
// Manually reset auth token with aws and reset the config password using the new iam token
|
|
||||||
pwd, errGetAccessToken := opts.GetAccessToken(ctx)
|
|
||||||
if errGetAccessToken != nil {
|
|
||||||
return fmt.Errorf("failed to refresh access token for iam authentication with PostgreSQL: %w", errGetAccessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
pgConfig.Password = pwd
|
|
||||||
opts.PoolConfig.ConnConfig.Password = pwd
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coalesce is a helper function to return the first non-empty string from the inputs
|
// Coalesce is a helper function to return the first non-empty string from the inputs
|
||||||
// This helps us to migrate away from the deprecated duplicate aws auth profile metadata fields in Dapr 1.17.
|
// This helps us to migrate away from the deprecated duplicate aws auth profile metadata fields in Dapr 1.17.
|
||||||
func Coalesce(values ...string) string {
|
func Coalesce(values ...string) string {
|
||||||
|
|
|
@ -17,15 +17,22 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
awsv2 "github.com/aws/aws-sdk-go-v2/aws"
|
awsv2 "github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
v2creds "github.com/aws/aws-sdk-go-v2/credentials"
|
v2creds "github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
"github.com/dapr/kit/logger"
|
"github.com/dapr/kit/logger"
|
||||||
)
|
)
|
||||||
|
@ -227,6 +234,104 @@ func (a *StaticAuth) Ses() *SesClients {
|
||||||
return a.clients.ses
|
return a.clients.ses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *StaticAuth) UpdatePostgres(ctx context.Context, poolConfig *pgxpool.Config) {
|
||||||
|
a.mu.Lock()
|
||||||
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
|
// Set max connection lifetime to 8 minutes in postgres connection pool configuration.
|
||||||
|
// Note: this will refresh connections before the 15 min expiration on the IAM AWS auth token,
|
||||||
|
// while leveraging the BeforeConnect hook to recreate the token in time dynamically.
|
||||||
|
poolConfig.MaxConnLifetime = time.Minute * 8
|
||||||
|
|
||||||
|
// Setup connection pool config needed for AWS IAM authentication
|
||||||
|
poolConfig.BeforeConnect = func(ctx context.Context, pgConfig *pgx.ConnConfig) error {
|
||||||
|
// Manually reset auth token with aws and reset the config password using the new iam token
|
||||||
|
pwd, err := a.getDatabaseToken(ctx, poolConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get database token: %w", err)
|
||||||
|
}
|
||||||
|
pgConfig.Password = pwd
|
||||||
|
poolConfig.ConnConfig.Password = pwd
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.Connecting.Go.html
|
||||||
|
func (a *StaticAuth) getDatabaseToken(ctx context.Context, poolConfig *pgxpool.Config) (string, error) {
|
||||||
|
dbEndpoint := poolConfig.ConnConfig.Host + ":" + strconv.Itoa(int(poolConfig.ConnConfig.Port))
|
||||||
|
|
||||||
|
// First, check if there are credentials set explicitly with accesskey and secretkey
|
||||||
|
var creds credentials.Value
|
||||||
|
if a.session != nil {
|
||||||
|
var err error
|
||||||
|
creds, err = a.session.Config.Credentials.Get()
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Infof("failed to get access key and secret key, will fallback to reading the default AWS credentials file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if creds.AccessKeyID != "" && creds.SecretAccessKey != "" {
|
||||||
|
creds, err := a.session.Config.Credentials.Get()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to retrieve session credentials: %w", err)
|
||||||
|
}
|
||||||
|
awsCfg := v2creds.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken)
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(
|
||||||
|
ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, awsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second, check if we are assuming a role instead
|
||||||
|
if a.assumeRoleARN != nil {
|
||||||
|
awsCfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load default AWS authentication configuration %w", err)
|
||||||
|
}
|
||||||
|
stsClient := sts.NewFromConfig(awsCfg)
|
||||||
|
|
||||||
|
assumeRoleCfg, err := config.LoadDefaultConfig(ctx,
|
||||||
|
config.WithRegion(*a.region),
|
||||||
|
config.WithCredentialsProvider(
|
||||||
|
awsv2.NewCredentialsCache(
|
||||||
|
stscreds.NewAssumeRoleProvider(stsClient, *a.assumeRoleARN, func(aro *stscreds.AssumeRoleOptions) {
|
||||||
|
if a.sessionName != "" {
|
||||||
|
aro.RoleSessionName = a.sessionName
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to assume aws role %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(
|
||||||
|
ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, assumeRoleCfg.Credentials)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lastly, and by default, just use the default aws configuration
|
||||||
|
awsCfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load default AWS authentication configuration %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, awsCfg.Credentials)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *StaticAuth) Kafka(opts KafkaOptions) (*KafkaClients, error) {
|
func (a *StaticAuth) Kafka(opts KafkaOptions) (*KafkaClients, error) {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
|
@ -22,9 +22,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
awsv2 "github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
v2creds "github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/arn"
|
"github.com/aws/aws-sdk-go/aws/arn"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
@ -34,6 +39,12 @@ import (
|
||||||
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere"
|
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere"
|
||||||
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere/rolesanywhereiface"
|
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere/rolesanywhereiface"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||||
|
|
||||||
cryptopem "github.com/dapr/kit/crypto/pem"
|
cryptopem "github.com/dapr/kit/crypto/pem"
|
||||||
spiffecontext "github.com/dapr/kit/crypto/spiffe/context"
|
spiffecontext "github.com/dapr/kit/crypto/spiffe/context"
|
||||||
"github.com/dapr/kit/logger"
|
"github.com/dapr/kit/logger"
|
||||||
|
@ -72,6 +83,7 @@ type x509 struct {
|
||||||
trustProfileArn *string
|
trustProfileArn *string
|
||||||
trustAnchorArn *string
|
trustAnchorArn *string
|
||||||
assumeRoleArn *string
|
assumeRoleArn *string
|
||||||
|
sessionName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newX509(ctx context.Context, opts Options, cfg *aws.Config) (*x509, error) {
|
func newX509(ctx context.Context, opts Options, cfg *aws.Config) (*x509, error) {
|
||||||
|
@ -296,6 +308,105 @@ func (a *x509) Ses() *SesClients {
|
||||||
return a.clients.ses
|
return a.clients.ses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.Connecting.Go.html
|
||||||
|
func (a *x509) getDatabaseToken(ctx context.Context, poolConfig *pgxpool.Config) (string, error) {
|
||||||
|
dbEndpoint := poolConfig.ConnConfig.Host + ":" + strconv.Itoa(int(poolConfig.ConnConfig.Port))
|
||||||
|
|
||||||
|
// First, check if there are credentials set explicitly with accesskey and secretkey
|
||||||
|
var creds credentials.Value
|
||||||
|
if a.session != nil {
|
||||||
|
var err error
|
||||||
|
creds, err = a.session.Config.Credentials.Get()
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Infof("failed to get access key and secret key, will fallback to reading the default AWS credentials file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if creds.AccessKeyID != "" && creds.SecretAccessKey != "" {
|
||||||
|
creds, err := a.session.Config.Credentials.Get()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to retrieve session credentials: %w", err)
|
||||||
|
}
|
||||||
|
awsCfg := v2creds.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken)
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(
|
||||||
|
ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, awsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second, check if we are assuming a role instead
|
||||||
|
if a.assumeRoleArn != nil {
|
||||||
|
awsCfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load default AWS authentication configuration %w", err)
|
||||||
|
}
|
||||||
|
stsClient := sts.NewFromConfig(awsCfg)
|
||||||
|
|
||||||
|
assumeRoleCfg, err := config.LoadDefaultConfig(ctx,
|
||||||
|
config.WithRegion(*a.region),
|
||||||
|
config.WithCredentialsProvider(
|
||||||
|
awsv2.NewCredentialsCache(
|
||||||
|
stscreds.NewAssumeRoleProvider(stsClient, *a.assumeRoleArn, func(aro *stscreds.AssumeRoleOptions) {
|
||||||
|
if a.sessionName != "" {
|
||||||
|
aro.RoleSessionName = a.sessionName
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to assume aws role %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(
|
||||||
|
ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, assumeRoleCfg.Credentials)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lastly, and by default, just use the default aws configuration
|
||||||
|
awsCfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load default AWS authentication configuration %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationToken, err := auth.BuildAuthToken(
|
||||||
|
ctx, dbEndpoint, *a.region, poolConfig.ConnConfig.User, awsCfg.Credentials)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AWS authentication token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticationToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *x509) UpdatePostgres(ctx context.Context, poolConfig *pgxpool.Config) {
|
||||||
|
a.mu.Lock()
|
||||||
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
|
// Set max connection lifetime to 8 minutes in postgres connection pool configuration.
|
||||||
|
// Note: this will refresh connections before the 15 min expiration on the IAM AWS auth token,
|
||||||
|
// while leveraging the BeforeConnect hook to recreate the token in time dynamically.
|
||||||
|
poolConfig.MaxConnLifetime = time.Minute * 8
|
||||||
|
|
||||||
|
// Setup connection pool config needed for AWS IAM authentication
|
||||||
|
poolConfig.BeforeConnect = func(ctx context.Context, pgConfig *pgx.ConnConfig) error {
|
||||||
|
// Manually reset auth token with aws and reset the config password using the new iam token
|
||||||
|
pwd, err := a.getDatabaseToken(ctx, poolConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get database token: %w", err)
|
||||||
|
}
|
||||||
|
pgConfig.Password = pwd
|
||||||
|
poolConfig.ConnConfig.Password = pwd
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *x509) Kafka(opts KafkaOptions) (*KafkaClients, error) {
|
func (a *x509) Kafka(opts KafkaOptions) (*KafkaClients, error) {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/dapr/components-contrib/common/authentication/aws"
|
"github.com/dapr/components-contrib/common/authentication/aws"
|
||||||
"github.com/dapr/components-contrib/common/authentication/azure"
|
"github.com/dapr/components-contrib/common/authentication/azure"
|
||||||
"github.com/dapr/components-contrib/metadata"
|
"github.com/dapr/components-contrib/metadata"
|
||||||
|
"github.com/dapr/kit/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PostgresAuthMetadata contains authentication metadata for PostgreSQL components.
|
// PostgresAuthMetadata contains authentication metadata for PostgreSQL components.
|
||||||
|
@ -86,16 +87,43 @@ func (m *PostgresAuthMetadata) InitWithMetadata(meta map[string]string, opts Ini
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PostgresAuthMetadata) ValidateAwsIamFields() (string, string, string, error) {
|
func (m *PostgresAuthMetadata) BuildAwsIamOptions(logger logger.Logger, properties map[string]string) (*aws.Options, error) {
|
||||||
awsRegion, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSRegion")
|
awsRegion, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSRegion")
|
||||||
if awsRegion == "" {
|
if awsRegion == "" {
|
||||||
return "", "", "", errors.New("metadata property AWSRegion is missing")
|
return nil, errors.New("metadata property AWSRegion is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: access key and secret keys can be optional
|
// Note: access key and secret keys can be optional
|
||||||
// in the event users are leveraging the credential files for an access token.
|
// in the event users are leveraging the credential files for an access token.
|
||||||
awsAccessKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSAccessKey")
|
awsAccessKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSAccessKey")
|
||||||
|
// This is needed as we remove the awsAccessKey field to use the builtin AWS profile 'accessKey' field instead.
|
||||||
|
accessKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AccessKey")
|
||||||
|
if awsAccessKey == "" || accessKey != "" {
|
||||||
|
awsAccessKey = accessKey
|
||||||
|
}
|
||||||
awsSecretKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSSecretKey")
|
awsSecretKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSSecretKey")
|
||||||
return awsRegion, awsAccessKey, awsSecretKey, nil
|
// This is needed as we remove the awsSecretKey field to use the builtin AWS profile 'secretKey' field instead.
|
||||||
|
secretKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "SecretKey")
|
||||||
|
if awsSecretKey == "" || secretKey != "" {
|
||||||
|
awsSecretKey = secretKey
|
||||||
|
}
|
||||||
|
sessionToken, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "sessionToken")
|
||||||
|
assumeRoleArn, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "assumeRoleArn")
|
||||||
|
sessionName, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "sessionName")
|
||||||
|
if sessionName == "" {
|
||||||
|
sessionName = "DaprDefaultSession"
|
||||||
|
}
|
||||||
|
return &aws.Options{
|
||||||
|
Region: awsRegion,
|
||||||
|
AccessKey: awsAccessKey,
|
||||||
|
SecretKey: awsSecretKey,
|
||||||
|
SessionToken: sessionToken,
|
||||||
|
AssumeRoleARN: assumeRoleArn,
|
||||||
|
SessionName: sessionName,
|
||||||
|
|
||||||
|
Logger: logger,
|
||||||
|
Properties: properties,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPgxPoolConfig returns the pgxpool.Config object that contains the credentials for connecting to PostgreSQL.
|
// GetPgxPoolConfig returns the pgxpool.Config object that contains the credentials for connecting to PostgreSQL.
|
||||||
|
@ -154,27 +182,6 @@ func (m *PostgresAuthMetadata) GetPgxPoolConfig() (*pgxpool.Config, error) {
|
||||||
cc.Password = at.Token
|
cc.Password = at.Token
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case m.UseAWSIAM:
|
|
||||||
// We should use AWS IAM
|
|
||||||
awsRegion, awsAccessKey, awsSecretKey, err := m.ValidateAwsIamFields()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("failed to validate AWS IAM authentication fields: %w", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
awsOpts := aws.Options{
|
|
||||||
PoolConfig: config,
|
|
||||||
ConnectionString: m.ConnectionString,
|
|
||||||
Region: awsRegion,
|
|
||||||
AccessKey: awsAccessKey,
|
|
||||||
SecretKey: awsSecretKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = awsOpts.InitiateAWSIAMAuth()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("failed to initiate AWS IAM authentication rotation: %w", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
|
|
@ -39,7 +39,7 @@ type pgMetadata struct {
|
||||||
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
||||||
CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"`
|
CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"`
|
||||||
|
|
||||||
aws.AWSIAM `mapstructure:",squash"`
|
aws.DeprecatedPostgresIAM `mapstructure:",squash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error {
|
func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
|
awsAuth "github.com/dapr/components-contrib/common/authentication/aws"
|
||||||
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
|
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
|
||||||
pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces"
|
pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces"
|
||||||
pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions"
|
pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions"
|
||||||
|
@ -54,6 +55,8 @@ type PostgreSQL struct {
|
||||||
etagColumn string
|
etagColumn string
|
||||||
enableAzureAD bool
|
enableAzureAD bool
|
||||||
enableAWSIAM bool
|
enableAWSIAM bool
|
||||||
|
|
||||||
|
awsAuthProvider awsAuth.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
@ -96,16 +99,31 @@ func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) error {
|
||||||
AWSIAMEnabled: p.enableAWSIAM,
|
AWSIAMEnabled: p.enableAWSIAM,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := p.metadata.InitWithMetadata(meta, opts)
|
if err := p.metadata.InitWithMetadata(meta, opts); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to parse metadata: %w", err)
|
return fmt.Errorf("failed to parse metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
config, err := p.metadata.GetPgxPoolConfig()
|
config, err := p.metadata.GetPgxPoolConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.AWSIAMEnabled && p.metadata.UseAWSIAM {
|
||||||
|
opts, validateErr := p.metadata.BuildAwsIamOptions(p.logger, meta.Properties)
|
||||||
|
if validateErr != nil {
|
||||||
|
return fmt.Errorf("failed to validate AWS IAM authentication fields: %w", validateErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, config)
|
||||||
|
}
|
||||||
|
|
||||||
connCtx, connCancel := context.WithTimeout(ctx, p.metadata.Timeout)
|
connCtx, connCancel := context.WithTimeout(ctx, p.metadata.Timeout)
|
||||||
p.db, err = pgxpool.NewWithConfig(connCtx, config)
|
p.db, err = pgxpool.NewWithConfig(connCtx, config)
|
||||||
connCancel()
|
connCancel()
|
||||||
|
@ -491,11 +509,15 @@ func (p *PostgreSQL) Close() error {
|
||||||
p.db = nil
|
p.db = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errs := make([]error, 2)
|
||||||
if p.gc != nil {
|
if p.gc != nil {
|
||||||
return p.gc.Close()
|
errs[0] = p.gc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if p.awsAuthProvider != nil {
|
||||||
|
errs[1] = p.awsAuthProvider.Close()
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCleanupInterval returns the cleanupInterval property.
|
// GetCleanupInterval returns the cleanupInterval property.
|
||||||
|
|
|
@ -32,7 +32,7 @@ type metadata struct {
|
||||||
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
||||||
ConfigTable string `mapstructure:"table"`
|
ConfigTable string `mapstructure:"table"`
|
||||||
MaxIdleTimeoutOld time.Duration `mapstructure:"connMaxIdleTime"` // Deprecated alias for "connectionMaxIdleTime"
|
MaxIdleTimeoutOld time.Duration `mapstructure:"connMaxIdleTime"` // Deprecated alias for "connectionMaxIdleTime"
|
||||||
aws.AWSIAM `mapstructure:",squash"`
|
aws.DeprecatedPostgresIAM `mapstructure:",squash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metadata) InitWithMetadata(meta map[string]string) error {
|
func (m *metadata) InitWithMetadata(meta map[string]string) error {
|
||||||
|
|
|
@ -46,25 +46,31 @@ builtinAuthenticationProfiles:
|
||||||
example: |
|
example: |
|
||||||
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
||||||
type: string
|
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
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
description: |
|
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.
|
AWS access key associated with an IAM account.
|
||||||
example: '"AKIAIOSFODNN7EXAMPLE"'
|
example: '"AKIAIOSFODNN7EXAMPLE"'
|
||||||
- name: awsSecretKey
|
- name: awsSecretKey
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
sensitive: true
|
sensitive: true
|
||||||
description: |
|
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.
|
The secret key associated with the access key.
|
||||||
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
||||||
|
- name: awsRegion
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
This maintains backwards compatibility with existing fields.
|
||||||
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
|
The AWS Region where the AWS service is deployed to.
|
||||||
|
example: '"us-east-1"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "Connection string"
|
- title: "Connection string"
|
||||||
description: "Authenticate using a Connection String."
|
description: "Authenticate using a Connection String."
|
||||||
|
|
|
@ -31,6 +31,8 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
|
awsAuth "github.com/dapr/components-contrib/common/authentication/aws"
|
||||||
|
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
|
||||||
"github.com/dapr/components-contrib/configuration"
|
"github.com/dapr/components-contrib/configuration"
|
||||||
contribMetadata "github.com/dapr/components-contrib/metadata"
|
contribMetadata "github.com/dapr/components-contrib/metadata"
|
||||||
"github.com/dapr/kit/logger"
|
"github.com/dapr/kit/logger"
|
||||||
|
@ -47,6 +49,10 @@ type ConfigurationStore struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
|
enableAzureAD bool
|
||||||
|
enableAWSIAM bool
|
||||||
|
awsAuthProvider awsAuth.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
type subscription struct {
|
type subscription struct {
|
||||||
|
@ -77,6 +83,10 @@ func NewPostgresConfigurationStore(logger logger.Logger) configuration.Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConfigurationStore) Init(ctx context.Context, metadata configuration.Metadata) error {
|
func (p *ConfigurationStore) Init(ctx context.Context, metadata configuration.Metadata) error {
|
||||||
|
opts := pgauth.InitWithMetadataOpts{
|
||||||
|
AzureADEnabled: p.enableAzureAD,
|
||||||
|
AWSIAMEnabled: p.enableAWSIAM,
|
||||||
|
}
|
||||||
err := p.metadata.InitWithMetadata(metadata.Properties)
|
err := p.metadata.InitWithMetadata(metadata.Properties)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error(err)
|
p.logger.Error(err)
|
||||||
|
@ -84,11 +94,37 @@ func (p *ConfigurationStore) Init(ctx context.Context, metadata configuration.Me
|
||||||
}
|
}
|
||||||
|
|
||||||
p.ActiveSubscriptions = make(map[string]*subscription)
|
p.ActiveSubscriptions = make(map[string]*subscription)
|
||||||
p.client, err = p.connectDB(ctx)
|
config, err := p.metadata.GetPgxPoolConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error connecting to configuration store: '%w'", err)
|
return fmt.Errorf("PostgreSQL configuration store connection error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.AWSIAMEnabled && p.metadata.UseAWSIAM {
|
||||||
|
opts, validateErr := p.metadata.BuildAwsIamOptions(p.logger, metadata.Properties)
|
||||||
|
if validateErr != nil {
|
||||||
|
return fmt.Errorf("failed to validate AWS IAM authentication fields: %w", validateErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool, err := pgxpool.NewWithConfig(ctx, config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("PostgreSQL configuration store connection error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pool.Ping(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("PostgreSQL configuration store ping error: %w", err)
|
||||||
|
}
|
||||||
|
p.client = pool
|
||||||
|
|
||||||
err = p.client.Ping(ctx)
|
err = p.client.Ping(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to connect to configuration store: '%w'", err)
|
return fmt.Errorf("unable to connect to configuration store: '%w'", err)
|
||||||
|
@ -304,25 +340,6 @@ func (p *ConfigurationStore) handleSubscribedChange(ctx context.Context, handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConfigurationStore) connectDB(ctx context.Context) (*pgxpool.Pool, error) {
|
|
||||||
config, err := p.metadata.GetPgxPoolConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("PostgreSQL configuration store connection error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := pgxpool.NewWithConfig(ctx, config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("PostgreSQL configuration store connection error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pool.Ping(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("PostgreSQL configuration store ping error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildQuery(req *configuration.GetRequest, configTable string) (string, []interface{}, error) {
|
func buildQuery(req *configuration.GetRequest, configTable string) (string, []interface{}, error) {
|
||||||
var query string
|
var query string
|
||||||
var params []interface{}
|
var params []interface{}
|
||||||
|
@ -436,5 +453,9 @@ func (p *ConfigurationStore) Close() error {
|
||||||
p.client.Close()
|
p.client.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
errs := make([]error, 1)
|
||||||
|
if p.awsAuthProvider != nil {
|
||||||
|
errs[0] = p.awsAuthProvider.Close()
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -46,6 +46,7 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.43
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.43
|
||||||
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10
|
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10
|
||||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.17.3
|
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.17.3
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.32.4
|
||||||
github.com/aws/rolesanywhere-credential-helper v1.0.4
|
github.com/aws/rolesanywhere-credential-helper v1.0.4
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
|
||||||
github.com/camunda/zeebe/clients/go/v8 v8.2.12
|
github.com/camunda/zeebe/clients/go/v8 v8.2.12
|
||||||
|
@ -188,7 +189,6 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.4 // indirect
|
|
||||||
github.com/aws/smithy-go v1.22.0 // indirect
|
github.com/aws/smithy-go v1.22.0 // indirect
|
||||||
github.com/awslabs/kinesis-aggregation/go v0.0.0-20210630091500-54e17340d32f // indirect
|
github.com/awslabs/kinesis-aggregation/go v0.0.0-20210630091500-54e17340d32f // indirect
|
||||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
|
|
|
@ -29,7 +29,7 @@ builtinAuthenticationProfiles:
|
||||||
description: |
|
description: |
|
||||||
This maintains backwards compatibility with existing fields.
|
This maintains backwards compatibility with existing fields.
|
||||||
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
The AWS Region where the AWS Relational Database Service is deployed to.
|
The AWS Region where the AWS service is deployed to.
|
||||||
example: '"us-east-1"'
|
example: '"us-east-1"'
|
||||||
- name: awsAccessKey
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
|
@ -76,7 +76,7 @@ builtinAuthenticationProfiles:
|
||||||
If both fields are set, then 'sessionName' value will be used.
|
If both fields are set, then 'sessionName' value will be used.
|
||||||
Represents the session name for assuming a role.
|
Represents the session name for assuming a role.
|
||||||
example: '"MyAppSession"'
|
example: '"MyAppSession"'
|
||||||
default: '"MSKSASLDefaultSession"'
|
default: '"DaprDefaultSession"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "OIDC Authentication"
|
- title: "OIDC Authentication"
|
||||||
description: |
|
description: |
|
||||||
|
|
|
@ -53,16 +53,12 @@ builtinAuthenticationProfiles:
|
||||||
example: |
|
example: |
|
||||||
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
||||||
type: string
|
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
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: false
|
||||||
description: |
|
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.
|
AWS access key associated with an IAM account.
|
||||||
example: '"AKIAIOSFODNN7EXAMPLE"'
|
example: '"AKIAIOSFODNN7EXAMPLE"'
|
||||||
- name: awsSecretKey
|
- name: awsSecretKey
|
||||||
|
@ -70,8 +66,18 @@ builtinAuthenticationProfiles:
|
||||||
required: false
|
required: false
|
||||||
sensitive: true
|
sensitive: true
|
||||||
description: |
|
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.
|
The secret key associated with the access key.
|
||||||
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
||||||
|
- name: awsRegion
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
This maintains backwards compatibility with existing fields.
|
||||||
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
|
The AWS Region where the AWS service is deployed to.
|
||||||
|
example: '"us-east-1"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "Connection string"
|
- title: "Connection string"
|
||||||
description: "Authenticate using a Connection String"
|
description: "Authenticate using a Connection String"
|
||||||
|
|
|
@ -43,7 +43,7 @@ type pgMetadata struct {
|
||||||
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"`
|
||||||
CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"`
|
CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"`
|
||||||
|
|
||||||
aws.AWSIAM `mapstructure:",squash"`
|
aws.DeprecatedPostgresIAM `mapstructure:",squash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error {
|
func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error {
|
||||||
|
@ -60,7 +60,7 @@ func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithM
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and sanitize input
|
// Validate and sanitize inputq
|
||||||
err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, opts)
|
err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -52,16 +52,12 @@ builtinAuthenticationProfiles:
|
||||||
example: |
|
example: |
|
||||||
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
"host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require"
|
||||||
type: string
|
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
|
- name: awsAccessKey
|
||||||
type: string
|
type: string
|
||||||
required: false
|
required: false
|
||||||
description: |
|
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.
|
AWS access key associated with an IAM account.
|
||||||
example: '"AKIAIOSFODNN7EXAMPLE"'
|
example: '"AKIAIOSFODNN7EXAMPLE"'
|
||||||
- name: awsSecretKey
|
- name: awsSecretKey
|
||||||
|
@ -69,8 +65,18 @@ builtinAuthenticationProfiles:
|
||||||
required: false
|
required: false
|
||||||
sensitive: true
|
sensitive: true
|
||||||
description: |
|
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.
|
The secret key associated with the access key.
|
||||||
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
|
||||||
|
- name: awsRegion
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
This maintains backwards compatibility with existing fields.
|
||||||
|
It will be deprecated as of Dapr 1.17. Use 'region' instead.
|
||||||
|
The AWS Region where the AWS service is deployed to.
|
||||||
|
example: '"us-east-1"'
|
||||||
authenticationProfiles:
|
authenticationProfiles:
|
||||||
- title: "Connection string"
|
- title: "Connection string"
|
||||||
description: "Authenticate using a Connection String"
|
description: "Authenticate using a Connection String"
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
|
awsAuth "github.com/dapr/components-contrib/common/authentication/aws"
|
||||||
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
|
pgauth "github.com/dapr/components-contrib/common/authentication/postgresql"
|
||||||
pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces"
|
pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces"
|
||||||
pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions"
|
pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions"
|
||||||
|
@ -51,6 +52,8 @@ type PostgreSQL struct {
|
||||||
|
|
||||||
enableAzureAD bool
|
enableAzureAD bool
|
||||||
enableAWSIAM bool
|
enableAWSIAM bool
|
||||||
|
|
||||||
|
awsAuthProvider awsAuth.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
@ -98,6 +101,21 @@ func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) (err error)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.AWSIAMEnabled && p.metadata.UseAWSIAM {
|
||||||
|
opts, validateErr := p.metadata.BuildAwsIamOptions(p.logger, meta.Properties)
|
||||||
|
if validateErr != nil {
|
||||||
|
return fmt.Errorf("failed to validate AWS IAM authentication fields: %w", validateErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, config)
|
||||||
|
}
|
||||||
|
|
||||||
connCtx, connCancel := context.WithTimeout(ctx, p.metadata.Timeout)
|
connCtx, connCancel := context.WithTimeout(ctx, p.metadata.Timeout)
|
||||||
defer connCancel()
|
defer connCancel()
|
||||||
p.db, err = pgxpool.NewWithConfig(connCtx, config)
|
p.db, err = pgxpool.NewWithConfig(connCtx, config)
|
||||||
|
@ -534,11 +552,15 @@ func (p *PostgreSQL) Close() error {
|
||||||
p.db = nil
|
p.db = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errs := make([]error, 2)
|
||||||
if p.gc != nil {
|
if p.gc != nil {
|
||||||
return p.gc.Close()
|
errs[0] = p.gc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if p.awsAuthProvider != nil {
|
||||||
|
errs[1] = p.awsAuthProvider.Close()
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCleanupInterval returns the cleanupInterval property.
|
// GetCleanupInterval returns the cleanupInterval property.
|
||||||
|
|
Loading…
Reference in New Issue