API Gateway resource policies are JSON documents that grant or deny access to your API based on criteria like source IP address, time of day, or even specific HTTP headers.
Here’s a real-world example of an API Gateway resource policy in action. Imagine you have a dev-api that you only want accessible from your company’s internal network.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/dev/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"192.168.1.0/24",
"10.0.0.0/16"
]
}
}
}
]
}
This policy explicitly allows execute-api:Invoke actions for any principal (*) on the specified API resource ARN, but only if the request originates from an IP address within the 192.168.1.0/24 or 10.0.0.0/16 ranges. Requests from outside these ranges will be implicitly denied.
The problem this solves is granular, API-level access control that complements IAM permissions. While IAM controls who can call which API Gateway actions (like GET or POST on a specific resource), resource policies control from where or under what conditions those calls are permitted, even if the caller has IAM permissions. This is crucial for security, compliance, and operational control.
Internally, API Gateway evaluates resource policies before it checks IAM policies. If a resource policy explicitly denies access, the request is rejected immediately. If it allows access (or has no specific deny statement for that request), API Gateway then proceeds to check the caller’s IAM permissions. The Principal element in the policy defines who can be granted or denied access. * is a wildcard, meaning any principal. You can also specify AWS accounts or IAM roles here. The Action specifies the API Gateway operation to be controlled, with execute-api:Invoke being the most common for allowing API calls. The Resource element is an ARN that uniquely identifies your API and the specific resources within it that the policy applies to. The Condition block is where the real power lies, allowing you to specify criteria like IpAddress, DateGreaterThan, UserAgent, X-Forwarded-For, and many more.
The aws:SourceIp condition key checks the public IP address of the client making the request. If your API Gateway is behind a NAT gateway or a corporate proxy, the aws:SourceIp will reflect the IP address of that gateway or proxy, not the original client. In such scenarios, you’d need to rely on headers like X-Forwarded-For and use the aws:XfwdIp condition key to inspect the original client IP, though this requires your clients to correctly set this header.
If you’re using a private API endpoint, the aws:SourceVpc or aws:SourceVpce condition keys become incredibly useful for restricting access to specific VPCs or VPC endpoints, effectively creating a zero-trust network boundary around your API.
A common mistake is assuming that an "Allow" statement in a resource policy is sufficient. If there isn’t an explicit "Allow" that matches your request criteria, and there isn’t a broader "Allow" statement that covers it, the request will be implicitly denied. Conversely, an explicit "Deny" statement always overrides any "Allow" statements.
The next concept you’ll likely encounter is how to integrate these resource policies with other AWS security services like AWS WAF for more sophisticated threat protection.