Created
March 18, 2023 07:33
-
-
Save jbg/781792a2ecf2068c24c5ec7cf971a8e4 to your computer and use it in GitHub Desktop.
A Lambda@Edge function that signs the origin request with SigV4A, allowing the use of an S3 Multi-Region Access Point as a custom origin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CrtSignerV4 } from "@aws-sdk/signature-v4-crt"; | |
const allowedHeaders = [ | |
"accept-encoding", | |
"content-length", | |
"if-modified-since", | |
"if-none-match", | |
"if-range", | |
"if-unmodified-since", | |
"transfer-encoding", | |
"via", | |
]; | |
const signer = new CrtSignerV4({ | |
credentials: { | |
accessKeyId: process.env["AWS_ACCESS_KEY_ID"], | |
secretAccessKey: process.env["AWS_SECRET_ACCESS_KEY"], | |
sessionToken: process.env["AWS_SESSION_TOKEN"], | |
}, | |
region: "*", | |
service: "s3", | |
signingAlgorithm: 1, // SigV4Asymmetric | |
}); | |
export const handler = async (event) => { | |
const request = event.Records[0].cf.request; | |
const originHostname = request.origin.custom.domainName; | |
// Allow failover: if the origin is not a MRAP, skip SigV4A signing. | |
if (!originHostname.endsWith(".mrap.accesspoint.s3-global.amazonaws.com")) { | |
return request; | |
} | |
// X-Amz-Cf-Id will be added to the origin request after this function finishes. | |
// Therefore, we have to include it in the signature in order for the signature | |
// to verify successfully at the origin. However, we have to remove it from the | |
// headers we return below, as we're not allowed to set this header. | |
const cfRequestId = event.Records[0].cf.config.requestId; | |
const headersToSign = { | |
"Host": originHostname, | |
"X-Amz-Cf-Id": cfRequestId, | |
}; | |
for (const key of allowedHeaders) { | |
if (key in request.headers) { | |
const header = request.headers[key][0]; | |
headersToSign[header.key] = header.value; | |
} | |
} | |
const signedRequest = await signer.sign({ | |
method: request.method, | |
protocol: "https", | |
hostname: originHostname, | |
path: request.uri, | |
headers: headersToSign, | |
}); | |
const signedHeaders = {}; | |
for (const [key, value] of Object.entries(signedRequest.headers)) { | |
if (key != "X-Amz-Cf-Id") { | |
signedHeaders[key.toLowerCase()] = [{key, value}]; | |
} | |
} | |
request.headers = signedHeaders; | |
// We ignore the query string. If you choose to pass it through, add it to | |
// the signer.sign() call above. | |
delete request.querystring; | |
return request; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment