Skip to content

Feature request — expose SourceIdentity on ProviderConfig.spec.assumeRoleChain #2314

Description

@rhysxevans

What problem are you facing?

We need to be able to attribute changes to the requesting user, in cloudtrail, when resources are created by crossplane.

How could Crossplane help solve your problem?

The Below is AI generated, based of the ask I have prompted for. So take it with a pinch of salt

Summary

Add an optional sourceIdentity field to AssumeRoleOptions on ProviderConfig.spec.assumeRoleChain[*] and pass it through to stscreds.AssumeRoleOptions.SourceIdentity in SetAssumeRoleOptions, so that operators can set the STS SourceIdentity field on per-ProviderConfig AssumeRole calls. SourceIdentity is the dedicated AWS surface for identifying the principal that initiated a session and is the canonical answer to "who started this?" in CloudTrail and in aws:SourceIdentity IAM policy conditions.

Use case

Platform teams running Crossplane in cross-account / chained-role configurations need the originating principal — captured upstream by the platform (typically as a Claim annotation, then propagated by a Composition into the ProviderConfig it emits per request) — to land in the dedicated userIdentity.sessionContext.sourceIdentity CloudTrail field on every AWS call the provider makes for that request. Today AssumeRoleOptions exposes ExternalID, Tags, and TransitiveTagKeys but not SourceIdentity, so the field is unreachable from ProviderConfig without forking the provider. Audit pipelines that key on sourceIdentity, and IAM policies that use the aws:SourceIdentity condition key, cannot be satisfied without this.

Current behaviour

Verified on master (crossplane-contrib/provider-aws):

  • apis/v1beta1/providerconfig_types.goAssumeRoleOptions exposes RoleARN, ExternalID, Tags, TransitiveTagKeys. No SourceIdentity field.
  • pkg/utils/connect/aws/config.goSetAssumeRoleOptions assigns ExternalID, Tags, TransitiveTagKeys onto stscreds.AssumeRoleOptions. Never assigns opt.SourceIdentity.

stscreds.AssumeRoleOptions in aws-sdk-go-v2/credentials/stscreds already exposes SourceIdentity *string, so no SDK upgrade is required.

Proposed change

API

Add an optional sourceIdentity field to AssumeRoleOptions:

apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: example
spec:
  credentials:
    source: IRSA
  assumeRoleChain:
    - roleARN: arn:aws:iam::111111111111:role/per-claim-role
      sourceIdentity: alice-at-example-com
      tags:
        - key: originating-principal
          value: alice@example.com
      transitiveTagKeys:
        - originating-principal

Go type

In apis/v1beta1/providerconfig_types.go:

type AssumeRoleOptions struct {
    RoleARN           *string  `json:"roleARN,omitempty"`
    ExternalID        *string  `json:"externalID,omitempty"`
    // SourceIdentity is the source identity to set on the assumed session.
    // It appears in CloudTrail as userIdentity.sessionContext.sourceIdentity and
    // is usable as the aws:SourceIdentity IAM condition key. Must satisfy STS's
    // [A-Za-z0-9+=,.@_-]{2,64} character / length constraints.
    // +optional
    SourceIdentity    *string  `json:"sourceIdentity,omitempty"`
    Tags              []Tag    `json:"tags,omitempty"`
    TransitiveTagKeys []string `json:"transitiveTagKeys,omitempty"`
}

Wiring

In pkg/utils/connect/aws/config.go, SetAssumeRoleOptions:

func SetAssumeRoleOptions(aro v1beta1.AssumeRoleOptions) func(*stscreds.AssumeRoleOptions) {
    return func(opt *stscreds.AssumeRoleOptions) {
        opt.ExternalID     = aro.ExternalID
        opt.SourceIdentity = aro.SourceIdentity   // new line
        for _, t := range aro.Tags { /* unchanged */ }
        opt.TransitiveTagKeys = append(opt.TransitiveTagKeys, aro.TransitiveTagKeys...)
    }
}

IAM trust-policy note (docs only)

sts:SetSourceIdentity must be permitted on both the caller's identity policy and the assumed role's trust policy for the field to appear on the resulting session. This is an operator concern, not a provider concern, but is worth a sentence in the provider docs adjacent to the new field so the failure mode (silent absence of sourceIdentity in CloudTrail when only one side allows the action) is discoverable.

Why this shape

  • Minimal — one optional field, one assignment, additive, backwards-compatible. Unset behaviour is unchanged.
  • Symmetric — ExternalID / Tags / TransitiveTagKeys already establish the "pass per-PC options through to the SDK call" precedent on this exact struct; SourceIdentity is the next field in the same shape.
  • Per-ProviderConfig is the right granularity — SourceIdentity is bound to the session, and the session is created per ProviderConfig chain element. Templating the value per request is achievable today by emitting a per-Claim ProviderConfig from a Composition; that remains an operator concern, not a provider one.

Alternatives considered

  • Fork or wrap the provider. Possible but pushes a one-line change into every consumer's build / supply-chain process; not justified for a field this small and broadly useful.
  • Carry the originating principal via session tags only. Already supported (Tags + TransitiveTagKeys) and is recommended in parallel with SourceIdentity. But session tags do not populate userIdentity.sessionContext.sourceIdentity, and aws:SourceIdentity is a distinct IAM condition key from aws:PrincipalTag/*; the two are complementary, not substitutes.

Out of scope for this request

  • AssumeRoleWithWebIdentity (SetWebIdentityRoleOptions) currently sets only RoleSessionName. The same SourceIdentity gap exists on that path and would matter for any caller using credentials.webIdentity directly. Out of scope here to keep this review focused; intended to be filed as a separate, narrower request if this primary AssumeRoleChain ask lands.
  • Per-managed-resource SourceIdentity (e.g. setting it from MR annotations directly, bypassing the ProviderConfig). Out of scope; the per-Claim ProviderConfig pattern already covers this need without an MR API change.
  • Validation of the STS character / length constraints ([A-Za-z0-9+=,.@_-]{2,64}). The AWS SDK / API returns a clear error if violated; client-side validation can be added as a follow-up if maintainers prefer.
  • Normalisation of long or non-conforming principal strings. Operator concern, upstream of the ProviderConfig.

PR shape

  • apis/v1beta1/providerconfig_types.go — add the SourceIdentity *string field on AssumeRoleOptions.
  • pkg/utils/connect/aws/config.go — add opt.SourceIdentity = aro.SourceIdentity in SetAssumeRoleOptions.
  • Regenerate CRD manifests under package/crds/ (via the repo's existing generation target).
  • One sentence in the AssumeRole section of the provider docs covering the IAM trust-policy note above.
  • Unit test in pkg/utils/connect/aws/config_test.go asserting the pass-through.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions