-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(agentcore): agentcore gateway L2 construct #35771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
packages/@aws-cdk/aws-bedrock-agentcore-alpha/agentcore/gateway/validation-helpers.ts
Fixed
Show fixed
Hide fixed
mazyu36
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I received a review request in another place, so I've reviewed part of it.
I left one nit comment. I'll review the rest when I have time.
| /** | ||
| * The Cognito User Pool to use for authentication | ||
| */ | ||
| readonly userPool: UserPool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be IUserPool (Interface).
kumvprat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added inline comments, overall adding more test cases in integration test can increase visibility on how the gateway and target combinations can be used.
| public readonly gatewayId = attrs.gatewayId; | ||
| public readonly name = attrs.gatewayName; | ||
| public readonly description = undefined; | ||
| public readonly protocolConfiguration: IGatewayProtocolConfig; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are assigning undefined values and for protocolConfiguration and authorizerConfig we are just type casting properties but not assigning any values, do we need to do this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These properties are already typecast in base class hence in child we are initializing all the optional fields. This is as per a previous code review comment for runtime construct ( avoid duplicate type cast wherever possible). The protocolConfiguration and authorizerConfig are required fields and cannot be assigned to undefined hence they are cast in child class too.
| const gatewayId = resourceParts[1] || 'unknown'; | ||
|
|
||
| // Create a minimal role for the imported gateway | ||
| const role = iam.Role.fromRoleArn(scope, `${id}Role`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the role in the original resource can be a customer provided role, hardcoding it to ManagedPolicy is not correct in a referenced version of Gateway resource.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed , wrong commit. we actually use fromGatewayAttributes to import an existing resource . we don't need fromGatewayArn because it ultimately use fromGatewayAttributes. Removing fromGatewayArn.
| actions: GatewayPerms.ASSUME_ROLE, | ||
| conditions: { | ||
| StringEquals: { | ||
| 'aws:SourceAccount': account, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused here
Why not restrict the permissions here to take in a resource field ? That way we can scope it down to a specific resource inside the source account ?
Or why do we need to scope it down like this we already have limited via a service principal.
Just curious around the ideology behind scoping down the permissions via the Conditions field
On the same lines : Is it possible for gateway in one account to invoke a target in another account. If so will this role create any issues, since we are tying it to a SourceAccount?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trust policy conditions follow the official AWS Bedrock AgentCore Gateway documentation.
The documentation explicitly requires these conditions in the trust policy:
StringEquals: 'aws:SourceAccount' - Ensures only gateways from this account can assume the role
ArnLike: 'aws:SourceArn' - Scopes to specific gateway resources.
I think this is for to prevent the 'confused deputy' security issue where a malicious actor could potentially trick the bedrock-agentcore service into using our role via their gateway in a different account.
Cross account access - No, this will not create any issue.
This condition only check trust policy ( who can assume the role) meaning only gateways from Account A can assume this role.
The cross account permission check (WHAT the role can do once assumed) happen at invoke time , example - //
// Gateway role (in account A) needs permissions to cross-account target
gateway.role.addToPolicy(new iam.PolicyStatement({
actions: ['lambda:InvokeFunction'],
resources: ['arn:aws:lambda:us-east-1:222222222222:function/TargetFunc']
}));
// Lambda (in account B) must allow Account A's gateway role
targetFunction.grantInvoke(
iam.Role.fromRoleArn(this, 'GatewayRole',
'arn:aws:iam::111111111111:role/GatewayRole'
)
);
| * Provisions a Cognito User Pool and configures JWT authentication | ||
| * @internal | ||
| */ | ||
| private createDefaultCognitoAuthorizer(): IGatewayAuthorizerConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this creating an authorizer config can we change the name to createDefaultCognitoAuthorizerConfig or something similar ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
| /** | ||
| * Static method for creating a Cognito authorizer | ||
| */ | ||
| public static usingCognito(props: CognitoAuthorizerProps): IGatewayAuthorizerConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think we need to redirect call from one public static function to another. We can directly use the this one and shift the logic here to ensure consistency in code design
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. Didn't updated the old code. removed fromCognito and directly using usingCognito
| * OAuth credential provider configuration implementation | ||
| * Can be used with OpenAPI targets | ||
| */ | ||
| export class OAuthCredentialProviderConfiguration implements ICredentialProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again if it is a provider itself we should remove configuration from the class name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated ICredentialProvider to ICredentialProviderConfig
| /** | ||
| * Properties for creating a Gateway Target resource | ||
| */ | ||
| export interface GatewayTargetProps extends McpGatewayTargetCommonProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we not using GatewayTargetProps as the base class for each of the target types ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We follow this structure
McpGatewayTargetCommonProps // ← Common base for both generic and specific targets
└──GatewayTargetProps // ← For generic use
└── GatewayTargetLambdaProps // ← For Lambda Target-specific use
GatewayTargetOpenApiProps // ← For open API target-specific use
GatewayTargetSmithyProps //← For Smithy target-specific use
we have convenience methods to create specific target like lambda, openapi and smithy. These specific target have fixed targetconfiguration while GatewayTargetProps can be used to create any target with generic constructor with targetconfiguration.
The McpGatewayTargetCommonProps help us avoiding duplication for common properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just keep : GatewayTargetProps
And provide 3 helper functions inside the above class that allows anyone to create 3 different types of GatewayProps
Basically moving the stuff here : Code link to config classes and that way we can keep GatewayTarget simple and all the abstraction resides on the configuration class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example :
export interface LambdaGatewayTargetProps extends GatewayTargetProps {
...
constructor(
gatewayTargetName: string,
credentialProviderConfigurations: ICredentialProviderConfig,
lambdaFunction: IFunction,
toolSchema: IToolSchema,
description: string
) {
super(
gatewayTargetName,
description,
credentialProviderConfigurations,
targetConfiguration: LambdaTargetConfiguration.create(
lambdaFunction,
toolSchema,
),
});
}
}
Now in GatewayTarget :
export class GatewayTarget extends GatewayTargetBase implements IMcpGatewayTarget {
...
public static forLambda(
scope: Construct,
id: string,
props: LambdaGatewayTargetProps,
): GatewayTarget {
return new GatewayTarget(scope, id, props);
}
}
| * @returns Array of validation error messages (empty if valid) | ||
| * @internal | ||
| */ | ||
| export function validateOpenApiSchema(params: OpenApiSchemaValidationParams): string[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function seems too bloated.
I assume all the validations here are necessary but clubbing them together doesnot really allow for maintainable code.
Can we break into smaller function chunks ? We can encapsulate the overall logic into smaller functions that handle a specific part of validation. (I have not read the whole function details and assumption behind this comment is that all the validation are mutually-exclusive )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes they are mutually exclusive and don't interfere with each other
| }); | ||
|
|
||
| // Create the integration test | ||
| new integ.IntegTest(app, 'GatewayIntegTest', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a cross-service integration we should have assertion logic that check that the gateway is properly tied to the Lambda function and is able to invoke the lambda properly when a user request comes in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
| import * as integ from '@aws-cdk/integ-tests-alpha'; | ||
| import * as agentcore from '../../../agentcore'; | ||
|
|
||
| const app = new cdk.App(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and the integ test above seem to be duplicate : packages/@aws-cdk/aws-bedrock-agentcore-alpha/test/agentcore/gateway/integ.gateway.ts
kumvprat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment for GatewayTargetProps structure
| /** | ||
| * Properties for creating a Gateway Target resource | ||
| */ | ||
| export interface GatewayTargetProps extends McpGatewayTargetCommonProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just keep : GatewayTargetProps
And provide 3 helper functions inside the above class that allows anyone to create 3 different types of GatewayProps
Basically moving the stuff here : Code link to config classes and that way we can keep GatewayTarget simple and all the abstraction resides on the configuration class.
| /** | ||
| * Properties for creating a Gateway Target resource | ||
| */ | ||
| export interface GatewayTargetProps extends McpGatewayTargetCommonProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example :
export interface LambdaGatewayTargetProps extends GatewayTargetProps {
...
constructor(
gatewayTargetName: string,
credentialProviderConfigurations: ICredentialProviderConfig,
lambdaFunction: IFunction,
toolSchema: IToolSchema,
description: string
) {
super(
gatewayTargetName,
description,
credentialProviderConfigurations,
targetConfiguration: LambdaTargetConfiguration.create(
lambdaFunction,
toolSchema,
),
});
}
}
Now in GatewayTarget :
export class GatewayTarget extends GatewayTargetBase implements IMcpGatewayTarget {
...
public static forLambda(
scope: Construct,
id: string,
props: LambdaGatewayTargetProps,
): GatewayTarget {
return new GatewayTarget(scope, id, props);
}
}
Issue # (if applicable)
Related to aws/aws-cdk-rfcs#785
Reason for this change
Adding bedrock agent core gateway and gateway target
Description of changes
Describe any new or updated permissions being added
The gateway creates a role with permission to bedrock agentcore , s3
Description of how you validated changes
Unit tests, integration tests, manual tests
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license