aws / serverless-application-model

The AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.
https://aws.amazon.com/serverless/sam
Apache License 2.0
9.29k stars 2.38k forks source link

Support WAF for API Gateway #792

Open dan-lind opened 5 years ago

dan-lind commented 5 years ago

As announced here, API Gateway now natively supports WAF.

Would be great to have access to this functionality through SAM More detailed description of steps required to set this up currently can be found here

keetonian commented 5 years ago

It looks like the CFN API Stage resource does not yet support adding a WAF, so I don't think SAM can connect a WAF to an API Gateway Stage yet. However, it looks like it is possible to create WAF Resources in CFN, so this functionality might be available soon. I'll keep this issue open as a feature request; we can create an RFC for this feature when CFN support is available.

It looks like there are some WAF examples in aws-samples: https://github.com/awslabs/aws-waf-security-automations . They use custom resources to connect the WAF to ApiGw.

azarboon commented 5 years ago

Yes, I also need this feature

azarboon commented 5 years ago

Just checked one of the templates: https://github.com/awslabs/aws-waf-security-automations/blob/master/deployment/aws-waf-security-automations.template

It's too crowded and has lots of things to configure. Can you pleasep oint out which of those are needed? Or even better, can you please provide a bare example of how can one create WAF and attach it to APG stage with current limitations?

keetonian commented 5 years ago

It looks like there is a way to do this in CloudFormation. This requires more investigation still.

It looks like you need to create a Regional WAF: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafregional-webacl.html

Then associate it with your API stage by using the API stage arn (by using !GetAtt MyApi.Stage): https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafregional-webaclassociation.html

Here's more information about how to set this up using the console/sdk/cli: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-aws-waf.html

I'm tagging this to indicate that we need more information. It seems like SAM could offer a lot of value in this area. We would need some simple default values for rules, more information on common WAF setups, and how SAM can make this process easier as well as keep it configurable for more advanced features.

For now, you can use the native CloudFormation resources in your template to create this WAF and associate it with your API.

MartinasGit commented 5 years ago

I already tried this but without success. Please find here my example template and a description of my attempt. Would be happy if somebody sees what I'm missing out here.

amkuipers commented 5 years ago

@keetonian aws waf-regional list-web-acls gives a WebACLId value. Now when the URL of your stage is something like https://RestApiId.execute-api.us-east-1.amazonaws.com/StageName, using the RestApiId and StageName and WebACLId you can call aws waf-regional associate-web-acl --web-acl-id WebACLId --resource-arn arn:aws:apigateway:us-east-1::restapis/RestApiId/stages/StageName. After this the aws waf-regional get-web-acl --web-acl-id WebACLId returns data with the WebACLArn so you see that it is associated. A CloudFormation/SAM example using the WAFRegional to RestAPI and/or Stage would be nice.

MartinasGit commented 5 years ago

I contacted the AWS Support: ...[this is a ] limitation we have with CloudFormation when dealing with AWS::WAFRegional::RateBasedRule.

Despite the fact that CloudFormation supports creating WAF regional rate-based rules, the association of them with a Web ACL is not currently supported. If you observe link [1] below, you will realize that:

"To add the rate-based rules created through CloudFormation to a web ACL, use the AWS WAF console, API, or command line interface (CLI)."

[1] AWS::WAFRegional::RateBasedRule: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafregional-ratebasedrule.html

@amkuipers an example for WAFRegional with a RESTApi can be found in my post here: https://stackoverflow.com/questions/56683755/sam-api-gateway-with-cloudformation-wafregional

azarboon commented 4 years ago

It looks like there is a way to do this in CloudFormation. This requires more investigation still.

It looks like you need to create a Regional WAF: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafregional-webacl.html

Then associate it with your API stage by using the API arn (by using !Ref MyApi.Stage): https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafregional-webaclassociation.html

Here's more information about how to set this up using the console/sdk/cli: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-aws-waf.html

I'm tagging this to indicate that we need more information. It seems like SAM could offer a lot of value in this area. We would need some simple default values for rules, more information on common WAF setups, and how SAM can make this process easier as well as keep it configurable for more advanced features.

For now, you can use the native CloudFormation resources in your template to create this WAF and associate it with your API.

I tried this for AWS::WAFRegional::IPSet and can confirm that it works. Thanks.

chrisoverzero commented 4 years ago

Then associate it with your API stage by using the API arn (by using !Ref MyApi.Stage) [...]

@keetonian Doesn't !Ref MyApi.Stage give the name of the stage? In order to associate the ARN of an API Gateway stage, it would have to be constructed, like this:

!Sub arn:${AWS::Partition}:apigateway:${AWS::Region}::/restapis/${ServerlessRestApi}/stages/${ServerlessRestApi.Stage}

...except I think that will fail because it will try to !GetAtt ServerlessRestApi.Stage. So it will have to become:

!Sub
- arn:${AWS::Partition}:apigateway:${AWS::Region}::/restapis/${ServerlessRestApi}/stages/${Stage}
- Stage: !Ref ServerlessRestApi.Stage

