Comment puis-je configurer une API HTTP pour autoriser l'accès uniquement à des adresses IP spécifiques ?

Lecture de 5 minute(s)
0

Je souhaite intégrer une API HTTP à une fonction AWS Lambda pour autoriser et autoriser uniquement des adresses IP spécifiques.

Brève description

Pour restreindre l'accès á l’API HTTP à des adresses IP spécifiques et prédéfinies, déployez une fonction Lambda en tant que mécanisme d'autorisation. La fonction Lambda effectue la validation des adresses IP pour déterminer si les requêtes entrantes proviennent de sources approuvées. Le mécanisme d'autorisation accorde ou refuse ensuite l'accès à l'API HTTP en fonction de l'adresse IP.

Résolution

Créer une fonction Lambda

Tout d'abord, créez une fonction Lambda à utiliser comme mécanisme d’autorisation. Vous pouvez configurer la fonction Lambda pour utiliser des variables d'environnement pour une liste d'autorisations :

Key: IP_RANGE, Value: ['0.0.0.0','XX.XX.XX.XX']

La valeur peut être soit une adresse IP spécifique que vous souhaitez autoriser, soit une plage d'adresses CIDR complète.

Après avoir déployé le code, configurez les variables d'environnement pour la fonction Lambda afin de définir une variable spécifique comme IP_RANGE.

Pour des raisons de sécurité, il est recommandé de chiffrer les variables d'environnement avec AWS Key Management Service (AWS KMS).

Créer une API HTTP

Créez une API HTTP à utiliser comme point d'entrée pour les requêtes entrantes. Les API HTTP étant publiques par défaut, utilisez la fonction Lambda d'autorisation pour contrôler l'accès.

Attacher le mécanisme d’autorisation Lambda

Associez la fonction Lambda en tant que mécanisme d'autorisation à l'API. Lorsqu'une requête atteint l'API, le mécanisme d'autorisation évalue l'adresse IP source par rapport à la liste autorisée. Si le mécanisme d’autorisation autorise l'adresse IP, il accorde l'accès au point de terminaison de l'API HTTP. Dans le cas contraire, il refuse la requête.

Pour évaluer les adresses IP en fonction de vos variables d'environnement autorisées, utilisez l'exemple de fonction Lambda suivant :

Remarque : Cet exemple transmet un jeton d'autorisation dont la valeur est secretcode. Toutefois, vous pouvez configurer ces paramètres pour utiliser une autre valeur.

# -*- 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

Remarque : Si vous nécessitez des modules Python supplémentaires, utilisez pip pour installer les modules dans l'environnement de la fonction Lambda. Packagez la fonction Lambda avec les dépendances nécessaires.

Tester votre mécanisme d’autorisation

Avant de déployer votre fonction pour la production, assurez-vous de la tester dans un environnement de test. Pour vérifier les adresses IP auxquelles vous souhaitez accéder au point de terminaison de l'API HTTP, utilisez la journalisation des accès. Utilisez des variables de contexte pour identifier une adresse IP et incluez-la dans votre liste d'autorisations via la variable d’environnement IP_RANGE. L'exemple suivant inclut des variables de contexte pour la journalisation des accès dans l'API HTTP :

{"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"}

La variable ip":"$context.identity.sourceIp fournit l'adresse IP qui demande le point de terminaison de l'API. Cela vous permet d'identifier les adresses IP et de les ajouter à votre liste d'autorisation.

AWS OFFICIEL
AWS OFFICIELA mis à jour il y a un an