Skip to content
Open
12 changes: 12 additions & 0 deletions cmd/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/formancehq/go-libs/v5/pkg/cloud/aws/iam"
"github.com/formancehq/go-libs/v5/pkg/fx/storagefx"
"github.com/formancehq/go-libs/v5/pkg/observe/metrics"
"github.com/formancehq/go-libs/v5/pkg/observe/traces"
Expand Down Expand Up @@ -132,6 +133,17 @@ func NewWorkerCommand() *cobra.Command {
connect.AddFlags(cmd.Flags())
metrics.AddFlags(cmd.Flags())
traces.AddFlags(cmd.Flags())
// iam.AddFlags registers --aws-region, --aws-access-key-id,
// --aws-secret-access-key, --aws-session-token, --aws-profile, and
// --aws-role-arn. All flags except --aws-role-arn are consumed by
// iam.LoadOptionFromFlags inside connect.ConnectionOptionsFromFlags.
// --aws-role-arn is not yet read by iam.LoadOptionFromFlags in go-libs;
// passing it has no effect on the credential chain. Full role-assumption
// support is tracked for a future go-libs update.
iam.AddFlags(cmd.Flags())
Comment thread
NumaryBot marked this conversation as resolved.
Comment thread
NumaryBot marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [major] --aws-role-arn flag is registered but silently ignored in the Postgres IAM connection path
reported by codex, gfyrag, NumaryBot

The worker command registers --aws-role-arn (alongside --postgres-aws-enable-iam), but connect.ConnectionOptionsFromFlags loads AWS config via iam.LoadOptionFromFlags, which reads region/static keys/session/profile but not role ARN. A worker started with --postgres-aws-enable-iam --aws-role-arn will not assume the requested role; it silently falls back to the default/static credential chain. MarkHidden (if used) only hides the flag from help output — Cobra still parses and accepts it — so the no-op behaviour is invisible to operators.

Suggestion: Either (a) make the connection path consume AWSRoleArnFlag by wrapping the loaded credentials with stscreds.NewAssumeRoleProvider + aws.NewCredentialsCache when the flag is non-empty, (b) do not register --aws-role-arn on the worker command at all until the underlying library supports it and document the limitation clearly, or (c) add an explicit pre-run validation that returns a clear error when --aws-role-arn is set alongside --postgres-aws-enable-iam so operators are not silently misled.

// Hide --aws-role-arn until go-libs consumes it; exposing a no-op flag
// would silently mislead operators configuring RDS IAM role assumption.
_ = cmd.Flags().MarkHidden(iam.AWSRoleArnFlag)

return cmd
}
Expand Down
105 changes: 105 additions & 0 deletions cmd/worker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package cmd

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/formancehq/go-libs/v5/pkg/cloud/aws/iam"
"github.com/formancehq/go-libs/v5/pkg/storage/bun/connect"
)

// TestWorkerCommandHasAWSIAMFlags verifies that the worker command registers
// all required AWS IAM flags, so that users running the standalone worker binary
// (e.g. on AWS ECS Fargate) can configure RDS IAM auth for the primary store
// via CLI flags, not just via environment variables.
func TestWorkerCommandHasAWSIAMFlags(t *testing.T) {
t.Parallel()

cmd := NewWorkerCommand()
flags := cmd.Flags()

// These flags come from connect.AddFlags and enable IAM on the primary store
t.Run("postgres-aws-enable-iam flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(connect.PostgresAWSEnableIAMFlag)
require.NotNil(t, f, "--postgres-aws-enable-iam flag must be registered on the worker command")
assert.Equal(t, "false", f.DefValue)
})

// These flags come from iam.AddFlags (added to fix issue #1556).
t.Run("aws-region flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSRegionFlag)
require.NotNil(t, f, "--aws-region flag must be registered on the worker command")
})

t.Run("aws-access-key-id flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSAccessKeyIDFlag)
require.NotNil(t, f, "--aws-access-key-id flag must be registered on the worker command")
})

t.Run("aws-secret-access-key flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSSecretAccessKeyFlag)
require.NotNil(t, f, "--aws-secret-access-key flag must be registered on the worker command")
})

t.Run("aws-session-token flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSSessionTokenFlag)
require.NotNil(t, f, "--aws-session-token flag must be registered on the worker command")
})

t.Run("aws-profile flag is registered", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSProfileFlag)
require.NotNil(t, f, "--aws-profile flag must be registered on the worker command")
})

// --aws-role-arn is registered by iam.AddFlags but is hidden on the worker
// command until iam.LoadOptionFromFlags in go-libs consumes it. Exposing a
// no-op flag would silently mislead operators configuring RDS IAM role
// assumption. Full support is tracked for a future go-libs update.
t.Run("aws-role-arn flag is registered but hidden", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(iam.AWSRoleArnFlag)
require.NotNil(t, f, "--aws-role-arn flag must be registered on the worker command")
assert.True(t, f.Hidden, "--aws-role-arn must be hidden until go-libs consumes it")
})
}

// TestServeCommandHasAWSIAMFlags verifies the serve command also exposes
// the same AWS IAM flags for parity (regression guard).
func TestServeCommandHasAWSIAMFlags(t *testing.T) {
t.Parallel()

cmd := NewServeCommand()
flags := cmd.Flags()

// Verify the IAM-enable flag defaults to false on serve as well
f := flags.Lookup(connect.PostgresAWSEnableIAMFlag)
require.NotNil(t, f, "--postgres-aws-enable-iam flag must be registered on the serve command")
assert.Equal(t, "false", f.DefValue)

iamFlags := []string{
connect.PostgresAWSEnableIAMFlag,
iam.AWSRegionFlag,
iam.AWSAccessKeyIDFlag,
iam.AWSSecretAccessKeyFlag,
iam.AWSSessionTokenFlag,
iam.AWSProfileFlag,
iam.AWSRoleArnFlag,
}

for _, flagName := range iamFlags {
t.Run(flagName+" is registered on serve", func(t *testing.T) {
t.Parallel()
f := flags.Lookup(flagName)
require.NotNil(t, f, "--%s flag must be registered on the serve command", flagName)
})
}

}