Er, right? Or have I missed something?

ETA What I've written certainly works, but I'm not confident it's optimal. Th-There must be a way to get the ARN of a stage, right?

keetonian commented 4 years ago

@chrisoverzero you are correct, it looks like you can't get the arn of the stage: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html

chrisoverzero commented 4 years ago

Perhaps that's the first small value SAM could provide in this area, then. Moving the relationship into the AWS::Serverless::Api so that the Stage ARN construction is abstracted away, that is. Something like:

Globals:
  Api:
    OpenApiVersion: '3.0.1'
    WebACL: !Ref WebACLId
    ... #etc.

...expanding into:

"ServerlessRestApi": {
  "Type": "AWS::ApiGateway::RestApi",
  "Properties": {
    "Body": "<All the generated OpenAPI>",
    "Etc.": {}
  }
},
"ServerlessRestApiProdStage": {
  "Type": "AWS::ApiGateway::Stage",
  "Properties": {
    "RestApiId": { "Ref": "ServerlessRestApi" },
    "Etc.": {}
  }
},
"ServerlessRestApiProdStageWebACLAssociation": {
  "Type": "AWS::WAFRegional::WebACLAssociation",
  "Properties": {
    "WebACLId": { "Ref": "WebACLId" },
    "ResourceArn": {
      "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}::/restapis/${ServerlessRestApi}/stages/${ServerlessRestApiProdStage}"
    }
  }
}

...except maybe with a better generated name.

I think this is even forwards compatible -- in that other places in SAM accept different shapes of data to determine behavior, so this could, as well. Something like:

ETA Ah, but in thinking about it, !If makes this idea intractable. Oh, well.

piersf commented 4 years ago

Hello there,

I was hoping this would be an already supported feature but got disappointed when I saw it is now. Is there any estimation on when this would be supported?

Thank you!

tkeeber commented 4 years ago

I'm also having problems with this.

ApiGatewayWebACLAssociation:
    Type: AWS::WAFRegional::WebACLAssociation
    Properties:
      ResourceArn: !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/${ServerlessRestApi}/stages/${ServerlessRestApiProdStage}"
      WebACLId: !Ref WAFRegionalWebACLId

  Api:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod

Fails when deployed.

As does


    Type: AWS::WAFRegional::WebACLAssociation
    DependsOn: ProvisionalOrdersApi
    Properties:
      ResourceArn: !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/${Api}/stages/Prod"
      WebACLId: !Ref WAFRegionalWebACLId

  Api:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod```
tkeeber commented 4 years ago

I solved this problem by removing the AWS::Serverless::Api completely and defining any of the properties i needed (in this case -> Auth) in a global

Api:
    EndpointConfiguration: REGIONAL
    Auth:
      DefaultAuthorizer: MyLambdaAuthorizer
      Authorizers:
        LambdaAuthorizer:
          FunctionArn: !GetAtt MyAuthorizerFunction.Arn
nekoni commented 3 years ago

The following code works for me:

  ApplicationApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      EndpointConfiguration: EDGE
......
  ApiGatewayWebACLAssociation:
    Type: AWS::WAFRegional::WebACLAssociation
    Properties:
      WebACLId: !Ref WafWebAcl
      ResourceArn:
        !Sub
          - "arn:aws:apigateway:${AWS::Region}::/restapis/${ApplicationApi}/stages/${ApiStage}"
          - ApiStage: !Ref ApplicationApi.Stage
aprilmintacpineda commented 3 years ago

After hours of reading and researching, I finally found how to do it. Here's how I did it:

mainBackendApi:
  Type: AWS::Serverless::Api
  Properties:
    StageName: !Ref Stage
    EndpointConfiguration:
      Type: REGIONAL
mainBackendWebAcl:
  Type: AWS::WAFv2::WebACL
  Properties:
    DefaultAction:
      Block: {}
    Scope: REGIONAL
    VisibilityConfig: 
      CloudWatchMetricsEnabled: true
      MetricName: web-acl
      SampledRequestsEnabled: true
    Rules:
      - Action:
          Block: {}
        Name: RateLimit
        Statement:
          RateBasedStatement:
            AggregateKeyType: IP
            Limit: 1500
        VisibilityConfig: 
          CloudWatchMetricsEnabled: true
          MetricName: rate-limit
          SampledRequestsEnabled: true
        Priority: 0
      - Action:
          Allow: {}
        Name: AllowedCounties
        Statement:
          GeoMatchStatement:
            CountryCodes:
              - AU
              - PH
        VisibilityConfig: 
          CloudWatchMetricsEnabled: true
          MetricName: allowed-countries
          SampledRequestsEnabled: true
        Priority: 1
mainBackendWebAclAssociations:
  Type: AWS::WAFv2::WebACLAssociation
  Properties: 
    ResourceArn: !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/${mainBackendApi}/stages/${Stage}"
    WebACLArn: !GetAtt mainBackendWebAcl.Arn

This will implement a rate-limiting of 5 requests per second and also only allow requests coming from defined countries.