Search code examples
amazon-web-servicesamazon-ec2api-gateway

Allow a specific API Path to an IP address using Resource Policy on AWS API Gateway


The Scenario

I have an EC2 Server, who hits 'n' number and a group of users who hits 'm' number of APIs via of APIs via Internet. For only those 'n' number of APIs I need only this EC2 Server to access it and no other users.

The Problem

While setting the resource policy, for whitelisting for EC2 to access 'n' APIs, the other 'm' API paths are also restricted.

Description

API Structure

Note that all Requests are GET for n.

/
|--m
   |--square GET
   |--triangle POST
   L--circle GET, POST
|--n
   |--red GET
   |--green GET
      |--trees GET
      L--pear GET
   L--blue GET

Resource Policy created

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:xx-direction-1:123456789101:abcdefghij/alpha/GET/m/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "100.200.100.200/32"
                }
            }
        }
    ]
}

When I access 'm' paths via curl, I receive the same json as of user when it hits the 'n' APIs. Viz.

{"message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:...


Solution

  • So to speak, the problem here is anything, but simple. The trick is to Allow and Deny on same API path and solution reminds me the logic used in Electronics' NAND Gate and NOR gate kind.

    Solution

    Step 1: Deny the path if the IP address is not the one we require

    Notice, how the Condition calls for NotIpAddress.

    {
                "Effect": "Deny",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:xx-direction-1:123456789101:abcdefghij/alpha/GET/m/*",
                "Condition": {
                    "NotIpAddress": {
                        "aws:SourceIp": "100.200.100.200/32"
                    }
                }
            }
    

    Step 2: Allow the path if the IP address is the One we require

    Change the Condition here, this is the one I missed out on.

    {
                "Effect": "Allow",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:xx-direction-1:123456789101:abcdefghij/alpha/GET/m/*",
                "Condition": {
                    "IpAddress": {
                        "aws:SourceIp": "100.200.100.200/32"
                    }
                }
            }
    

    Step 3: Allow the Root path to be accessed by Public / Users

    There's no IP Address Condition mentioned, well because "Public"!

    {
                "Effect": "Allow",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:ap-south-1:480833364711:q9omcoj8ba/alpha/GET/*"
            }
    

    Complete Resource Policy looks like this:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Deny",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:xx-direction-1:123456789101:abcdefghij/alpha/GET/m/*",
                "Condition": {
                    "NotIpAddress": {
                        "aws:SourceIp": "100.200.100.200/32"
                    }
                }
            },
            {
                "Effect": "Allow",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:xx-direction-1:123456789101:abcdefghij/alpha/GET/m/*",
                "Condition": {
                    "IpAddress": {
                        "aws:SourceIp": "100.200.100.200/32"
                    }
                }
            },
            {
                "Effect": "Allow",
                "Principal": "*",
                "Action": "execute-api:Invoke",
                "Resource": "arn:aws:execute-api:ap-south-1:480833364711:q9omcoj8ba/alpha/GET/*"
            }
        ]
    }
    

    This works like a charm!