Initializing Enclave...

How to Fix API Gateway Lambda Execution Role Missing Permissions (lambda:InvokeFunction Error)

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins

TL;DR

  • What broke: API Gateway's execution role (or the Lambda resource-based policy) is missing lambda:InvokeFunction, causing every API call to return 500 Internal Server Error with AccessDeniedException in CloudWatch.
  • How to fix it: Either add lambda:InvokeFunction to the API Gateway IAM execution role scoped to the target Lambda ARN, or add a Lambda resource-based policy granting apigateway.amazonaws.com invoke rights.
  • Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your IAM policy or Terraform aws_iam_policy_document block and get the corrected output instantly.

The Incident (What Does the Error Mean?)

Raw error surfaced in CloudWatch Logs for the API Gateway stage:

{
  "message": "Wed, 05 Jun 2024 14:22:11 GMT : Execution failed due to configuration error: 
              Invalid permissions on Lambda function",
  "status": 500
}

And in the Lambda invocation log or X-Ray trace:

User: arn:aws:sts::123456789012:assumed-role/APIGatewayExecutionRole/AmazonAPIGateway-my-api
is not authorized to perform: lambda:InvokeFunction 
on resource: arn:aws:lambda:us-east-1:123456789012:function:my-function
because no identity-based policy allows the lambda:InvokeFunction action

Immediate consequence: 100% of requests to this API stage fail. No traffic reaches Lambda. The API is fully down.


The Attack Vector / Blast Radius

This is a misconfiguration causing a full service outage, not a security vulnerability in the traditional sense — but the blast radius is total for the affected API stage.

Cascading failure chain:

  1. API Gateway attempts to assume its execution role and call lambda:InvokeFunction.
  2. IAM denies the call — implicit deny wins over any resource-based policy that may exist.
  3. API Gateway returns 500 to the client. No retry logic at the gateway layer rescues this.
  4. If this API backs a downstream service (e.g., a mobile app, a webhook receiver, a B2B integration), all dependent systems fail simultaneously.

Why this happens in production unexpectedly:

  • Terraform aws_lambda_permission resource was removed or never applied.
  • The Lambda function was redeployed with a new ARN (e.g., alias or version change) and the permission was not updated.
  • IAM role was rotated or recreated during a security remediation and the lambda:InvokeFunction statement was dropped.
  • Cross-account Lambda invocation where the resource-based policy principal is wrong.

How to Fix It (The Solution)

There are two valid trust models for API Gateway invoking Lambda. Use one, not both inconsistently.

Option A — Lambda Resource-Based Policy (Recommended, Least Privilege)

This grants API Gateway permission at the Lambda resource level. No changes to the execution role needed.

AWS CLI:

- # No resource-based policy exists — Lambda rejects the invocation
+ aws lambda add-permission \
+   --function-name my-function \
+   --statement-id apigateway-invoke-permission \
+   --action lambda:InvokeFunction \
+   --principal apigateway.amazonaws.com \
+   --source-arn "arn:aws:execute-api:us-east-1:123456789012:abc123def4/*/POST/my-resource"

Terraform (Enterprise Best Practice — scope to exact method/path ARN):

- resource "aws_lambda_permission" "apigw" {
-   statement_id  = "AllowAPIGatewayInvoke"
-   action        = "lambda:InvokeFunction"
-   function_name = aws_lambda_function.my_function.function_name
-   principal     = "apigateway.amazonaws.com"
-   # MISSING: source_arn — allows ANY API Gateway in the account to invoke
- }
+ resource "aws_lambda_permission" "apigw" {
+   statement_id  = "AllowAPIGatewayInvoke"
+   action        = "lambda:InvokeFunction"
+   function_name = aws_lambda_function.my_function.function_name
+   principal     = "apigateway.amazonaws.com"
+   source_arn    = "${aws_api_gateway_rest_api.my_api.execution_arn}/*/*"
+ }

Option B — IAM Execution Role Policy (When Using IAM Auth on the Integration)

If your API Gateway integration type is AWS with credentials pointing to an IAM role:

  {
    "Version": "2012-10-17",
    "Statement": [
-     {
-       "Effect": "Allow",
-       "Action": "lambda:*",
-       "Resource": "*"
-     }
+     {
+       "Effect": "Allow",
+       "Action": "lambda:InvokeFunction",
+       "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
+     }
    ]
  }

The - example above also shows a common over-permissioning anti-pattern (lambda:* on *) that passes IAM Access Analyzer but violates least privilege. Lock it to the exact function ARN and lambda:InvokeFunction only.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Checkov — catch missing source_arn on Lambda permissions:

# .checkov.yml
checks:
  - CKV_AWS_45   # Ensure Lambda function has a resource-based policy
  - CKV_AWS_116  # Ensure Lambda permission has source_arn condition

2. OPA/Conftest policy — enforce source_arn is always set:

package terraform.aws.lambda_permission

deny[msg] {
  r := input.resource.aws_lambda_permission[name]
  not r.source_arn
  msg := sprintf("aws_lambda_permission.%v must define source_arn to prevent confused deputy attacks", [name])
}

3. Terraform plan validation in CI:

# In your GitHub Actions / GitLab CI pipeline
terraform plan -out=tfplan
checkov -f tfplan --framework terraform_plan --check CKV_AWS_116

4. AWS Config Rule — continuous compliance: Enable lambda-function-public-access-prohibited and write a custom Config rule that alerts when any Lambda function has Principal: "*" without a Condition block scoping the aws:SourceArn.

5. Integration test in pipeline: After terraform apply in staging, run a smoke test that actually invokes the API endpoint and asserts HTTP 200. A missing permission is caught in staging, not prod.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →