Skip to content

Conversation

@amirbenun
Copy link
Contributor

Refactors the GCP Cloud Connectors authentication flow from direct OIDC/JWT-based token exchange to AWS Workload Identity Federation. The new approach first assumes an AWS role using the JWT via AssumeRoleWithWebIdentity, then uses the resulting AWS credentials for GCP Workload Identity Federation token exchange before impersonating the target service account in the customer's GCP project. This change aligns the GCP Cloud Connectors authentication with the AWS-based trust pattern and includes updates to the configuration to properly pass CloudConnectorsConfig with the JWT file path to GCP auth providers.

Copilot AI review requested due to automatic review settings January 13, 2026 10:20
@amirbenun amirbenun requested a review from a team as a code owner January 13, 2026 10:20
@mergify
Copy link

mergify bot commented Jan 13, 2026

This pull request does not have a backport label. Could you fix it @amirbenun? 🙏
To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-v./d./d./d is the label to automatically backport to the 8./d branch. /d is the digit
  • backport-active-all is the label that automatically backports to all active branches.
  • backport-active-8 is the label that automatically backports to all active minor branches for the 8 major.
  • backport-active-9 is the label that automatically backports to all active minor branches for the 9 major.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the GCP Cloud Connectors authentication flow from direct OIDC/JWT-based token exchange to AWS Workload Identity Federation. The new approach first assumes an AWS role using the JWT via AssumeRoleWithWebIdentity, then uses the resulting AWS credentials for GCP Workload Identity Federation token exchange before impersonating the target service account in the customer's GCP project.

Changes:

  • Modified authentication flow to use AWS role assumption as an intermediary step before GCP credential exchange
  • Updated CloudConnectorsConfig to include JWTFilePath and properly pass it to GCP auth providers
  • Added awsCredentialsSupplier type to handle AWS role assumption with web identity

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/config/config.go Added JWTFilePath field to CloudConnectorsConfig and initialization logic for GCP Cloud Connectors when service_account_email is set
internal/resources/providers/gcplib/auth/auth_provider.go Implemented new AWS-based authentication flow with awsCredentialsSupplier type that assumes AWS role before GCP token exchange
internal/resources/providers/gcplib/auth/credentials.go Updated FindCloudConnectorsCredentials signature to accept CloudConnectorsConfig parameter and updated log message
internal/resources/providers/gcplib/auth/credentials_mock.go Updated mock function signatures to match new CloudConnectorsConfig parameter
internal/resources/providers/gcplib/auth/credentials_test.go Updated test mock calls to include new ccConfig parameter

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if ccConfig.ResourceID == "" {
return nil, fmt.Errorf("cloud connectors config ResourceID is required")
}

Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Missing validation for required parameters 'audience' and 'serviceAccountEmail'. These parameters are used directly in the configuration without checking if they are empty strings. If either is empty, it could lead to runtime errors when constructing the GCP IAM credentials URL or configuring the external account. Consider adding validation checks similar to the other required fields.

Suggested change
if audience == "" {
return nil, fmt.Errorf("cloud connectors audience is required")
}
if serviceAccountEmail == "" {
return nil, fmt.Errorf("cloud connectors serviceAccountEmail is required")
}

Copilot uses AI. Check for mistakes.
Comment on lines 100 to 142
// awsCredentialsSupplier implements externalaccount.AwsSecurityCredentialsSupplier
// It assumes an AWS role using a JWT token and provides the resulting credentials to GCP.
type awsCredentialsSupplier struct {
jwtFilePath string
globalRoleARN string
roleSessionID string
region string
}

// AwsRegion returns the AWS region for the credentials.
func (s *awsCredentialsSupplier) AwsRegion(ctx context.Context, options externalaccount.SupplierOptions) (string, error) {
return s.region, nil
}

// AwsSecurityCredentials assumes the AWS role using the JWT and returns the temporary credentials.
func (s *awsCredentialsSupplier) AwsSecurityCredentials(ctx context.Context, options externalaccount.SupplierOptions) (*externalaccount.AwsSecurityCredentials, error) {
// Create STS client without credentials (we're using web identity)
stsClient := sts.New(sts.Options{
Region: s.region,
})

// Use the AWS SDK's built-in web identity provider
webIdentityProvider := stscreds.NewWebIdentityRoleProvider(
stsClient,
s.globalRoleARN,
stscreds.IdentityTokenFile(s.jwtFilePath),
func(o *stscreds.WebIdentityRoleOptions) {
o.RoleSessionName = s.roleSessionID
},
)

// Retrieve credentials using the web identity provider
creds, err := webIdentityProvider.Retrieve(ctx)
if err != nil {
return nil, fmt.Errorf("failed to assume role %s with web identity: %w", s.globalRoleARN, err)
}

return &externalaccount.AwsSecurityCredentials{
AccessKeyID: creds.AccessKeyID,
SecretAccessKey: creds.SecretAccessKey,
SessionToken: creds.SessionToken,
}, nil
}
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

The new awsCredentialsSupplier type and its methods (AwsRegion and AwsSecurityCredentials) lack unit test coverage. While the FindCloudConnectorsCredentials method is tested through mocks in credentials_test.go, the actual AWS role assumption logic and credential retrieval flow are not directly tested. Consider adding unit tests to verify the behavior of the awsCredentialsSupplier, including error handling when JWT file is missing, invalid, or when AWS role assumption fails.

Copilot uses AI. Check for mistakes.
}

// AwsSecurityCredentials assumes the AWS role using the JWT and returns the temporary credentials.
func (s *awsCredentialsSupplier) AwsSecurityCredentials(ctx context.Context, _ externalaccount.SupplierOptions) (*externalaccount.AwsSecurityCredentials, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks fairly similar to code that we have in:

I think that this can be shared and not duplicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is not a lot of shared code but I took it out into a new file web_identity.go

@jeniawhite jeniawhite self-requested a review January 21, 2026 15:15
@amirbenun amirbenun merged commit ffc6d19 into elastic:main Jan 21, 2026
9 checks passed
@amirbenun amirbenun deleted the cc-gcp-aws-trust branch January 21, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants