3

Recently AWS announced that Amazon API Gateway Supports Resource Policies for APIs

Is it possible to attach a resource policy to a AWS::Serverless::Api created via Cloudformation with SAM?

niqui
  • 1,562
  • 1
  • 16
  • 28

2 Answers2

2

I haven't had the chance to try this yet but I assume you can use it like you would use an S3 Bucket Policy. The trickiest part for you would be to grab the api-id to be able to use in the Resource ARN(s).

So, in your template you would have a piece that contains similar YAML (or JSON). This would allow <some user> to use the API

Statement:
- Effect: Allow
  Principal:
    AWS:
    - arn:aws:iam::<account-id>:user/<some user>
    - account-id
  Action: execute-api:Invoke
  Resource:
  - execute-api:/*/*/*

Note that the execute-api:/*/*/* gets converted automatically during deployment to something that looks like arn:aws:execute-api:<region>:<account-id>:<api-id>/*/*/*

This approach should work just like bucket policies and this is how you apply a policy to a Bucket.

Good luck!

V Maharajh
  • 9,013
  • 5
  • 30
  • 31
Adam
  • 344
  • 1
  • 15
  • 1
    Apparently this policy should be added as part of RestApi declaration. Ref https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-policy. How do you get the ? (since it needs to refer to itself). I get circular dependency error on trying to do that. – whitehat Apr 01 '19 at 19:05
  • 1
    I think what you're asking is "how do I submit/deploy this policy?". There's a couple of ways to go about getting the policy attached to the API. You can create a separate template and submit it. This template would contain your policy and it would Ref the API. The second, probably simpler way for this case is to embed the policy directly into your API's declaration; Just put the policy at the same level as "properties" in the API's template under a key called "policies". Search for "Policy Templates" here: https://docs.aws.amazon.com/serverlessrepo/latest/devguide/using-aws-sam.html – Adam Apr 02 '19 at 22:24
  • 1
    You can use "Resource": "execute-api:*/*/*" without having to provide – V Maharajh Oct 24 '19 at 16:24
  • 1
    Yeah, that works like a charm. I've edited the answer. @niqui did this work for you? – V Maharajh Oct 24 '19 at 17:08
  • My issue is that I have "AWS:Serverless:Api" rather than "AWS:ApiGateway:RestApi" type defined, because I need complicated OpenAPI swagger definitions. "AWS:Serverless:Api" does not allow "Policy" property. :( How can I attach a resource policy then in this case? Any help or suggestion would be most appreciated. Thank you! – Tracy Xia Nov 14 '19 at 19:30
  • Maybe you're looking for something like this https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api-auth-object . – Adam Nov 15 '19 at 21:26
1

Yes, I have reproduced a minimal example of this based on this old Python example from AWS. I modified it because the bucket it references is region-specific and doesn't seem to grant public access anyway. My example below is fully inline, not depending on any external resources. I also changed the language to JS.

Note that the policy goes under the Auth element which must be nested under Properties*. Here is a snippet:

      Auth:
        ResourcePolicy:
          CustomStatements: {
              Effect: 'Allow',
              Action: 'execute-api:Invoke', 
              Resource: ['execute-api:/*/*/*'],
              Principal: '*'
            }

And here is full example code that you can deploy with SAM:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs12.x
    Timeout: 30
    AutoPublishAlias: live

Resources:
  ExplicitApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ResourcePolicy:
          CustomStatements: {
              Effect: 'Allow',
              Action: 'execute-api:Invoke', 
              Resource: ['execute-api:/*/*/*'],
              Principal: '*'
            }
  MinimalFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: index.handler
      InlineCode: |
        exports.handler = (event, context, callback) => {
            callback(
                null,
                {
                    statusCode: 200,
                    body: JSON.stringify({
                        message: 'Hello World'
                    })
                });
        };
      Events:
        AddItem:
          Type: Api
          Properties:
            RestApiId: 
              Ref: ExplicitApi
            Path: /add
            Method: post

After deploying this, I can go to the API Gateway in the AWS console, and under "Resource Policies" I can see:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "XXX"
        }
    ]
}

Note: I've redacted the Resource ARN in the above.

*I originally had a bug in my code, with Auth nested directly under the AWS::Serverless::Api. SAM did not throw an error, nor did my VS linter, but the policy upload silently failed.

Alternative Syntax

The above uses a JSON object within YAML. If you want to stick with pure YAML, use this:

  Auth:
    ResourcePolicy:
      CustomStatements:
        Effect: Allow
        Action: execute-api:Invoke
        Resource:
        - execute-api:/*/*/*
        Principal: '*'  
Colm Bhandal
  • 3,343
  • 2
  • 18
  • 29