似乎无法调用通过 CloudFront 分配启用了 AWS_IAM 保护的 REST API。
以下是重现此问题的方法:
- 使用 API Gateway 创建 REST API
- 通过 AWS_IAM 身份验证保护 REST API 方法
- 创建面向 REST API 的 CloudFront 分配
- 在 Route 53 中创建面向 CloudFront 分配的 A 记录
现在使用经过身份验证的用户(我使用 Cognito UserPool 用户和 aws-amplify)来调用
- protected REST API 方法及其 API 网关 URL = SUCCESS
- 通过 CloudFront 分发 URL = FAILURE 的 protected REST API 方法
- 通过 Route 53 域 URL = FAILURE 的 protected REST API 方法
我收到的错误是:
{"message":"我们计算的请求签名与您提供的签名不匹配。请检查您的 AWS secret 访问 key 和签名方法。有关详细信息,请参阅服务文档。"}
我简直不敢相信 AWS 不支持自定义域后面受 AWS_IAM 保护的端点,因为这肯定是一个非常非常常见的用例。
因此,您能否向我提供如何实现这一目标的详细列表?
谢谢
最佳答案
CloudFront 不支持对访问分配的调用进行 IAM 身份验证。正如其他人所强调的那样,SigV4 依赖于主机 header ,并且无法在访问您的域时计算签名(无需执行一些黑客操作,例如在客户端硬编码 API 网关域,然后使用该 header 进行 SigV4)。不过,您可以使用 Lambda@Edge 函数将 IAM 从您的发行版添加到您的 API。
假设您已将 API Gateway 设置为 CloudFront 分配的源,则需要设置 Lambda@Edge function拦截原始请求,然后使用 SigV4 对其进行签名这样您就可以限制您的 API Gateway 只能通过 CloudFront 访问。
普通 HTTP 请求和 CloudFront event format 之间存在相当多的转换。但这一切都是可控的。
首先,创建 Lambda@Edge 函数 ( guide ),然后确保其执行角色有权访问您想要访问的 API 网关。为简单起见,您可以在 Lambda 执行角色中使用 AmazonAPIGatewayInvokeFullAccess
托管 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;
关于aws-sdk - CloudFront后面的API网关不支持AWS_IAM身份验证吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48815143/