Skip to content

Commit ffe9863

Browse files
authored
fix(apigatewayv2): incorrect arn function causing unwanted behavior (#33100)
fixes: #33218 ### Reason for this change In websocket APIs in `aws-apigatewayv2`, the function arnForExecuteApi has essentially the same exact functionality as a REST API, which is not appropriate for Websockets which are fundamentally different. The way I found this issue was I used arnForExecuteApi to put the arn of the api into an IAM Role. The reason for this was because I was trying to use an IAM authorizer, which from a React standpoint meant signing iam credentials from my Cognito id pool using Amplify lib. When doing this I used arnForExecuteApi from CDK to write the policy, I did not include any arguments, just the default. The issue was that this was not working. I spent time diving deep on the issue in case it was the method in which I was signing the credentials, since I was not too familiar with this process. I also got the assistance of a Cloud Support Engineer from AWS to try and identify the problem. Shout-out Mike Sacks. The problem ended up being that that the resource policy was not correct. The policy that was generated by the function arnForExecuteApi was ``` "Resource": "arn:aws:execute-api:us-east-1:acc:apiId/*/*/*", ``` This is because the function itself has 3 values, stage, method and path, so when all are left in default states, this indicates `all` or `*`. So when adding each value at default you get `/*/*/*`, 3 x /*. This is an issue because Websocket arns are not structured like this, and as it turns out **iam prevents access if you have too many wild cards than applicable**. This means the reason for getting access denied was not because of my signed url, but because having 1 extra /* means that you no longer have permissions. Websocket arns are structured like this ``` arn:aws:execute-api:us-east-1:acc:apiId/*/$connect ``` In this example, * is the stage (this is what it shows on the console) and $connect is the `route`. You can add as many routes as you want, but the main ones by default are $connect, $disconnect and $default for no matching route. So if I want to grant an IAM role to have access to all routes and all stages, I would use this: ``` "Resource": "arn:aws:execute-api:us-east-1:acc:apiId/*/*", ``` Note 2 x /* instead of 3. Simply changing this by hand (deleting 2 characters) was enough to get the websocket to begin connecting via my signed url. ### Description of changes A re-write of the function for creating the arn. This is implemented as arnForExecuteApiV2, the original function has been changes to include the deprecated tag. This is to avoid making a breaking change since the new function only has 2 args and the original had 3. ```ts /** * Get the "execute-api" ARN. * * @default - The default behavior applies when no specific route, or stage is provided. * In this case, the ARN will cover all routes, and all stages of this API. * Specifically, if 'route' is not specified, it defaults to '*', representing all routes. * If 'stage' is not specified, it also defaults to '*', representing all stages. */ public arnForExecuteApiV2(route?: string, stage?: string): string { return Stack.of(this).formatArn({ service: 'execute-api', resource: this.apiId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, resourceName: `${stage ?? '*'}/${route ?? '*'}`, }); } ``` I removed "Method" and "Path" entirely since these are not even appropriate to use as terms for websockets. I used Route instead. ### Description of how you validated changes Updated Tests, there were 4 tests before: * get arnForExecuteApi * is now using route, and intentionally uses a route with no `$` to check that the `$` is being added correctly. * get arnForExecuteApi with default values * is now using route * get arnForExecuteApi with ANY method (removed) * doesn't make any sense here because "ANY" is not a term used with websockets, and method does not exist. Thus, removed this test * throws when call arnForExecuteApi method with specifing a string that does not start with / for the path argument. (removed) * This test is checking for a specific format for path, which is not needed since the route can be anything. Also path does not exist. This leaves 2 total tests now. Added a new integ function, `integ.api-grant-invoke.ts` and used --update-on-failed with my personal account to bootstrap new snapshots to match. For this test I included an iam role and 2 arns, one with default settings and one with `.arnForExecuteApi('connect', 'prod')` Intentionally left off the `$` to check that it's being added. All tests and integ are passing. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent d32baf6 commit ffe9863

File tree

12 files changed

+702
-7
lines changed

12 files changed

+702
-7
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.assets.json

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
{
2+
"Resources": {
3+
"webocketapiD5DB5DB0": {
4+
"Type": "AWS::ApiGatewayV2::Api",
5+
"Properties": {
6+
"Name": "webocket-api",
7+
"ProtocolType": "WEBSOCKET",
8+
"RouteSelectionExpression": "$request.body.action"
9+
}
10+
},
11+
"websocketstageA39DAC37": {
12+
"Type": "AWS::ApiGatewayV2::Stage",
13+
"Properties": {
14+
"ApiId": {
15+
"Ref": "webocketapiD5DB5DB0"
16+
},
17+
"StageName": "prod"
18+
}
19+
},
20+
"testiamrole05EFDD08": {
21+
"Type": "AWS::IAM::Role",
22+
"Properties": {
23+
"AssumeRolePolicyDocument": {
24+
"Statement": [
25+
{
26+
"Action": "sts:AssumeRole",
27+
"Effect": "Allow",
28+
"Principal": {
29+
"Service": "lambda.amazonaws.com"
30+
}
31+
}
32+
],
33+
"Version": "2012-10-17"
34+
},
35+
"Policies": [
36+
{
37+
"PolicyDocument": {
38+
"Statement": [
39+
{
40+
"Action": "execute-api:Invoke",
41+
"Effect": "Allow",
42+
"Resource": [
43+
{
44+
"Fn::Join": [
45+
"",
46+
[
47+
"arn:",
48+
{
49+
"Ref": "AWS::Partition"
50+
},
51+
":execute-api:",
52+
{
53+
"Ref": "AWS::Region"
54+
},
55+
":",
56+
{
57+
"Ref": "AWS::AccountId"
58+
},
59+
":",
60+
{
61+
"Ref": "webocketapiD5DB5DB0"
62+
},
63+
"/*/*"
64+
]
65+
]
66+
},
67+
{
68+
"Fn::Join": [
69+
"",
70+
[
71+
"arn:",
72+
{
73+
"Ref": "AWS::Partition"
74+
},
75+
":execute-api:",
76+
{
77+
"Ref": "AWS::Region"
78+
},
79+
":",
80+
{
81+
"Ref": "AWS::AccountId"
82+
},
83+
":",
84+
{
85+
"Ref": "webocketapiD5DB5DB0"
86+
},
87+
"/prod/connect"
88+
]
89+
]
90+
}
91+
]
92+
}
93+
],
94+
"Version": "2012-10-17"
95+
},
96+
"PolicyName": "webSocketAccess"
97+
}
98+
]
99+
}
100+
}
101+
},
102+
"Parameters": {
103+
"BootstrapVersion": {
104+
"Type": "AWS::SSM::Parameter::Value<String>",
105+
"Default": "/cdk-bootstrap/hnb659fds/version",
106+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
107+
}
108+
},
109+
"Rules": {
110+
"CheckBootstrapVersion": {
111+
"Assertions": [
112+
{
113+
"Assert": {
114+
"Fn::Not": [
115+
{
116+
"Fn::Contains": [
117+
[
118+
"1",
119+
"2",
120+
"3",
121+
"4",
122+
"5"
123+
],
124+
{
125+
"Ref": "BootstrapVersion"
126+
}
127+
]
128+
}
129+
]
130+
},
131+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
132+
}
133+
]
134+
}
135+
}
136+
}

packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/cdk.out

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/integ.json

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/manifest.json

+125
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)