Serverless Framework AWS Fine-Grained Access Control - amazon-web-services

I am attempting secure my AWS API such that DynamoDB rows can only be accessed by the corresponding authenticated Cognito user by implementing fine grained access control in my Serverless Framework config (serverless.yml)
See example of what I am attempting in the AWS Documentation
I have tried to convert the Cloudformation syntax to Serverless without success; when I try something like the following expression in my policy statement:
Condition:
ForAllValues:StringEquals:
dynamodb:LeadingKeys: ["${cognito-identity.amazonaws.com:sub}"]
I then get an error:
Invalid variable reference syntax for variable cognito-identity.amazonaws.com:sub. You can only reference env vars, options, & files. You can check our docs for more info.
Is this even possible in Serverless? Or is it Cloudformation and SAM only?

I was encountring same problem and solve it this way:
Condition:
ForAnyValue:StringLike:
"dynamodb:LeadingKeys":
- !Join ["", [ "$", "{cognito-identity.amazonaws.com:sub}" ]]
That's not very clean, but as per now variables syntax collides with AWS params syntax. See this for more details - https://github.com/serverless/serverless/issues/2601

It is possible in serverless. If I were you I will use AWS Lambda to verify the id_token which is sent to the user.
In this scenario, you should first transfer the key to AWS Lambda function using Api Gateway or other methods. Then follow this guide to verify the token. The code can be found in: https://github.com/awslabs/aws-support-tools/tree/master/Cognito/decode-verify-jwt
After verifying it you can add your code here:
......
if claims['aud'] != app_client_id:
print('Token was not issued for this audience')
return False
# now we can use the claims
# add your code here #
print(claims)
return claims

Related

AWS CloudFormation: Cognito LambdaTrigger CustomEmailSender - Property "Not currently supported by AWS CloudFormation." and CDK usage

Generally what means Property "Not currently supported by AWS CloudFormation" for a CDK implementation, specifically:
In the CloudFormation Properties for the Cognito Userpool Lambda Config it says:
CustomEmailSender - Not currently supported by AWS CloudFormation.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-lambdaconfig.html
In the CDK for Cognito.CfnUssrPool this property is described:
https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-cognito.CfnUserPool.LambdaConfigProperty.html#customemailsender
My question now is whether this can be implemented with CDK at all? Currently, our Cognito is provided completely via CDK and I would like to keep it that way.
Edit:
I found a link (Using CustomEmailSender with CFN) where it says that contrary to the documentation it does seem to work and only the documentation has not been updated, I will test this and give feedback.
After testing CustomEmailSender CDK implementation, I have to say that the AWS CloudFormation Documentation hasn't a current state. So it is possible to use this function by CFN and CDK. AWS Doc CFN Cognito CustomEmailSender
ToDos in CDK:
Configure Cognito: instead of using the property emailConfiguration you have to use lambdaConfig:
lambdaConfig: {
customEmailSender: { lambdaArn: customSenderEmailLambdaArn, lambdaVersion: 'V1_0' },
// the version is an ENUM so be careful when you set it
kmsKeyId: kmsKeyArn,
},
As you see here you have also to set up a KMS Key (you can adjust an existing one or create a new KMS key). The most important thing here is that you allow the action kms:CreateGrant to Cognito AND the Lambda function!
Another important ToDo is to add your Lambda the permission for Cognito, action cognito-idp:InvokeFunction.
The big advantage of using CustomEmailSender instead of the common Lambda Triggers that you don't have to set all the triggers in you CDK code or in the Cognito Console (all email events will be sent automatically to your lambda).

API Gateway export API definition with Postman

