mlevit / aws-auto-cleanup

Programmatically delete AWS resources based on an allowlist and time to live (TTL) settings
MIT License
495 stars 54 forks source link

Restrict API access by IP #78

Closed miki79 closed 3 years ago

miki79 commented 3 years ago

At the moment when you deploy the API, there is no security mechanism in place, so everybody that can access the API can change the whitelist and look at the resources.

A quick solution to block the access would be via IP restriction, so that the frontend web app wouldn't require any changes.

To restrict access by IP, it would be possible to use the resource policy for the API gateway and set it up via serverless with something like this

  resourcePolicy:
    - Effect: Allow
      Principal: '*'
      Action: execute-api:Invoke
      Resource:
        - execute-api:/*/*/*
      Condition:
        IpAddress:
          aws:SourceIp:
            - ${self:custom.ipRange}

so the changes to serverless.yml in api directory would be like this

custom:
  log_level: INFO # DEBUG for dev | INFO for prod
  region: ${opt:region, "ap-southeast-2"} # AWS deployment region
  manifest:
    output: ../web/src/serverless.manifest.json
    silent: true
  pythonRequirements:
    layer:
      name: ${self:service}-${self:provider.stage}-requirements
      compatibleRuntimes:
        - python3.8
    noDeploy:
      - boto
      - boto3
      - botocore
    slim: true
  ipRange: ${opt:ip, "0.0.0.0/0"} # overwrite via CLI "--ip 50.1.0.0/24"

provider:
  name: aws
  runtime: python3.8
  stage: ${opt:stage, "prod"} # overwrite via CLI "--stage dev"
  region: ${self:custom.region}
  profile: ${opt:profile, ""} # overwrite via CLI "--aws-profile saml"
  resourcePolicy:
    - Effect: Allow
      Principal: '*'
      Action: execute-api:Invoke
      Resource:
        - execute-api:/*/*/*
      Condition:
        IpAddress:
          aws:SourceIp:
            - ${self:custom.ipRange}
  apiGateway:
    minimumCompressionSize: 1024
miki79 commented 3 years ago

I'm happy to create a PR, but I'd like to discuss how you prefer to setup the IP range

mlevit commented 3 years ago

Hey @miki79 thanks for the recommendation. I think the easiest way to secure the API is to make it private.

service: my-service
provider:
  name: aws
  apiGateway:
    apiKeys:
      - myFirstKey
functions:
  hello:
    events:
      - http:
          path: user/create
          method: get
          private: true

The private key is generated and needs to be passed in for each API request. The trick here is to pass that generated key to the web application during deployment. Are you able to look into this?

miki79 commented 3 years ago

if you pass and embed the private key into the web application, you still have the problem that whoever knows the url of the web application can access and modify the data. I think it makes more sense to don't bake the key inside the web application but pass it on the query parameter when you open it. Even if it's not best practice from security point of view, you could bookmark the url of the webapp and so no need to re-insert the api key every time.

Otherwise having a popup on the launch of the webapp asking for the key, and then store it in the local storage of the browser, so it would ask for the key only the first time.

miki79 commented 3 years ago

I implemented some changes to have the API secured with API key, and the setup of the key on the first load of the application (the key is stored in localStorage on the browser) insert-key

reset-button

I have the code ready, if it's ok I'll update the documentation and raise a PR

mlevit commented 3 years ago

Hey @miki79 I like it. Thanks for putting the effort in. I guess this way we kill two birds with one stone, we secure the API and the website.

Raise the PR and we'll do a review and merge :)