Skip to content

Instantly share code, notes, and snippets.

@brandonpapworth
Last active March 8, 2021 14:20
Show Gist options
  • Save brandonpapworth/5bb083a81be44e7ee2424e0aee000e0d to your computer and use it in GitHub Desktop.
Save brandonpapworth/5bb083a81be44e7ee2424e0aee000e0d to your computer and use it in GitHub Desktop.
Simple "what's-my-ip" service (think ipify) via Lambda@Edge

Simple "what's-my-ip" service

via Lambda@Edge

Usage

This will return the IPv4 or IPv6 address of the client making the GET request to the domain you specified when making the CloudFront distribution. If you add an Accept header with application/json present, it will return an json-parseable object like { "ip": "127.0.0.1" }, but if not specified or text/plain appears before application/json, it will default to text/plain and just return the IP address like 127.0.0.1 (following standard handling of Accept headers).

Shitty Directions

  • Add the source of index.js to your AWS account
    • This file is written with speed in mind. Lambda@Edge tends to bounce execution around between different workers, which means you'll encounter lots of cold-starts. Not being able to rely on V8's JIT to make your oh-so-pretty code fast, some V8 optimization tricks are worth employing (var is still faster than let and const, and functions are faster than arrows () => {})
  • Configure it as you like (i.e. amount of memory (I use 1024mb which is hella excessive)
  • Create a Cloudfront distribution, set it up as you'd like
    • Have the default origin point to an empty, dummy S3 bucket (we won't be using this as this is purely ran by Lambda, but we need an origin to bind a behavior to)
    • Viewer Protocol Policy: Redirect HTTP to HTTPS
    • Allowed HTTP Methods: GET, HEAD
    • Cache and origin request settings: Use a cache policy and origin request policy
    • Cache Policy: see Cache Policy below
    • Origin Request Policy: see Origin Request Policy below
    • Compress Objects Automatically: Yes
    • Lambda Function Associations: Origin Request and put the Lamba function you created's ARN in, hit the plus button
  • Deploy and you're done!

Cache Policies

Cache Policy

  • TTL Settings (set these to whatever you're comfortable with
  • Headers: Whitelist
    • Accept
    • X-Forwarded-For you have to manually enter this one
  • Cookies: None
  • Query strings: None
  • Compression Support: Gzip and Brotli because why not?

Origin Request Policy

  • Headers: Whitelist
    • Accept
    • X-Forwarded-For you have to manually enter this one
  • Cookies: None
  • Query strings: None
'use strict';
var HEADER_CONTENT_TYPE = 'Content-Type';
var CF_HEADER_OBJ_KEY_CONTENT_TYPE = HEADER_CONTENT_TYPE.toLowerCase();
var MIME_TYPES = {
JSON: 'application/json',
PLAIN: 'text/plain'
};
var DEFAULT_MIME_TYPE = MIME_TYPES.PLAIN;
var CONTENT_TYPE_OPTS = Object.assign(Object.create(null), {
[MIME_TYPES.JSON]: {
responseHeader: [{
key: HEADER_CONTENT_TYPE,
value: MIME_TYPES.JSON
}],
responseWrapper: {
prefix: '{"ip":"',
suffix: '"}'
}
},
[MIME_TYPES.PLAIN]: {
responseHeader: [{
key: HEADER_CONTENT_TYPE,
value: MIME_TYPES.PLAIN
}]
}
});
var mimeTypeRegexp = new RegExp(`((${MIME_TYPES.PLAIN})|(${MIME_TYPES.JSON}))`);
var DEFAULT_IF_MATCH_NULLISH = [MIME_TYPES.PLAIN];
exports.handler = async function handler(event) {
var req = event.Records[0].cf.request;
var contentTypeOpts = CONTENT_TYPE_OPTS[DEFAULT_MIME_TYPE];
try {
contentTypeOpts = CONTENT_TYPE_OPTS[req.headers.accept
? (
(mimeTypeRegexp.exec(req.headers.accept[0].value.toLowerCase()) || DEFAULT_IF_MATCH_NULLISH)[0]
|| DEFAULT_MIME_TYPE
)
: DEFAULT_MIME_TYPE
];
if (!contentTypeOpts) {
contentTypeOpts = CONTENT_TYPE_OPTS[DEFAULT_MIME_TYPE];
}
} catch(err) {
console.error('ERROR attempting to extract mimeType from req.headers.accept', err);
contentTypeOpts = CONTENT_TYPE_OPTS[DEFAULT_MIME_TYPE];
}
return {
status: '200',
statusDescription: 'OK',
body: CONTENT_TYPE_OPTS.responseWrapper
? (CONTENT_TYPE_OPTS.responseWrapper.prefix + req.clientIp + CONTENT_TYPE_OPTS.responseWrapper.suffix)
: req.clientIp,
headers: {
[CF_HEADER_OBJ_KEY_CONTENT_TYPE]: contentTypeOpts.responseHeader
}
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment