CloudFront 不支持对命中分配的调用进行 IAM 身份验证。正如其他人所强调的那样,SigV4 依赖于主机标头,并且无法在访问您的域时计算签名(无需做一些骇人听闻的事情,例如在客户端硬编码 API 网关域,然后使用该标头对 SigV4 进行硬编码)。但是,您可以使用 Lambda@Edge 函数将 IAM 从您的分配中添加到您的 API。
假设您已经将 API Gateway 设置为 CloudFront 分配的源,您需要设置一个Lambda@Edge 函数来拦截源请求,然后使用SigV4对其进行签名,这样您就可以将 API Gateway 限制为只能通过 CloudFront 访问。
正常 HTTP 请求和CloudFront 事件格式之间有相当多的转换,但都是可控的。
首先,创建一个 Lambda@Edge 函数 ( guide ),然后确保其执行角色可以访问您想要访问的 API 网关。为简单起见,您可以AmazonAPIGatewayInvokeFullAccess
在您的 Lambda 执行角色中使用托管 IAM 策略,使其能够访问您账户中的任何 API 网关。
然后,如果您使用aws4作为您的签名客户端,您的 lambda 代码将如下所示:
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}`,
};
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;