可以通过对SigV4签名源请求使用Lambda@Edge 函数强制通过 CloudFront 进行访问,然后在您的 API 网关上启用 IAM 身份验证。此策略可以与 CloudFront 分配中的 API 密钥结合使用(CloudFront+API 密钥指南)。
假设您已经将 API Gateway 设置为 CloudFront 分配的源,您首先需要创建一个 Lambda@Edge 函数(Lambda@Edge 设置指南),然后确保其执行角色可以访问您想要的 API Gateway访问。为简单起见,您可以AmazonAPIGatewayInvokeFullAccess
在您的 Lambda 执行角色中使用托管 IAM 策略,使其能够访问您账户中的任何 API 网关。
然后,如果您使用aws4作为您的签名客户端,您的 Lambda@Edge 代码将如下所示:
const aws4 = require("aws4");
const signCloudFrontOriginRequest = (request) => {
const searchString = request.querystring === "" ? "" : `?${request.querystring}`;
// Utilize a dummy request because the structure of the CloudFront origin request
// is different than the signing client expects
const dummyRequest = {
host: request.origin.custom.domainName,
method: request.method,
path: `${request.origin.custom.path}${request.uri}${searchString}`,
};
// Include the body in the signature if present
if (Object.hasOwnProperty.call(request, 'body')) {
const { data, encoding } = request.body;
const buffer = Buffer.from(data, encoding);
const decodedBody = buffer.toString('utf8');
if (decodedBody !== '') {
dummyRequest.body = decodedBody;
dummyRequest.headers = { 'content-type': request.headers['content-type'][0].value };
}
}
// Use the Lambda's execution role credentials
const credentials = {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
sessionToken: process.env.AWS_SESSION_TOKEN
};
aws4.sign(dummyRequest, credentials); // Signs the dummyRequest object
// Sign a clone of the CloudFront origin request with appropriate headers from the signed dummyRequest
const signedRequest = JSON.parse(JSON.stringify(request));
signedRequest.headers.authorization = [ { key: "Authorization", value: dummyRequest.headers.Authorization } ];
signedRequest.headers["x-amz-date"] = [ { key: "X-Amz-Date", value: dummyRequest.headers["X-Amz-Date"] } ];
signedRequest.headers["x-amz-security-token"] = [ { key: "X-Amz-Security-Token", value: dummyRequest.headers["X-Amz-Security-Token"] } ];
return signedRequest;
};
const handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const signedRequest = signCloudFrontOriginRequest(request);
callback(null, signedRequest);
};
module.exports.handler = handler;
请注意,如果您在请求中包含正文,则必须手动配置 Lambda@Edge 函数以通过控制台或 SDK 包含正文,或设置CloudFormation 自定义资源以调用 SDK,因为CloudFormation 尚不支持本机启用此功能