AWS CloudFront
Lambda@Edge functions to ingest traffic from AWS CloudFront distributions.
How It Works
Levo provides two Lambda functions — a Request Handler and a Response Handler — that are associated with a CloudFront distribution's cache behavior. As traffic flows through CloudFront, the functions forward API request/response data to the Levo Satellite.
Client ──> CloudFront Distribution
│
├─ Origin request ──> Request Handler (Lambda@Edge)
├─ Origin response ──> Response Handler (Lambda@Edge)
│ │
└──> Your Origin └──> Levo Satellite
(directly, or via an ALB
if in a private subnet)
- Lambda@Edge functions must be created in
us-east-1. CloudFront replicates them to edge locations automatically. - Lambda@Edge functions cannot be attached to a VPC. They run outside your network, so the Satellite endpoint they send traffic to must be reachable over the public internet (via HTTPS). If your Satellite runs in a private subnet, see Exposing a Private Satellite below.
- Each cache behavior supports one Lambda@Edge association per event type (e.g., one
Origin requestand oneOrigin response). If your behavior already has Lambda@Edge functions attached for these event types, see Alternatives below.
Installation
Pre-requisites
- Install the AWS CLI (version 2) by following the AWS docs.
- You have sufficient permissions on AWS to create and deploy Lambda@Edge functions (including creating the IAM role that allows the
lambda.amazonaws.comandedgelambda.amazonaws.comservice principals to assume it). - Your AWS CLI is configured for the
us-east-1region (required for Lambda@Edge). - The Satellite has been successfully set up and is reachable (via HTTPS) from the Lambda@Edge functions. Since Lambda@Edge runs outside your VPC, this means the Satellite endpoint must be publicly reachable — either directly, or through a load balancer as described below.
Creating the Lambda Functions using the AWS CLI
- Obtain your organization's ID from the settings page or by clicking on your profile picture in Levo's dashboard and navigating to
User Settings -> Organizations - Run the
install.shscript in the repository.
git clone https://github.com/levoai/aws-cloudfront-lambda
cd aws-cloudfront-lambda
LEVO_ORG_ID=<value> LEVO_SATELLITE_URL=<satellite-url> ./install.sh
The script prints the Request Handler ARN and Response Handler ARN. Keep these handy for the next step.
Exposing a Private Satellite via an ALB
If your Satellite runs on an EC2 instance (or other compute) in a private subnet, the Lambda@Edge functions cannot reach it directly — they run outside your VPC. Front the Satellite with an internet-facing Application Load Balancer (ALB):
- Create an internet-facing ALB in public subnets of the same VPC as the Satellite.
- Provision an ACM certificate for a custom domain you control (for example,
satellite.example.com). ACM does not issue certificates for the AWS-generated*.elb.amazonaws.comhostname, so a custom domain is required for TLS to validate. Request or import the certificate in the same region as the ALB. - Create a target group pointing at the Satellite instance and port (the Satellite's HTTPS listener), and configure a health check against the Satellite's health endpoint.
- Add an HTTPS listener on the ALB using the ACM certificate from step 2, forwarding to the target group.
- Point a DNS record at the ALB: create a Route 53 alias (or a CNAME) for your custom domain that resolves to the ALB's DNS name.
- Allow the ALB to reach the Satellite: the Satellite instance's security group should accept inbound traffic on the Satellite port from the ALB's security group.
- Use your custom domain (with HTTPS) as the
LEVO_SATELLITE_URLwhen runninginstall.sh— for example,https://satellite.example.com. Do not use the raw*.elb.amazonaws.comhostname: the ALB presents the certificate from step 2, so a request to the AWS-generated hostname fails TLS validation and the handlers cannot connect.
Securing the ALB
The Lambda@Edge handlers POST captured traffic directly to LEVO_SATELLITE_URL (specifically POST /v1/cloudfront-event) from the AWS-managed Lambda execution environment in each edge region. This egress does not flow through CloudFront, so it is not covered by the com.amazonaws.global.cloudfront.origin-facing managed prefix list — that list only describes CloudFront's own origin-fetch IPs, and CloudFront never contacts the Satellite ALB. Restricting the ALB's security group to that prefix list (or any fixed source-IP allowlist) would block the handlers, because Lambda@Edge egress spans AWS-managed ranges that are impractical to enumerate.
Secure the ALB at the application layer instead of by source IP:
- Terminate TLS at the ALB with the ACM certificate above so all captured traffic is encrypted in transit.
- Scope the listener/routing rules to the handler endpoint — the handlers only issue
POST /v1/cloudfront-event. - Add AWS WAF to the ALB to enforce access rules. The handlers send an
x-levo-organization-idheader, but this is an identifier rather than a secret. For stronger authentication, ask support@levo.ai about configuring the handlers to send a secret header that you can require with a WAF rule.
Do not rely on source-IP allowlisting — including the CloudFront origin-facing prefix list — to secure the Satellite ALB. Lambda@Edge egress is not covered by it, and allowlisting it would block traffic. Use TLS plus application-layer controls (AWS WAF, scoped listener rules) instead.
Associating the Lambdas with a CloudFront Distribution
- Go to the AWS CloudFront Console and select your distribution.
- Click on the "Behaviors" tab, then click on the "Create Behaviour" button.
- Configure the behaviour and ensure that the following properties are set:
- Path pattern: Use
*to send all JSON payloads to Levo, or use a more specific API pattern - Origin and origin groups: The origin for which the traffic should be sent
- Allowed HTTP methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
- Cache policy: Set this to any policy as per your requirements
- Function Associations
Origin request:- Function type:
Lambda@Edge - Function ARN: Paste the "Request Handler ARN" value printed by the
install.shscript - Include body:
Yes
- Function type:
Origin response:- Function type:
Lambda@Edge - Function ARN: Paste the "Response Handler ARN" value printed by the
install.shscript
- Function type:
- Path pattern: Use
- Click on the "Create behaviour" button to save the configuration.
That's all! Within a few minutes, you should start seeing API catalogs in your Levo dashboard.
Lambda@Edge Origin response triggers do not expose the response body — CloudFront only makes request bodies available (via Include body: Yes on the request trigger). The Response Handler captures response status codes and headers. If full response payloads are required for your use case, consider an in-VPC sensor such as the eBPF Sensor at the origin instead, or contact support@levo.ai.
Alternatives
Behavior already has Lambda@Edge associations
A cache behavior allows only one Lambda@Edge function per event type. If your Origin request or Origin response slots are already occupied by existing functions, you have two options:
- Merge handlers: Combine your existing logic and Levo's handler into a single Lambda function per event type. Contact support@levo.ai for guidance.
- Use a separate behavior: Create a more specific path-pattern behavior for the API routes you want Levo to observe, and attach Levo's functions there.
CloudFront Functions
CloudFront Functions are a lighter-weight (and significantly cheaper) alternative to Lambda@Edge for viewer-side logic. However, they cannot access request or response bodies and cannot make network calls, so they are not suitable for forwarding API payloads to the Satellite on their own. They can be useful in combination with Lambda@Edge when the viewer-event association slots are contended.
Troubleshooting
install.shfails creating functions: Confirm your CLI region isus-east-1and that your IAM principal can create Lambda functions and IAM roles.- Distribution update fails with an association error: The behavior likely already has a Lambda@Edge function on the same event type. See Alternatives above.
- No traffic appears in Levo:
- Check the Lambda functions' CloudWatch logs. Note that Lambda@Edge logs are written in the region closest to the edge location that served the request, not necessarily
us-east-1— check log groups named/aws/lambda/us-east-1.<function-name>across regions. - Verify the Satellite (or its ALB) is reachable over HTTPS from outside your VPC, and that the ALB target group health checks are passing.
- Confirm
LEVO_SATELLITE_URLuses the certificate-covered custom domain (not the raw*.elb.amazonaws.comhostname), or the handlers' HTTPS requests will fail TLS validation. - Confirm the ALB is reachable from the public internet over HTTPS, that any WAF rules are not rejecting the handlers'
POST /v1/cloudfront-eventrequests, and that the Satellite's security group allows inbound traffic from the ALB. Do not restrict the ALB to the CloudFront prefix list — see Securing the ALB.
- Check the Lambda functions' CloudWatch logs. Note that Lambda@Edge logs are written in the region closest to the edge location that served the request, not necessarily
Please contact support@levo.ai if you need help with this setup.