AWS API Gateway

Configuring Revenium Metering for the AWS API Gateway

Revenium is capable of monitoring and mediating AWS API Gateway traffic by leveraging CloudWatch and Lambda functions.

Configuring the AWS API Gateway for Metering

To configure the AWS Gateway for metering you'll need to do the following:

  • Create a CloudWatch log group for API Gateway access logs

  • Enable and configure access logs on all metered API stages

  • Create and attach the Metering Lambda Function to the CloudWatch log group

The following guide outlines the process of configuring Revenium metering using the AWS Console. Additionally, this configuration can be automated using CloudFormation, Terraform, or similar Infrastructure as Code (IaC) tools. Here is a starter CloudFormation template to help you get going.: https://github.com/revenium/revenium-metering-aws/blob/main/revenium_lambda_metering_template.yaml

Create a CloudWatch LogGroup for API Gateway access logs

Follow these steps to create a log group in AWS CloudWatch for capturing API Gateway access logs:

  1. Navigate to the CloudWatch Service

    • Log in to the AWS Management Console.

    • Open the CloudWatch service by searching for it in the search bar or by finding it under the "Services" menu.

  2. Access Log Groups

    • In the CloudWatch dashboard, locate the "Logs" section on the left sidebar.

    • Click on "Log groups" to view existing log groups or create a new one.

  3. Create a New Log Group

    • Click the "Create log group" button.

    • Enter a name for your new log group, such as APIGatewayAccessLogs.

    • (Optional) Set retention settings according to your log retention policy. If unsure, you can leave it as the default setting.

    • Click the "Create" button to create the log group.

This log group will capture and store access logs from your API Gateway, allowing you to monitor and troubleshoot your API's usage and performance.

Enable and configure Access Logs on all metered API stages

To choose an API Gateway deployment stage for metering and activate Access Logs, follow these steps:

  1. Navigate to the API Gateway Console: Log in to your AWS Management Console and go to the API Gateway service.

  2. Select Your API: Identify and click on the API you wish to configure.

  3. Stages: In the left navigation pane, click on Stages under your selected API.

  4. Choose a Stage: Select the deployment stage you want to meter and monitor.

  5. Enable Access Logging: Within the stage editor, click on the Logs/Tracing tab. Then, find the Access Logging section and click on the Edit button.

  6. Specify Log Destination: Enter the ARN (Amazon Resource Name) of the CloudWatch Logs log group where access logs will be stored.

  7. Save Changes: After configuring the settings, click on the Save Changes button to enable access logging for the selected API Gateway deployment stage.

To proceed, link the Amazon Resource Name (ARN) of your CloudWatch log group to serve as the log destination. Additionally, specify the log format to structure the data accordingly.

Create the Metering Lambda Function and configure the CloudWatch Trigger

Now we'll define the Lambda Function and associate a CloudWatch trigger to it from our log group. Select "Python 3.12" as the Runtime.

Now add a CloudWatch trigger:

Next, select the log group you created earlier, and assign a name to your filter:

Now copy and paste the metering function into the Code Source:

import base64
import gzip
import json
import urllib.request
import os


def lambda_handler(event, context):
    cw_data = base64.b64decode(event['awslogs']['data'])
    uncompressed_data = gzip.decompress(cw_data)
    log_data = json.loads(uncompressed_data)

    for log_event in log_data['logEvents']:

        analytics_data = json.loads(log_event['message'])

        payload = {
            "method": analytics_data.get("httpMethod"),
            "url": analytics_data.get("path"),
            "application": analytics_data.get("principalId"),
            "responseCode": analytics_data.get("status"),
            "remoteHost": analytics_data.get("sourceIp"),
            "backendLatency": analytics_data.get("integrationLatency"),
            "responseMessageSize": analytics_data.get("responseLength"),
            "source": "AWS"
        }

        data = json.dumps(payload).encode('utf-8')

        url = os.environ.get('REVENIUM_PLATFORM_API', 'https://api.revenium.io/meter/v1/api/meter')
        api_key = os.environ.get('REVENIUM_PLATFORM_API_KEY', '')

        req = urllib.request.Request(url,
                                     data=data, headers={'Content-Type': 'application/json',
                                                         'x-api-key': api_key}, method='POST')

        try:
            with urllib.request.urlopen(req) as response:
                response_body = response.read().decode('utf-8')
        except urllib.error.URLError as e:
            print("Error:", e.reason)

    return {
        'statusCode': 200,
        'body': json.dumps('Metering complete')
    }

Finally, configure your environment by setting the Revenium API Key as an environment variable.

API traffic passing through this stage will now be metered by Revenium.

Configuring the AWS API Gateway for Authorization

Lambda functions can serve as powerful authorizers, ensuring that client requests meet the required entitlements for accessing specific APIs managed by Revenium. This method provides a secure and efficient way to validate and control access to productized APIs, leveraging the flexible programming model of AWS Lambda to perform authentication and authorization tasks.

To configure an AWS API Gateway route to validate requests using Revenium, complete the following steps:

  • Create an authorizer lambda function

  • Configure the authorizer function on the API route

This method facilitates authorization, presuming that authentication occurs before reaching the API Gateway. However, the function can be adapted to handle authentication as needed.

Create an Authorizer Lambda Function

Create the Lambda function from scratch, set the name and select "Python 3.12" as the runtime:

Now copy and paste the authorization function into the Code Source:

import urllib.request
import os


def lambda_handler(event, context):
    print(event)
    client_id = event['headers'].get('clientid', '')

    has_funds = check_client_funds(client_id)

    if has_funds:
        return generate_policy('user', 'Allow', event['routeArn'], client_id)
    else:
        return generate_policy('user', 'Deny', event['routeArn'], client_id)


def check_client_funds(client_id):
    base_url = os.environ.get('REVENIUM_PLATFORM_API', 'https://api.revenium.io/meter/v1/api/meter')
    url = f"{base_url}/product-key?application={client_id}"
    api_key = os.environ.get('REVENIUM_PLATFORM_API_KEY', '')

    req = urllib.request.Request(url, headers={'x-api-key': api_key}, method='GET')

    print(url)
    try:
        with urllib.request.urlopen(req) as response:
            # Check the response code
            if response.status == 200:
                print("Client ID is valid: " + client_id)
                return True
            else:
                print("Client ID is not valid: " + client_id)
                return False
    except urllib.error.URLError as e:
        print(f"Error: {e.reason}")
        return False


def generate_policy(principal_id, effect, resource, client_id):
    auth_response = {'principalId': principal_id}
    if effect and resource:
        policy_document = {
            'Version': '2012-10-17',
            'Statement': [{
                'Action': 'execute-api:Invoke',
                'Effect': effect,
                'Resource': resource
            }]
        }
        context_map = {
            'clientId': client_id
        }
        auth_response['policyDocument'] = policy_document
        auth_response['context'] = context_map
    return auth_response

Next, configure your environment by setting the Revenium API Key as an environment variable.

Configure the authorizer function on the API route

Finally, you can set up the authorizer function for your API route. You will need to configure the following:

  • Authorizer Type: Lambda

  • Name: The name of the authorizer function (ie, "revenium_client_id_authorizer")

  • Lambda Function: The ARN of the previously created function

  • Response Mode: IAM Policy

  • Authorizer Caching: Enabled (optional but recommended)

    • Authorizer Cache Duration: 30s (or whichever is most appropriate)

    • Identity Sources: $request.header.clientId

Click on "Create and Attach" and requests going through your API will now be authorized by Revenium.

Last updated

© Revenium - www.revenium.io