From e8e4680de5266b57d1f6dd8eaf98106524f38f15 Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 17:41:17 -0800 Subject: [PATCH 1/6] Init --- go.mod | 25 ++++++++++---------- go.sum | 46 ++++++++++++++++++------------------ pgutils/connector.go | 55 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index 37708eb..a10455a 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,14 @@ module github.com/corbaltcode/go-libraries -go 1.22 +go 1.23 require ( - github.com/aws/aws-sdk-go-v2 v1.38.0 + github.com/aws/aws-sdk-go-v2 v1.41.0 github.com/aws/aws-sdk-go-v2/config v1.31.0 + github.com/aws/aws-sdk-go-v2/credentials v1.19.5 + github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.16 github.com/aws/aws-sdk-go-v2/service/ssm v1.63.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 github.com/coreos/go-oidc/v3 v3.6.0 github.com/google/go-cmp v0.5.9 github.com/jmoiron/sqlx v1.3.5 @@ -14,17 +17,15 @@ require ( ) require ( - github.com/aws/aws-sdk-go-v2/credentials v1.18.4 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 // indirect - github.com/aws/smithy-go v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/golang/protobuf v1.5.2 // indirect diff --git a/go.sum b/go.sum index 66c65f1..fd22c0a 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +1,33 @@ -github.com/aws/aws-sdk-go-v2 v1.38.0 h1:UCRQ5mlqcFk9HJDIqENSLR3wiG1VTWlyUfLDEvY7RxU= -github.com/aws/aws-sdk-go-v2 v1.38.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= +github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= +github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/config v1.31.0 h1:9yH0xiY5fUnVNLRWO0AtayqwU1ndriZdN78LlhruJR4= github.com/aws/aws-sdk-go-v2/config v1.31.0/go.mod h1:VeV3K72nXnhbe4EuxxhzsDc/ByrCSlZwUnWH52Nde/I= -github.com/aws/aws-sdk-go-v2/credentials v1.18.4 h1:IPd0Algf1b+Qy9BcDp0sCUcIWdCQPSzDoMK3a8pcbUM= -github.com/aws/aws-sdk-go-v2/credentials v1.18.4/go.mod h1:nwg78FjH2qvsRM1EVZlX9WuGUJOL5od+0qvm0adEzHk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 h1:GicIdnekoJsjq9wqnvyi2elW6CGMSYKhdozE7/Svh78= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3/go.mod h1:R7BIi6WNC5mc1kfRM7XM/VHC3uRWkjc396sfabq4iOo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 h1:o9RnO+YZ4X+kt5Z7Nvcishlz0nksIt2PIzDglLMP0vA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3/go.mod h1:+6aLJzOG1fvMOyzIySYjOFjcguGvVRL68R+uoRencN4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 h1:joyyUFhiTQQmVK6ImzNU9TQSNRNeD9kOklqTzyk5v6s= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3/go.mod h1:+vNIyZQP3b3B1tSLI0lxvrU9cfM7gpdRXMFfm67ZcPc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.16 h1:LFB4eCU2S9wpFAkEnSqtP8CgdOk0cjMIzuXas1+rbWM= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.16/go.mod h1:Q7hjCcQzFZ9QgZ+xeJhO4X1rv7uKAl4aoBEjab6MS8k= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 h1:ieRzyHXypu5ByllM7Sp4hC5f/1Fy5wqxqY0yB85hC7s= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3/go.mod h1:O5ROz8jHiOAKAwx179v+7sHMhfobFVi6nZt8DEyiYoM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= github.com/aws/aws-sdk-go-v2/service/ssm v1.63.0 h1:1T8wFNEtOP4lgLC7v8Fzgbb4kFrMmnscG7kOqkbA26c= github.com/aws/aws-sdk-go-v2/service/ssm v1.63.0/go.mod h1:CDVmu8K5JKdgdJakdZ9gC3K6OJ/+izv/kUncFeGRIj4= -github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 h1:Mc/MKBf2m4VynyJkABoVEN+QzkfLqGj0aiJuEe7cMeM= -github.com/aws/aws-sdk-go-v2/service/sso v1.28.0/go.mod h1:iS5OmxEcN4QIPXARGhavH7S8kETNL11kym6jhoS7IUQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 h1:6csaS/aJmqZQbKhi1EyEMM7yBW653Wy/B9hnBofW+sw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0/go.mod h1:59qHWaY5B+Rs7HGTuVGaC32m0rdpQ68N8QCN3khYiqs= -github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 h1:MG9VFW43M4A8BYeAfaJJZWrroinxeTi2r3+SnmLQfSA= -github.com/aws/aws-sdk-go-v2/service/sts v1.37.0/go.mod h1:JdeBDPgpJfuS6rU/hNglmOigKhyEZtBmbraLE4GK1J8= -github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= -github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= diff --git a/pgutils/connector.go b/pgutils/connector.go index 8a0951e..e21324a 100644 --- a/pgutils/connector.go +++ b/pgutils/connector.go @@ -6,13 +6,16 @@ import ( "fmt" "log" "net/url" + "time" "database/sql" "database/sql/driver" "github.com/aws/aws-sdk-go-v2/aws" awsconfig "github.com/aws/aws-sdk-go-v2/config" + "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/jmoiron/sqlx" "github.com/lib/pq" ) @@ -87,10 +90,30 @@ func NewPostgresqlConnectorFromConnectionString(connectionString string) *Postgr } } +//type IAMAuthConfig struct { +// RDSEndpoint string +// User string +// Database string +//} + type IAMAuthConfig struct { RDSEndpoint string User string Database string + + // Optional: cross-account role assumption. + // Set this to a role ARN in the RDS account (Account A) that has rds-db:connect. + AssumeRoleARN string + + // Optional: if your trust policy requires an external ID. + AssumeRoleExternalID string + + // Optional: override the default session name. + AssumeRoleSessionName string + + // Optional: override STS assume role duration. + // If zero, SDK default is used. + AssumeRoleDuration time.Duration } type iamAuthConnectionStringProvider struct { @@ -131,11 +154,40 @@ func NewPostgresqlConnectorWithIAMAuth(ctx context.Context, cfg *IAMAuthConfig) return nil, errors.New("AWS region is not configured") } + creds := awsCfg.Credentials + + // Cross-account support: + // If AssumeRoleARN is set, assume a role in the RDS account (Account A) + // using the ECS task role creds from Account B as the source credentials. + if cfg.AssumeRoleARN != "" { + stsClient := sts.NewFromConfig(awsCfg) + + sessionName := cfg.AssumeRoleSessionName + if sessionName == "" { + sessionName = "pgutils-rds-iam" + } + + assumeProvider := stscreds.NewAssumeRoleProvider(stsClient, cfg.AssumeRoleARN, func(assumeRoleOpts *stscreds.AssumeRoleOptions) { + assumeRoleOpts.RoleSessionName = sessionName + + if cfg.AssumeRoleExternalID != "" { + assumeRoleOpts.ExternalID = aws.String(cfg.AssumeRoleExternalID) + } + + if cfg.AssumeRoleDuration != 0 { + assumeRoleOpts.Duration = cfg.AssumeRoleDuration + } + }) + + // Cache to avoid calling STS too frequently. + creds = aws.NewCredentialsCache(assumeProvider) + } + return &PostgresqlConnector{ baseConnectionStringProvider: &iamAuthConnectionStringProvider{ IAMAuthConfig: *cfg, region: awsCfg.Region, - creds: awsCfg.Credentials, + creds: creds, }, }, nil } @@ -145,4 +197,3 @@ func OpenDB(conn *PostgresqlConnector) *sqlx.DB { sqlDB := sql.OpenDB(conn) return sqlx.NewDb(sqlDB, "postgres") } - From 19b487a94af9d51eebcbb5913f54fea87f8bed35 Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 20:07:25 -0800 Subject: [PATCH 2/6] Update prints --- pgutils/connector.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pgutils/connector.go b/pgutils/connector.go index e21324a..51b0279 100644 --- a/pgutils/connector.go +++ b/pgutils/connector.go @@ -90,12 +90,6 @@ func NewPostgresqlConnectorFromConnectionString(connectionString string) *Postgr } } -//type IAMAuthConfig struct { -// RDSEndpoint string -// User string -// Database string -//} - type IAMAuthConfig struct { RDSEndpoint string User string @@ -128,7 +122,7 @@ func (p *iamAuthConnectionStringProvider) getBaseConnectionString(ctx context.Co if err != nil { return "", fmt.Errorf("building auth token: %w", err) } - log.Printf("Signing RDS IAM token for user: %s", p.User) + log.Printf("Signing RDS IAM token for endpoint: %s user: %s", p.RDSEndpoint, p.User) dsnURL := &url.URL{ Scheme: "postgresql", @@ -160,6 +154,7 @@ func NewPostgresqlConnectorWithIAMAuth(ctx context.Context, cfg *IAMAuthConfig) // If AssumeRoleARN is set, assume a role in the RDS account (Account A) // using the ECS task role creds from Account B as the source credentials. if cfg.AssumeRoleARN != "" { + log.Printf("RDS IAM Assuming Role: %s", cfg.AssumeRoleARN) stsClient := sts.NewFromConfig(awsCfg) sessionName := cfg.AssumeRoleSessionName From 52cf7c82228354639f1ce3fb25a9e689192d06d8 Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 20:29:03 -0800 Subject: [PATCH 3/6] Update prints --- pgutils/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgutils/connector.go b/pgutils/connector.go index 51b0279..d6dcef9 100644 --- a/pgutils/connector.go +++ b/pgutils/connector.go @@ -154,7 +154,7 @@ func NewPostgresqlConnectorWithIAMAuth(ctx context.Context, cfg *IAMAuthConfig) // If AssumeRoleARN is set, assume a role in the RDS account (Account A) // using the ECS task role creds from Account B as the source credentials. if cfg.AssumeRoleARN != "" { - log.Printf("RDS IAM Assuming Role: %s", cfg.AssumeRoleARN) + log.Printf("RDS IAM Assuming Role: %s for \n Endpoint: %s \n User: %s \n Database: %s", cfg.AssumeRoleARN, cfg.RDSEndpoint, cfg.User, cfg.Database) stsClient := sts.NewFromConfig(awsCfg) sessionName := cfg.AssumeRoleSessionName From 10a250f33faef96ea0468480473efe1acf735397 Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 20:35:23 -0800 Subject: [PATCH 4/6] More prints --- pgutils/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgutils/connector.go b/pgutils/connector.go index d6dcef9..49ae6e2 100644 --- a/pgutils/connector.go +++ b/pgutils/connector.go @@ -122,7 +122,7 @@ func (p *iamAuthConnectionStringProvider) getBaseConnectionString(ctx context.Co if err != nil { return "", fmt.Errorf("building auth token: %w", err) } - log.Printf("Signing RDS IAM token for endpoint: %s user: %s", p.RDSEndpoint, p.User) + log.Printf("Signing RDS IAM token for \n Endpoint: %s \n User: %s \n Database: %s", p.RDSEndpoint, p.User, p.Database) dsnURL := &url.URL{ Scheme: "postgresql", From 66b547de0392dff9e49182354a5f007b077020a6 Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 20:36:50 -0800 Subject: [PATCH 5/6] Even more prints --- pgutils/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgutils/connector.go b/pgutils/connector.go index 49ae6e2..9037fb7 100644 --- a/pgutils/connector.go +++ b/pgutils/connector.go @@ -122,7 +122,7 @@ func (p *iamAuthConnectionStringProvider) getBaseConnectionString(ctx context.Co if err != nil { return "", fmt.Errorf("building auth token: %w", err) } - log.Printf("Signing RDS IAM token for \n Endpoint: %s \n User: %s \n Database: %s", p.RDSEndpoint, p.User, p.Database) + log.Printf("Signing RDS IAM token for \n Endpoint: %s \n User: %s \n Database: %s", p.RDSEndpoint, p.User, p.Database) dsnURL := &url.URL{ Scheme: "postgresql", From e7006f0db7bc1c86ca9a1a59b310c1bc5707ac6e Mon Sep 17 00:00:00 2001 From: Andrew Cremins Date: Tue, 9 Dec 2025 21:03:47 -0800 Subject: [PATCH 6/6] Bump go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a10455a..5018873 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/corbaltcode/go-libraries -go 1.23 +go 1.24 require ( github.com/aws/aws-sdk-go-v2 v1.41.0