Отказ от необходимости включения заголовка x-amz-cf-id в аутентификацию S3 в облачной среде
У меня не совсем ортодоксальная настройка CF->S3. Соответствующие компоненты здесь:
Распределение Cloudfront с происхождением s3.ap-southeast-2.amazonaws.com
Функция Lambda@Edge (Исходный запрос), которая добавляет строку запроса авторизации S3 (версия 2) (Подписано с использованием политики S3, используемой функцией).
Запрос, возвращенный из Lambda, полностью верен. Если я регистрирую URI, хост и строку запроса, я получаю файл, который запрашиваю. Однако, если я обращаюсь к нему через ссылку Cloudfront напрямую, запрос не выполняется, поскольку он больше не использует AWSAccessKeyID, вместо этого он выбирает использовать x-amz-cf-id
(но использует ту же подпись, Amz-Security-Token и т. д.). ИСПРАВЛЕНИЕ: оно не может заменить, но может потребоваться в дополнение к.
Я знаю, что это так, потому что я вернул оба StringToSign
и SignatureProvided
, Они оба соответствуют лямбда-ответу, за исключением AWSAccessKeyID, который был заменен на x-amz-cf-id
,
Это очень специфический вопрос, очевидно. Возможно, мне придется взглянуть на модернизацию этой архитектуры, но я бы предпочел не делать этого. Есть несколько требований, которые привели меня к этой не совсем обычной установке.
4 ответа
Так что похоже на аутентификацию V2 или V4, x-amz-cf-id
заголовок, который добавляется к запросу источника и недоступен функцией запроса источника Lambda@Edge, должен быть включен в строку аутентификации. Это невозможно.
Простое решение состоит в том, чтобы использовать встроенную интеграцию S3 в Cloudflare, использовать функцию запроса происхождения Lambda@Edge, которая переключает ведро, если, как и я, это ваша желаемая цель. Для каждого сегмента, который вы хотите использовать, добавьте следующую политику, чтобы ваш дистрибутив CF имел доступ к объектам в сегменте.
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <CloudfrontID>"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<bucket-name>/*"
}
]
}
CloudfrontID
ссылается на идентификатор в Origin Access Identity, а не на Amazon S3 Canonical ID.
У меня была аналогичная задача вернуть подписанный S3 URL-адрес из запроса источника CloudFront [email protected].[защищенный электронной почтой][email protected] Вот что я нашел:
Если в имени вашей корзины S3 нет точек, вы можете использовать происхождение S3 в CloudFront, использовать доменное имя в форме <bucket_name>.s3.<region>.amazonaws.com и сгенерировать подписанный URL-адрес, например, через getSignedUrl из @ aws-sdk/s3-request-presigner. CloudFront должен быть настроен для передачи URL-запроса в источник. В этом случае не предоставляйте CloudFront доступ к корзине S3 : предварительно заданный URL-адрес предоставит доступ к корзине.
Однако, если в имени вашей корзины есть точки, URL-адрес со знаком, созданный функцией, будет иметь URL -адрес в стиле пути, и вам потребуется использовать пользовательский источник CloudFront с доменом s3.<region>.amazonaws.com. При использовании пользовательского источника CloudFront добавляет заголовок «x-amz-cf-id» в запрос к источнику. Довольно неудобно, что значение заголовка должно быть подписано . Однако, если вы не меняете исходный домен в возвращаемом значении , CloudFront, похоже, использует то же значение для заголовка «x-amz-cf-id», которое передается в событие лямбда вevent.Records[0].cf.config.requestId
поле. Затем вы можете сгенерировать подписанный S3 URL-адрес со значением заголовка. С помощью AWS JavaScript SDK v3 это можно сделать с помощью S3Client.middlewareStack.add.
Вот пример JavaScript , создающий подписанный S3 URL-адрес с заголовком «x-amz-cf-id»:
const {S3Client, GetObjectCommand} = require("@aws-sdk/client-s3");
const {getSignedUrl} = require("@aws-sdk/s3-request-presigner");
exports.handler = async function handler(event, context) {
console.log('Request: ', JSON.stringify(event));
let bucketName = 'XXX';
let fileName = 'XXX';
let bucketRegion = 'XXX';
// Pre-requisite: this Lambda@Edge function has 's3:GetObject' permission for bucket ${bucketName}, otherwise you will get AccessDenied
const command = new GetObjectCommand({
Bucket: bucketName, Key: fileName,
});
const s3Client = new S3Client({region: bucketRegion});
s3Client.middlewareStack.add((next, context) => async (args) => {
args.request.headers["x-amz-cf-id"] = event.Records[0].cf.config.requestId;
return await next(args);
}, {
step: "build", name: "addXAmzCfIdHeaderMiddleware",
});
let signedS3Url = await getSignedUrl(s3Client, command, {
signableHeaders: new Set(["x-amz-cf-id"]), unhoistableHeaders: new Set(["x-amz-cf-id"])
});
let parsedUrl = new URL(signedS3Url);
const request = event.Records[0].cf.request;
if (!request.origin.custom || request.origin.custom.domainName != parsedUrl.hostname) {
return {
status: '500',
body: `CloudFront should use custom origin configured to the matching domain '${parsedUrl.hostname}'.`,
headers: {
'content-type': [{key: 'Content-Type', value: 'text/plain; charset=UTF-8',}]
}
};
}
request.querystring = parsedUrl.search.substring(1); //drop '?'
request.uri = parsedUrl.pathname;
console.log('Response: ', JSON.stringify(request));
return request;
}
Я верю AWSAccessKeyID
=> x-amz-cf-id
Замена является результатом двух механизмов:
Во-первых, вам необходимо настроить CloudFront для пересылки параметров запроса источнику. Без этого будут удалены все параметры. Если вы используете подписанные URL-адреса S3, не забудьте также кэшировать на основе всех параметров, иначе вы останетесь без какого-либо контроля доступа.
Во-вторых, CloudFront подключает x-amz-cf-id
к запросам , которые не отправляются в источник S3. Вы можете дважды проверить в консоли CloudFront тип источника и убедиться, что он указан как S3. У меня есть сообщение в блоге, в котором это подробно описано.
Но добавление подписи S3 ко всем запросам с помощью Lambda@Edge лишает смысла. Если вы хотите сохранить сегмент приватным и разрешить доступ к нему только CloudFront, используйте Origin Access Identity, это как раз для случая использования.
X-amz-cf-id - это зарезервированный заголовок CF, который может быть получен событием как event['Records'][0]['cf']['config']['requestId']. Вам не нужно рассчитывать аутентификацию V4 с X-amz-cf-id.