⚠️ Status: untested. This extension is provided as-is and has not been tested in production. Please feel free to fork, modify, improve, and open pull requests.Licensed under GNU GPLv3 (see LICENSE).
Screens CloudFront traffic against ip-block.com at the edge, returning a 403 (or redirect) for blocked visitors and letting clean requests continue to your origin.
- Trigger: CloudFront Viewer Request
- Runtime: Node.js 20.x Lambda@Edge (
us-east-1, then replicated) - Client IP source:
request.clientIp - Decision cache: warm-container in-memory map (per-IP)
| File | Purpose |
|---|---|
index.js |
The Lambda@Edge handler (screen -> allow/block) |
README.md |
This document (deploy + IAM steps) |
CloudFront offers two edge compute options:
- CloudFront Functions - ultra-light JS, runs on every request, but the
sandbox cannot make network calls (no
fetch/http, no external I/O). It therefore cannot call the ip-block.com API. Use it only for header/URL rewrites. - Lambda@Edge - full Node.js with outbound networking. This is required to call an external screening API, so this integration uses Lambda@Edge on the viewer-request trigger.
- Reads the real client IP from
request.clientIp. - Skips the lookup for whitelisted IPs and when
enabled=false. - Checks an in-process cache (persists while the edge container stays warm).
- On a miss,
POSTs tohttps://api.ip-block.com/v1/checkwith a ~1s timeout using the Nodehttpsmodule, then caches the decision forcacheTtls. - Blocks only when the API returns
{"action":"block"}. Any error, timeout, non-2xx, or missingactionfails open unlessfailOpen=false.
Lambda@Edge does not support environment variables, so configuration is
inline in the CONFIG object at the top of index.js. Edit it before publishing:
| Field | Default | Meaning |
|---|---|---|
enabled |
true |
Master on/off switch |
siteId |
- | Your ip-block.com site id |
apiKey |
- | Your API key |
apiUrl |
https://api.ip-block.com/v1/check |
Screening endpoint |
failOpen |
true |
Allow on error/timeout |
cacheTtl |
300 |
Seconds to cache a per-IP decision |
timeoutMs |
1000 |
API timeout (~1s) |
blockAction |
"403" |
"403" or "redirect" |
redirectUrl |
https://www.ip-block.com/blocked.php |
Used when blockAction="redirect" |
whitelist |
[] |
IPs to always allow |
Prefer not to inline the key? At cold start, read it from AWS Secrets Manager or SSM Parameter Store instead (adds a little latency; grant the role access).
Lambda@Edge functions must be created in us-east-1 and published as a
numbered version (aliases and $LATEST are not allowed on the trigger).
# 1. Package the function
cd extensions/AWS-CloudFront
zip -r function.zip index.js
# 2. Create the function in us-east-1
aws lambda create-function \
--function-name ip-block-protection \
--runtime nodejs20.x \
--handler index.handler \
--role arn:aws:iam::<ACCOUNT_ID>:role/ip-block-edge-role \
--zip-file fileb://function.zip \
--region us-east-1
# 3. Publish an immutable version (note the returned Version number)
aws lambda publish-version \
--function-name ip-block-protection \
--region us-east-1Then attach it to your distribution: CloudFront console -> your distribution ->
Behaviors -> edit the behavior -> Function associations ->
Viewer request = Lambda@Edge -> paste the versioned ARN
(arn:aws:lambda:us-east-1:<ACCOUNT_ID>:function:ip-block-protection:<VERSION>)
-> save and wait for the distribution to deploy.
Updating: edit index.js, update-function-code, publish-version, then point
the behavior at the new version ARN.
Execution role (ip-block-edge-role) trust policy must allow both edge
services to assume it:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": ["lambda.amazonaws.com", "edgelambda.amazonaws.com"]
},
"Action": "sts:AssumeRole"
}
]
}Attach AWSLambdaBasicExecutionRole for CloudWatch Logs (logs are written in
each edge Region). Add secretsmanager:GetSecretValue / ssm:GetParameter
only if you choose to load the API key from a secret store.
Put a CloudFront distribution in front of the SaaS custom domain (origin = the platform's hostname) and attach this function to the viewer-request event. Every visitor is screened at the edge before CloudFront forwards to the origin.