How do I configure an HTTP API to allow access only to specific IP addresses?
I want to integrate an HTTP API with an AWS Lambda function to authorize and allow only specific IP addresses.
Short description
To restrict HTTP API access to specific, predefined IP addresses, deploy a Lambda function as an authorizer. The Lambda function performs IP address validation to determine if incoming requests originate from approved sources. The authorizer then grants or denies access to the HTTP API based on the IP address.
Resolution
Create a Lambda Function
First, create a Lambda function to use as the authorizer. You can configure the Lambda function to use environment variables for an allowlist:
Key: IP_RANGE, Value: ['0.0.0.0','XX.XX.XX.XX']
The Value can be either a specific IP address that you want to allow or a complete CIDR range.
After you deploy the code, configure environment variables for the Lambda function to define a specific variable as IP_RANGE.
For security purposes, it's a best practice to encrypt the environment variables with AWS Key Management Service (AWS KMS).
Create an HTTP API
Create an HTTP API to use as the entry point for incoming requests. Because HTTP APIs are public by default, use the authorizer Lambda function to control access.
Attach the Lambda authorizer
Associate the Lambda function as an authorizer to the API. When a request reaches the API, the authorizer evaluates the source IP address against the allowlist. If the authorizer allows the IP address, then it grants access to the HTTP API endpoint. Otherwise, it denies the request.
To evaluate IP addresses based on your allowlisted environment variables, use the following example Lambda function:
Note: This example passes an authorizationtoken with a value of secretcode. However, you can configure these parameters to use another value.
# -*- coding: utf-8 -*- # ======================== # AWS Lambda Python Runtime # ======================== # Import necessary modules import os from ipaddress import ip_network, ip_address import uuid import ast # Function to check if an IP address is within the specified IP range def check_ip(IP_ADDRESS, IP_RANGE): VALID_IP = False # Check if any of the elements in IP_RANGE contain a CIDR notation (e.g., "192.168.0.0/24") cidr_blocks = list(filter(lambda element: "/" in element, IP_RANGE)) if cidr_blocks: for cidr in cidr_blocks: # Create an IP network from the CIDR notation net = ip_network(cidr) # Check if the IP_ADDRESS is within the network VALID_IP = ip_address(IP_ADDRESS) in net if VALID_IP: break # If the IP_ADDRESS is not within any CIDR block, check if it matches an exact IP in IP_RANGE if not VALID_IP and IP_ADDRESS in IP_RANGE: VALID_IP = True return VALID_IP # Lambda function handler def lambda_handler(event, context): # Extract the source IP address from the request's RequestContext IP_ADDRESS = event["requestContext"]["http"]["sourceIp"] # Parse the IP_RANGE environment variable, which is a list of allowed IP addresses or CIDR blocks IP_RANGE = ast.literal_eval(os.environ.get("IP_RANGE", "[]")) # Check if the source IP is within the allowed IP range VALID_IP = check_ip(IP_ADDRESS, IP_RANGE) # Extract relevant information from the request event # Extract the AWS API Gateway's API ID from the event API_ID = event["requestContext"]["apiId"] # Extract the AWS account ID from the event ACC_ID = event["requestContext"]["accountId"] # Extract the AWS API Gateway's Details Method, Stage and Route from the event METHOD = event["requestContext"]["http"]["method"] STAGE = event["requestContext"]["stage"] ROUTE = event["requestContext"]["http"]["path"] # Check if the request includes a valid authorization token and the source IP is allowed # Use the {region} based on the region of the API Gateway's configuration. if event["headers"]["authorizationtoken"] == "secretcode" and VALID_IP: # If authorized, allow the execution of the API response = { "principalId": f"{uuid.uuid4().hex}", "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow", "Resource": f"arn:aws:execute-api:{region}:{ACC_ID}:{API_ID}/{STAGE}/{METHOD}{ROUTE}", } ], }, "context": {"exampleKey": "exampleValue"}, } return response # If the request is not authorized or the IP is not allowed, deny execution # Use the Acc_ID, region & API_ID as per the HTTP API ID Details response = { "principalId": f"{uuid.uuid4().hex}", "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Deny", "Resource": f"arn:aws:execute-api:{region}:{ACC_ID}:{API_ID}/*/*/*", } ], }, "context": {"exampleKey": "exampleValue"}, } return response
Note: If you require additional Python modules, then use pip to install the modules in the Lambda function's environment. Package the Lambda function with the necessary dependencies.
Test your authorizer
Before you deploy your function for production, be sure to test the function in a testing environment. To check the IP addresses that you want to access the HTTP API endpoint, use access logging. Use context variables to identify an IP address, and include the address in your allowlist through the IP_RANGE environment variable. The following example includes context variables for access logging in HTTP API:
{"ownerAccountId":"$context.accountId","apiId":"$context.apiId","requestId":"$context.requestId", / "ip":"$context.identity.sourceIp","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod", / "routeKey":"$context.routeKey","status":"$context.status","protocol":"$context.protocol", / "responseLength":"$context.responseLength","errorMessage":"$context.error.message", / "errorMessageString":"$context.error.messageString","extendedRequestId":"$context.extendedRequestId", / "responseLatency":"$context.responseLatency","stage":"$context.stage","path":"$context.path", / "requestTimeEpoch":"$context.requestTimeEpoch","protocol":"$context.protocol","userAgent":"$context.identity.userAgent", / "errorResponseType":"$context.error.responseType","integrationStatus":"$context.integration.status", / "integrationErrorMessage":"$context.integrationErrorMessage","integrationRequestId":"$context.integration.requestId", / "integrationLatency":"$context.integration.latency","integrationStatusfromIntegration":"$context.integration.integrationStatus", / "integrationError":"$context.integration.error","awsEndpointRequestId":"$context.awsEndpointRequestId", / "customAuthorizerUser":"$context.identity.user","authorizerProperty":"$context.authorizer.property", / "customAuthorizerprincipalId":"$context.authorizer.principalId","authorizerError":"$context.authorizer.error"}
The variable ip":"$context.identity.sourceIp provides the IP address that requests the API endpoint. This allows you to identify IP addresses and add them to your allowlist.
Relevant content
- asked 2 years agolg...
- asked a year agolg...
- asked 3 months agolg...
- asked a year agolg...
- asked a month agolg...
- AWS OFFICIALUpdated 2 years ago
- AWS OFFICIALUpdated 5 months ago
- AWS OFFICIALUpdated 24 days ago
- AWS OFFICIALUpdated 2 years ago