I was checking out a couple methods from amazon-api-gateway-developer-guide and I stumbled with api-gateway-export-api, I managed to get the AWSCLI command (aws apigateway get-export --parameters extensions='apigateway' --rest-api-id abcdefg123 --stage-name dev --export-type swagger latestswagger2.json --profile profile --region us-east-1) working by adding the --profile and --region parameters, but for whatever reason I can't get the Request URL to return the API definition response.
I am trying to do the following couple things (Postman):
GET request with the URL: https://apigateway.us-east-1.amazonaws.com/restapis/abcdefg123/stages/dev/exports/oas30
Add authorization type AWS-Signature to the request with all the
parameters filled (AccessKey, SecretKey, AWS Region,
Service Name and Session Token)
The Authorization, x-Amz-Date and x-Amz-Security token are generated successfully, as far as I can tell
I am also sending a the headers Host (apigateway.us-east-1.amazonaws.com) and Accept (application/yaml)
This results in the following error:
{"logref":"2734hu2r2873","message":"User:
arn:aws:sts::7216832187:assumed-role/DEVELOPER/xxxx is not authorized to perform:
apigateway:GET on resource:
arn:aws:apigateway:us-east-1::/restapis/abcdefg123 /stages/dev/exports/oas30"}
I was actually getting the same error with the AWSCLI command before I added the --profile and --region parameters. I already checked out a couple posts about issues like mine, this one is an example export swagger api definition from api gateway via http request?, but I am doing basically the same thing (sending the same headers, same host and URL) and getting this error. I don't think my access key, secret key, token or any of that information might be wrong... because it's the credentials I use for the AWSCLI command.
Thank you for the taking the time to read and/or reply to my post, I really appreciate any feedback in anything.
The point of trying to get this request to work is that I wanted to add it as an HTTP proxy to an API, unfortunately I couldn't get it to work.
My problem was that I have a couple profiles in my session and each one of them has different permissions, I found no way to specify the profile I wanted to use in the request and thus it caused the error mentioned above.
In the end I opted to write a Lambda function with proxy integration and everything went fine, after giving the lambda profile the apigateway:GET permission.
If I have a misconception about anything I wrote about or I am using a term in an incorrect way please correct me, I am still fairly new to AWS and have a lot to learn!

uri for aws_api_gateway_integration for type AWS (integration) to DynamoDb

I am creating infrastructure with terraform with API Gateway connecting to DynamoDb API. I am creating resource aws_api_gateway_integration to define the integration with DynamoDb with type attribute set as AWS.
But somehow i am unable to get the uri value right for db.
Documentation says it should be of format arn:aws:apigateway:{region}:{subdomain.service|service}:{path|action}/{service_api}.
Current value configured is arn:aws:apigateway:us-east-1:dynamodb:GetItem.
I am not sure what is service_api. Did anyone encounter this before? Kindly help.
This also took me some time to figure out, I think the DynamoDB API-Gateway Integration deserves a dedicated example in the Terraform docs similar to Lambda. Actually service_api just refers to the DynamoDB Action (which you would also enter in the AWS Api Gateway Console Action field) prefixed with the literal action/. So the following block eventually worked for my PUT request mapping:
resource "aws_api_gateway_integration" "my-integration" {
type = "AWS"
integration_http_method = "POST"
uri = "arn:aws:apigateway:eu-central-1:dynamodb:action/PutItem"
# (...)
}

No integration defined for method - Choose a stage where your API will be deployed

I'm working with AWS API Gateway and AWS Lambda. Often I face this type of error message when attempt to deploy API. The error message says to select a deployment stage. But I still selecting and trying to deploy! but same error occur!
In this API I have multiple resources with multiple methods. Previously I succeed to deploy this same API with the same way. But now I can't deploy it.
Please anyone help me to fix it. For addition: I don't use AWS CLI tool, just use AWS web dashboard.
I talked with customer service center of AWS. The problem was:
In this API there was an unintegrated method. Suppose there are a resource image and I create a POST method for this resource. But I forgot to integrate it to any AWS Lambda Function or HTTP. So the API cannot be deployed.
If the method is unnecessary then delete the method. OR you can integrate it as Mock endpoint. You can change this endpoint anytime.
Note: For this unintegration problem AWS gives this type of wrong error message. They should update their message to save developer's time.
I was getting same error but when creating API using CloudFormation.
It turned out that in my AWS::ApiGateway::Deployment resource, I needed to include DependsOn attribute that "depends" on all my API methods.
For example, when building API with two AWS::ApiGateway::Method resources, AWS::ApiGateway::Deployment needs to depend on both these methods:
MyFirstApiMethod:
Type: AWS::ApiGateway::Method
Properties:
<your properties>
MySecondApiMethod:
Type: AWS::ApiGateway::Method
Properties:
<your properties>
MyDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: [MyFirstApiMethod, MySecondApiMethod] # <-- REQUIRED
Properties:
RestApiId: !Ref MyRestApi
Without the DependOn attribute on all the API methods, CloudFormation may be
creating them after the deployment resource, resulting in No integration defined for method error.
If you have another resource which is not completed to configuration it will read as well. In short, if you haven't given them a lambda function, the api itself is not allowed to be deployed until you finish the rest.
I encountered the same error with deploying via Terraform. The reason was I defined an IAM role for my API and I didn't include the role resource to triggers when deploying the API. Just make sure all resources that are defined before deploying are included in triggers.
Just integrate Lambda function in every method you created.
Make sure every resource and method is configured properly.
Let's say your api-gateway is hierarchy is like:
/
R1
R2
M1
M2
R3
M3
so every resource(R1,R2,R3) and every method(M1,M2,M3) should be configured properly.
I deployed using CDK with --no-rollback (this should work for any cloudformation though)
In my case, the API was created and I could inspect it in the AWS Console, and only the "AWS::ApiGateway::Deployment" failed to create. It turns out I had a bad value for service attribute (I was using StepFunctions, which was not working)
THEN I see that I have a dangling resouce/method that is broken - so my deployment was failing due to garbage in AWS, not my CDK/template.

AWS Execution failed due to configuration error: Authorizer error

I created 2 Authorizers to an API Gateway Endpoint. One manually using console and the other one using boto3.
The one created manually works great, however the one created using script gives error mentioned in the subject line. If you check the contents of these 2 authorizers, they are the same.
What can be the missing part? I dont think this is Invalid permission on lambda as it is working on one authorizer when configured manually.
The code for the same is as below:
response = client.create_authorizer(
restApiId=apiid,
name=authName,
type='TOKEN',
authType='custom',
authorizerUri=authorizerUri,
##arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:<AcctId>:function:CustomAuthorizer/invocations
identitySource='method.request.header.Authorization',
identityValidationExpression= '.*',
authorizerResultTtlInSeconds=300
)
Getting error:
Mon Jul 18 11:53:04 UTC 2016 : Execution failed due to configuration error: Invalid permissions on Lambda function
Mon Jul 18 11:53:04 UTC 2016 : Execution failed due to configuration error: Authorizer error
Mon Jul 18 11:53:04 UTC 2016 : AuthorizerConfigurationException
Just went down this rabbit hole, and came across a solution (worked with boto3 and python, hope it is similar across different sdk's).
The issue is that when you create it manually through the console, there is a popup that asks to enable api gateway (this specific authorizer to be exact) as a trigger to the lambda function you are using as an authorizer. When doing it via sdk, there obviously is no popup, so the authorizer is not authorized to invoke that lambda.
You can enable permissions two ways:
In lambda, create a new trigger for your authorizer in the Designer panel. Specifying an api and stage should do the trick
Create a role that will handle this permission. Give it permission to invoke lambda (you can use the AWSLambdaRole template role) and make sure to add api-gateway as a trusted entity to use this role in the Trust Relationships tab. your policy should look something like this:
{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "apigateway.amazonaws.com"},"Action": "sts:AssumeRole"}]}
Now, when creating a custom authorizer using the sdk, you have to provide an authorizerCredentials parameter, which is described as "(string) Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.".
Copy your newly-created role's arn and use this as the value for the authorizerCredentials param.
Viola! you now have a custom authorizer that is allowed to use the role that allows it to invoke the lambda function!
(sorry for bad formatting, I don't comment often :D)
First, use the test button in API Gateway to confirm if you can call your Lambda function from within it. This will ensure that the API Gateway-to-Lambda connection is working.
To assess your resource policies, you need to call the GetPolicy API; the execution role controls what your code an do in Lambda, not who can call it. That would be a good next check.
You can also turn on logging in API Gateway, which is a good way to gain additional insight into what it's doing on your behalf. These logs then show up in Amazon CloudWatch Logs, where you can check out the flow of your request.
If you're using CORS, it's possible to miss a setting (or two :), so I would double check that as well. CURLing directly to your endpoint (you can easily test from Lambda using its HTTP endpoint blueprint) is a good first step before you "wire up" your API to a website or other app directly.
It would be helpful if you check your CloudWatch Logs Insights.
Go to your lambda function, open "Monitor" section. Read through the last logs received.
In my case, I made a typo on lambda methods handler address. That is why the error thrown.
You need to add a resource-based statement to your Lambda's permissions, in order to allow it to be called by your gateway. As #MichaelJoy points out, this is taken care of in the console when you click "Create" on the popup. Doing this programmatically requires taking a second step after your authorizer has been created.
To do this via CLI, you can do the following (presumably boto3 has all of the corresponding commands):
aws lambda add-permission --function-name 'arn:aws:lambda:us-west-2:<AcctId>:function:CustomAuthorizer' \
--action lambda:InvokeFunction --statement-id 'api_gateway' \
--principal apigateway.amazonaws.com --output text \
--source-arn "AUTHORIZER_ARN"
where AUTHORIZER_ARN is the ARN of the authorizer you just created. Note that the statement id of 'api_gateway' is arbitrary.
You can see all resource-based policies on your Lambda via the following. This may be helpful esp if you want to inspect the permissions added by you via the console as a working example of what you need to effect programmatically to get the same result:
aws lambda get-policy --function-name XXXX
If you're updating an existing resource-based permission, you'll need to remove it first via the remove-policy command