amplify-cli
amplify-cli copied to clipboard
allowing Lambda function IAM read privileges for AppSync also requires create privileges
It appears as though GraphQL/AppSync resource access permissions for Lambda functions were broken at some point so that read permissions no longer work unless create permissions are also added to the function. This violates the Principal of Least Privilege; read permission should be allowed without requiring create permission.
This line of code only generates access to the API resource identifier arn:aws:appsync:region:accountId:apis/GraphQLAPIIdOutput/*
when permissions include create privileges. Note that the nearly-identical resource identifier without the trailing *
is still generated but the version with the trailing star is no longer included without create.
In our Amplify project, we have a custom resolver function (aptly named Resolver), that has permission to read from the GraphQL API, but not permission to create. Running auth update function
and stepping through resource access permissions for that function, re-choosing the existing settings without making any changes whatsoever, results in the following diff:
--- a/src/mobile/amplify/backend/function/Resolver/Resolver-cloudformation-template.json
+++ b/src/mobile/amplify/backend/function/Resolver/Resolver-cloudformation-template.json
@@ -583,33 +583,12 @@
{
"Effect": "Allow",
"Action": [
- "appsync:GraphQL",
"appsync:Get*",
"appsync:List*",
"appsync:Update*",
"appsync:Delete*"
],
"Resource": [
- {
- "Fn::Join": [
- "",
- [
- "arn:aws:appsync:",
- {
- "Ref": "AWS::Region"
- },
- ":",
- {
- "Ref": "AWS::AccountId"
- },
- ":apis/",
- {
- "Ref": "apinameGraphQLAPIIdOutput"
- },
- "/*"
- ]
- ]
- },
{
"Fn::Join": [
"",
Then if we redeploy Resolver and access a custom API endpoint, which in turn calls the GraphQL GetProfile
API operation, we get the following error:
{
"errorMessage" : "GraphQL Errors[GetProfile]: [{\"errorType\":\"UnauthorizedException\",\"message\":\"Permission denied\"}]",
"errorType" : "Error",
"stack" : [
"Error: GraphQL Errors[GetProfile]: [{\"errorType\":\"UnauthorizedException\",\"message\":\"Permission denied\"}]",
" at GraphQLGateway.runQuery (/opt/nodejs/node_modules/@crft/appsync-gateway/dist/gateway.js:40:19)",
" at processTicksAndRejections (internal/process/task_queues.js:95:5)",
" at async query (/var/task/index.js:45:20)",
" at async customEndpoint (/var/task/index.js:111:21)"
]
}
Restoring the original Resolver CloudFormation template and redeploying fixes the resolver so that the call to GetProfile
works again. Stepping through the CLI function update and adding create privileges also fixes the problem, but violates the Principal of Least Privilege.
Hi @gspeicher I think this may be a duplicate of https://github.com/aws-amplify/amplify-cli/issues/10141
Can you confirm that you have the generateGraphQLPermissions
feature flag enabled? After enabling that feature flag, running amplify update function
should generate a correct policy
@gspeicher could you confirm how you made the following change?
Restoring the original Resolver CloudFormation template and redeploying fixes the resolver so that the call to GetProfile works again.
I'm facing similar authentication issues.
@edwardfoyle I agree, #10141 looks like the same underlying issue, although I could argue that this one is the original so that one is the duplicate. ;-) I did try setting the appSync.generateGraphQLPermissions
feature flag in cli.json
but it has no impact whatsoever on the generated code, i.e. git diff
reports no changes after stepping through amplify function update
again. So perhaps as a separate but related issue it would appear that the feature flag is not being correctly ingested from that file, but I would also argue that if you are using the legacy setting, it should continue to behave as before rather than breaking existing projects by stripping the ability to read from the API unless create privileges are also granted.
@Lorenzohidalgo I had a working version of the template in source control, so I reverted to that. You may want to try granting create privileges for the function to access the API resource as a temporary workaround until this issue gets sorted out.
Thanks, @gspeicher, would you be able to share an extract of your cloud formation template where you grant access to the API? (or is it the one referenced in the issue description?)
I've granted all CRUD permissions to my lambda function (since they are needed) and I'm still getting the authentication error.
I've also compared the cloud formation template with a previously created lambda function (that is working correctly) and could not find some relevant differences.
@Lorenzohidalgo that's basically it shown above, except the second resource is cut off at the bottom where you see Fn::Join
in the template. That resource is a duplicate of the first one except the last component of the join ("/*"
) is omitted. So the two resources more succinctly are arn:aws:appsync:region:accountId:apis/GraphQLAPIIdOutput/*
and arn:aws:appsync:region:accountId:apis/GraphQLAPIIdOutput
.
Hey @gspeicher and @Lorenzohidalgo would y'all mind sharing the snippet from your Lambda that's making the call to AppSync?
Hi @josefaidt, I've shared the snipped you are asking for and further details on #10141
Hey @gspeicher and @Lorenzohidalgo please take a look at the sample posted in #10141 to see if this shares some additional insight https://github.com/aws-amplify/amplify-cli/issues/10141#issuecomment-1112435364
@josefaidt, my results are different than what you describe in that comment, and are still unchanged from my original issue submission. I just upgraded to the latest Amplify CLI and stepped through the process to update resource access permissions on the function and remove create
access to the API. This results in the exact same diff that I posted above.
To be clear, the only remaining policy statement related to App Sync is as follows:
{
"Effect": "Allow",
"Action": [
"appsync:Get*",
"appsync:List*",
"appsync:Update*",
"appsync:Delete*"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:appsync:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":apis/",
{
"Ref": "apiartmatcherGraphQLAPIIdOutput"
}
]
]
}
]
},
It seems that there are two things going on here: (1) permissions are incorrect when the generateGraphQLPermissions
feature flag is set to false
and API access does not include create
permissions; and (2) setting generateGraphQLPermissions
to true
has no effect in my project.
The contents of my cli.json
is as follows. The generated policy above is unchanged regardless of the value or presence/absence of the generateGraphQLPermissions
flag.
{
"features": {
"appSync": {
"generateGraphQLPermissions": true,
},
"graphQLTransformer": {
"addMissingOwnerFields": true,
"validateTypeNameReservedWords": true,
"useExperimentalPipelinedTransformer": false,
"enableIterativeGSIUpdates": true
},
"frontend-ios": {
"enableXcodeIntegration": true
},
"auth": {
"enableCaseInsensitivity": true,
"forceAliasAttributes": true,
"useInclusiveTerminology": true,
"breakCircularDependency": true
},
"codegen": {
"useAppSyncModelgenPlugin": true
}
}
}
Hey @gspeicher :wave: can you walk me through the process of updating your function resource with the appropriate permissions? I'm seeing:
- with
generategraphqlpermissions
set tofalse
, runamplify update function
- select function and modify the resource access permissions
- observe we are prompted for API CRUD operations
> amplify update function ? Select which capability you want to update: Lambda function (serverless function) ? Select the Lambda function you want to update listusers General information - Name: listusers - Runtime: nodejs Resource access permission - 9966 (Mutation) Scheduled recurring invocation - Not configured Lambda layers - Not configured Environment variables: - Not configured Secrets configuration - Not configured ? Which setting do you want to update? Resource access permissions ? Select the categories you want this function to have access to. api ? Api has 2 resources in this project. Select the one you would like your Lambda to access 9966 ? Select the operations you want to permit on 9966 (Press <space> to select, <a> to toggle all, <i> to invert selection) ◯ create ◯ read ◯ update ❯◯ delete
- ctrl+c/cancel the workflow
- set
generategraphqlpermissions
totrue
- run
amplify update function
, select function and modify the resource access permissions - observe we are prompted for GraphQL operations
> amplify update function ? Select which capability you want to update: Lambda function (serverless function) ? Select the Lambda function you want to update listusers General information - Name: listusers - Runtime: nodejs Resource access permission - 9966 (Mutation) Scheduled recurring invocation - Not configured Lambda layers - Not configured Environment variables: - Not configured Secrets configuration - Not configured ? Which setting do you want to update? Resource access permissions ? Select the categories you want this function to have access to. api ? Api has 2 resources in this project. Select the one you would like your Lambda to access 9966 ? Select the operations you want to permit on 9966 (Press <space> to select, <a> to toggle all, <i> to invert selection) ◯ Query ❯◉ Mutation ◯ Subscription
After enabling the feature flag we are able to step through the function's update which should modify the function's CloudFormation template to use the appsync:GraphQL
permissions:
"AmplifyResourcesPolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "amplify-lambda-execution-policy",
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"appsync:GraphQL"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:appsync:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":apis/",
{
"Ref": "api9966GraphQLAPIIdOutput"
},
"/types/Mutation/*"
]
]
}
]
}
]
}
}
}
@josefaidt, when I run amplify update function
it makes no difference whether generategraphqlpermissions
is set to true or false (or omitted) in cli.json
. In the last step of your terminal output, it prompts me for create/read/update/delete rather than Query/Mutation/Subscription either way.
Hey @gspeicher thank you for clarifying! Are you in our Discord by chance? I'd like to hop on a quick call to take a further look at the issue if that's okay
@josefaidt, I just joined Discord with username gspeicher
Hey @gspeicher please join our community server at https://discord.gg/invite/amplify and from we can set up a DM
Did that too. Is there something else I need to do before you can find me there?
@gspeicher I found you! reached out 🙂
Hey @gspeicher thanks again for hopping on a call with me! It was really helpful seeing the issue first-hand, and from our call we found a few issues:
-
generategraphqlpermissions
does in fact not have an effect on the prompts inamplify update function
-
cli.json
was several versions behind what was found in#current-cloud-backend
- several feature flags did not match what we had locally, namely
generategraphqlpermissions
anduseappsyncmodelgenplugin
, which was assumed to be the culprit - after updating both copies of
cli.json
and reuploading the current-cloud-backend zip, we are still experiencing issues with updating function permissions to generateappsync:GraphQL
permissions and the CLI continues to prompt for CRUD - pushing with
amplify push --force
did not update thecli.json
file in our deployment bucket - pulling with
amplify pull
in a locally initialized project does not update thecli.json
file with what is stored in our deployment bucket
- several feature flags did not match what we had locally, namely
- lastly
"failed to create models in the cloud: Modelgen creation failed"
, which led us toUnrecognizedClientException: The security token included in the request is invalid at Object
Hey @gspeicher :wave: I wanted to follow up on this one as I have not been able to successfully reproduce this issue. Have you continuously experienced this?
The issue is still unchanged for me, @josefaidt. I just ran amplify update function
using Amplify CLI 9.0.0 and updated the resolver function to remove create
permission from the API resource. The resulting diff is similar to the original one I posted above. I pushed that update to a non-production backend and the result is the same: the resolver function can no longer query the API after I've removed create permissions. Cloudwatch logs show the error:
"Error: GraphQL Errors[GetProfile]: [{\"errorType\":\"UnauthorizedException\",\"message\":\"Permission denied\"}]",
So the resolver is configured to allow read, update, and delete but the reads are still failing without create. Again, I believe the reason here is that the generated CF template is incorrect; it is missing the resource arn:aws:appsync:region:accountId:apis/GraphQLAPIIdOutput/*
(it only contains the one without the trailing *
).
Hey @gspeicher does amplify env checkout <current-env-name>
resolve the permissions/feature flag issue where amplify update function
will prompt us for Query, Mutation, Subscription? For the modelgen issue would you mind filing here? https://github.com/aws-amplify/amplify-adminui
Negative @josefaidt, doing a fresh (re)checkout doesn't change the situation at all.
Hey @gspeicher apologies for the delay here!! Are you still experiencing this with the latest version of the CLI (9.1.0)? I have not been able to successfully reproduce this issue.
@josefaidt just confirmed no change with CLI 9.2.1
Not sure if this is related at all, but I now get the following prompt on every single push. I mention this because at one point it appeared there might be a problem with amplify CLI reading cli.json
settings correctly, and I would kind of expect that this new prompt would be silenced via cli.json
after responding in the affirmative followed by a successful deployment.
✔ This version of Amplify CLI introduces additional security enhancements for your GraphQL API. The changes are applied automatically with this deployment. This change won't impact your client code. Continue? (Y/n)
Hey @gspeicher apologies for the delay here, I'm wondering if there may be a conflict in the file or an invalid combination of flags causing this issue. Below is a copy of a cli.json
file from a fresh project, can you copy this over to your project, and attempt updating your functions with amplify update function
to regenerate permissions in the template? Does this remove the CRUD permissions in favor of the GraphQL operations?
{
"features": {
"graphqltransformer": {
"addmissingownerfields": true,
"improvepluralization": false,
"validatetypenamereservedwords": true,
"useexperimentalpipelinedtransformer": true,
"enableiterativegsiupdates": true,
"secondarykeyasgsi": true,
"skipoverridemutationinputtypes": true,
"transformerversion": 2,
"suppressschemamigrationprompt": true,
"securityenhancementnotification": false,
"showfieldauthnotification": false,
"usesubusernamefordefaultidentityclaim": true,
"usefieldnameforprimarykeyconnectionfield": false,
"enableautoindexquerynames": false,
"respectprimarykeyattributesonconnectionfield": false,
"shoulddeepmergedirectiveconfigdefaults": false,
"populateownerfieldforstaticgroupauth": false
},
"frontend-ios": {
"enablexcodeintegration": true
},
"auth": {
"enablecaseinsensitivity": true,
"useinclusiveterminology": true,
"breakcirculardependency": true,
"forcealiasattributes": false,
"useenabledmfas": true
},
"codegen": {
"useappsyncmodelgenplugin": true,
"usedocsgeneratorplugin": true,
"usetypesgeneratorplugin": true,
"cleangeneratedmodelsdirectory": true,
"retaincasestyle": true,
"addtimestampfields": true,
"handlelistnullabilitytransparently": true,
"emitauthprovider": true,
"generateindexrules": true,
"enabledartnullsafety": true
},
"appsync": {
"generategraphqlpermissions": true
},
"latestregionsupport": {
"pinpoint": 1,
"translate": 1,
"transcribe": 1,
"rekognition": 1,
"textract": 1,
"comprehend": 1
},
"project": {
"overrides": true
}
},
"debug": {
"shareProjectConfig": true
}
}
@josefaidt unfortunately this results in no change in behavior of the CLI or the contents of the generated files at all.
Could it possibly be related to the fact that we have multiple auth providers configured for our API? We are using userPools
as the primary provider for users logged into our mobile app, and iam
as a secondary provider for connections from Lambda functions.
Hey @gspeicher I don't believe the auth modes would impact this, and to confirm I've added multiple auth mechanisms to my GraphQL API to no avail. I'm only able to reproduce the CRUD prompt by setting that flag to false or by removing it entirely. I'm going to mark this as "not-reproducible" for now, however I am interested in getting you unblocked. As a few follow-up questions:
- do you experience this issue in a separate project?
- are there any oddities associated with the file's ownership or permissions? Are you able to delete and recreate this file with any change to the CRUD prompt?
- if you pull this same project into a separate directory does the CLI still fail to read the
cli.json
file and continue to prompt for CRUD?
The file mode for cli.json
is 0644
and owned by me so I don't think it is a permissions issue. I have a second checkout of this same project and get the exact same behavior regardless of which folder I run it in.
Hmm, I am running on a case-sensitive filesystem on MacOS, rather than the default case-insensitive volume. Could amplify-cli possibly be loading this file using something other than all lowercase on MacOS?
Hmm, I am running on a case-sensitive filesystem on MacOS, rather than the default case-insensitive volume. Could amplify-cli possibly be loading this file using something other than all lowercase on MacOS?
That is a great callout, I'm not too sure how this would impact the CLI's functionality but worth investigating. If you load this project into an ec2 instance, initialize the project, run amplify update function
, select a function, and make an empty update does it change the contents of the policy?
In another issue we noticed there was a duplication of a feature flag, do you also see a duplicated feature flag?
I just cloned the project into a standard MacOS case-insensitive apfs filesystem and the behavior is the same. One thing I did notice (not sure how this eluded me until now) is that I have two copies of cli.json
: one in my application root folder and another in the amplify
folder. The one in the amplify folder is much more complete but also has conflicting settings with the one in the app root. Which location is correct?