Conversation
There was a problem hiding this comment.
Pull request overview
Adds optional AWS STS AssumeRole support to the lambda-invoker module so Lambda invocations can use short-lived assumed-role credentials configured via lambda-invoker settings.
Changes:
- Add AWS SDK STS dependency to the parent and
lambda-invokermodules. - Extend
lambda-invokerconfiguration (YAML +LambdaInvokerConfig) with STS-related settings. - Update
LambdaFunctionHandlerto assume a role via STS, cache credentials, and refresh when nearing expiration.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if(config.isStsEnabled()) { | ||
| if(logger.isInfoEnabled()) logger.info("STS AssumeRole is enabled. Obtaining temporary credentials for role: {}", config.getRoleArn()); | ||
| Credentials credentials = assumeRole(config); | ||
| stsCredentials = credentials; | ||
| credentialsExpiration = credentials.expiration(); | ||
| AwsSessionCredentials sessionCredentials = AwsSessionCredentials.create( | ||
| credentials.accessKeyId(), | ||
| credentials.secretAccessKey(), | ||
| credentials.sessionToken() | ||
| ); | ||
| builder.credentialsProvider(StaticCredentialsProvider.create(sessionCredentials)); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Call STS AssumeRole to obtain temporary credentials. | ||
| * @param config the lambda invoker config containing roleArn, roleSessionName, and durationSeconds | ||
| * @return the STS Credentials object containing temporary access key, secret key, session token, and expiration | ||
| */ | ||
| private Credentials assumeRole(LambdaInvokerConfig config) { | ||
| try (StsClient stsClient = StsClient.builder() | ||
| .region(Region.of(config.getRegion())) | ||
| .build()) { | ||
| AssumeRoleRequest assumeRoleRequest = AssumeRoleRequest.builder() | ||
| .roleArn(config.getRoleArn()) | ||
| .roleSessionName(config.getRoleSessionName()) | ||
| .durationSeconds(config.getDurationSeconds()) | ||
| .build(); |
There was a problem hiding this comment.
When stsEnabled is true, roleArn and durationSeconds are not validated before building the AssumeRoleRequest. With the current defaults (roleArn empty, durationSeconds potentially 0 if omitted), this will fail at runtime with an SDK exception; add explicit validation and a clear error message (and enforce the 900–43200 durationSeconds bounds) before calling STS.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| // If STS is enabled, obtain temporary credentials via AssumeRole | ||
| if(config.isStsEnabled()) { | ||
| if(logger.isInfoEnabled()) logger.info("STS AssumeRole is enabled. Obtaining temporary credentials for role: {}", config.getRoleArn()); | ||
| Credentials credentials = assumeRole(config); | ||
| stsCredentials = credentials; | ||
| credentialsExpiration = credentials.expiration(); | ||
| AwsSessionCredentials sessionCredentials = AwsSessionCredentials.create( | ||
| credentials.accessKeyId(), | ||
| credentials.secretAccessKey(), | ||
| credentials.sessionToken() | ||
| ); | ||
| builder.credentialsProvider(StaticCredentialsProvider.create(sessionCredentials)); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Call STS AssumeRole to obtain temporary credentials. | ||
| * @param config the lambda invoker config containing roleArn, roleSessionName, and durationSeconds | ||
| * @return the STS Credentials object containing temporary access key, secret key, session token, and expiration | ||
| */ | ||
| private Credentials assumeRole(LambdaInvokerConfig config) { | ||
| try (StsClient stsClient = StsClient.builder() | ||
| .region(Region.of(config.getRegion())) | ||
| .build()) { | ||
| AssumeRoleRequest assumeRoleRequest = AssumeRoleRequest.builder() | ||
| .roleArn(config.getRoleArn()) | ||
| .roleSessionName(config.getRoleSessionName()) | ||
| .durationSeconds(config.getDurationSeconds()) | ||
| .build(); | ||
| AssumeRoleResponse response = stsClient.assumeRole(assumeRoleRequest); | ||
| if(logger.isInfoEnabled()) { | ||
| logger.info("STS AssumeRole succeeded. Credentials expire at: {}", response.credentials().expiration()); | ||
| } | ||
| return response.credentials(); | ||
| } catch (Exception e) { | ||
| logger.error("Failed to assume role via STS: {}", config.getRoleArn(), e); | ||
| throw new RuntimeException("Failed to assume role via STS", e); | ||
| } | ||
| } |
There was a problem hiding this comment.
New STS behavior (assume-role request construction, validation, and refresh behavior) isn’t covered by tests. Since this module already has JUnit tests, add unit tests around the STS-enabled code path (e.g., validation errors for missing roleArn/invalid durationSeconds, and ensuring the chosen credentials provider is wired into the Lambda client).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| @BooleanField( | ||
| configFieldName = STS_ENABLED, | ||
| externalizedKeyName = STS_ENABLED, | ||
| description = "Enable STS AssumeRole to obtain temporary credentials for Lambda invocation instead of using the\n" + | ||
| "permanent IAM credentials. When set to true, the handler will call STS AssumeRole with the configured\n" + | ||
| "roleArn, roleSessionName, and durationSeconds to get short-lived credentials. This is the recommended\n" + | ||
| "approach for production environments to follow the principle of least privilege.\n", | ||
| defaultValue = "false" | ||
| ) | ||
| private boolean stsEnabled; | ||
|
|
||
| @StringField( | ||
| configFieldName = ROLE_ARN, | ||
| externalizedKeyName = ROLE_ARN, | ||
| description = "The ARN of the IAM role to assume when stsEnabled is true. For example,\n" + | ||
| "arn:aws:iam::123456789012:role/LambdaInvokerRole\n" | ||
| ) | ||
| private String roleArn; | ||
|
|
||
| @StringField( | ||
| configFieldName = ROLE_SESSION_NAME, | ||
| externalizedKeyName = ROLE_SESSION_NAME, | ||
| description = "The session name to use when assuming the role. This is used for auditing and tracking in CloudTrail.", | ||
| defaultValue = "light-gateway-session" | ||
| ) | ||
| private String roleSessionName; | ||
|
|
||
| @IntegerField( | ||
| configFieldName = DURATION_SECONDS, | ||
| externalizedKeyName = DURATION_SECONDS, | ||
| description = "The duration in seconds for the temporary credentials. Default is 3600 (1 hour). Minimum is 900 (15 min)\n" + | ||
| "and maximum is the role's max session duration setting (up to 43200 / 12 hours).\n", | ||
| defaultValue = "3600" | ||
| ) | ||
| private int durationSeconds; |
There was a problem hiding this comment.
This class is annotated for schema generation, but the checked-in lambda-invoker-schema.json needs to be regenerated/updated to include the new STS fields (stsEnabled/roleArn/roleSessionName/durationSeconds); otherwise config documentation and tooling will be out of sync.
| // If STS is enabled, obtain temporary credentials via AssumeRole | ||
| if(config.isStsEnabled()) { | ||
| if(logger.isInfoEnabled()) logger.info("STS AssumeRole is enabled. Obtaining temporary credentials for role: {}", config.getRoleArn()); | ||
| Credentials credentials = assumeRole(config); | ||
| stsCredentials = credentials; | ||
| credentialsExpiration = credentials.expiration(); | ||
| AwsSessionCredentials sessionCredentials = AwsSessionCredentials.create( | ||
| credentials.accessKeyId(), | ||
| credentials.secretAccessKey(), | ||
| credentials.sessionToken() | ||
| ); | ||
| builder.credentialsProvider(StaticCredentialsProvider.create(sessionCredentials)); | ||
| } |
There was a problem hiding this comment.
The STS integration currently uses StaticCredentialsProvider with a one-time AssumeRole, then recreates the LambdaAsyncClient to refresh credentials. This is fragile and unnecessary with AWS SDK v2: prefer using StsAssumeRoleCredentialsProvider (or StsAssumeRoleCredentialsProvider.builder().refreshRequest(...)) so credentials are refreshed automatically without rebuilding/closing the shared client.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| // STS credential cache fields | ||
| private Credentials stsCredentials; | ||
| private Instant credentialsExpiration; |
There was a problem hiding this comment.
stsCredentials is assigned but never read anywhere. Remove it, or use it (e.g., for debugging/metrics) to avoid dead state that can confuse future maintainers.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
…nctionHandler.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…nctionHandler.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Initial plan * Add validation for roleArn and durationSeconds before STS AssumeRole Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com>
* Initial plan * Add unit tests for STS code path in LambdaFunctionHandler Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com> Co-authored-by: Steve Hu <stevehu@gmail.com>
…resh (#158) * Initial plan * Replace StaticCredentialsProvider with StsAssumeRoleCredentialsProvider for automatic credential refresh Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stevehu <2042337+stevehu@users.noreply.github.com> Co-authored-by: Steve Hu <stevehu@gmail.com>

No description provided.