Je souhaite placer WAF devant API Gateway, et avec le (petit) info je trouve que ce n'est possible qu'en plaçant manuellement une distribution Cloudfront supplémentaire avec WAF activé, en face de APIG. C'est un peu dommage, d'autant plus qu'APIG prend désormais en charge les domaines personnalisés de manière native, mais cela devrait fonctionner.
Maintenant, pour que la solution soit sécurisée plutôt que simplement obscure, je souhaite faire en sorte que les API ne puissent être accessibles que via la distribution Cloudfront. Quelle est la meilleure option pour faire cela?
De meilleures idées? Ou peut-être que "la bonne façon" de le faire existe mais je l'ai oublié?
Je viens de API Gateway.
Malheureusement, la meilleure solution à l'heure actuelle consiste à injecter un en-tête personnalisé Origin dans CloudFront et à le valider dans un autoriseur personnalisé (option 4 de votre question).
Nous sommes déjà conscients de cette limitation et de cette solution de contournement pas si grande. Nous cherchons à fournir une meilleure intégration du WAF à l’avenir, mais nous n’avons pas d’ETA.
La "bonne" méthode consisterait à utiliser l'auteur automatique dans API Gateway, comme mentionné par d'autres.
Le moyen "bon marché" serait la puce 3, une clé API. Vous ne fourniriez probablement que waf -> cloudfront -> api gateway si vous tentiez de vous protéger d'une attaque par DDOS. Donc, si quelqu'un découvre l'URL de votre passerelle d'api et décide de ne pas utiliser Cloudfront, un autorisateur personnalisé signifie que vous subissez maintenant les conséquences de l'attaque de lambda. La passerelle Api peut traiter plus de 10 000 requêtes par seconde, la limite lambda par défaut est de 100 par seconde. Même si vous avez Amazon pour augmenter votre limite, êtes-vous prêt à payer 10 000 lambda par seconde pour une attaque soutenue?
Les représentants AWS vous diront: "Les clés d'API servent à l'identification, pas à l'authentification. Elles ne sont pas utilisées pour signer des demandes et ne doivent pas être utilisées comme mécanisme de sécurité" https://aws.Amazon.com/blogs/ aws/new-usage-plans-pour-Amazon-api-gateway/
Mais honnêtement, si vous ne faites pas quelque chose de mieux dans votre lambda que de valider une ficelle gigantesque, pourquoi ne pas laisser ce fardeau et ce coût à quelqu'un d'autre. (La longueur maximale de la clé est de 128 caractères)
Vous pourriez peut-être avoir une fonction lambda planifiée pour émettre une nouvelle clé d’API et mettre à jour l’en-tête de Cloudfront toutes les 6 heures?
Si vous souhaitez utiliser les clés d'api pour d'autres tâches, utilisez une passerelle api Origin pour l'authentification, ainsi qu'une autre passerelle Origin et api pour tout le reste. De cette manière, dans une attaque par DDOS, vous pouvez traiter 10 000 requêtes par seconde sur votre API authentifiée, tandis que tous les autres clients déjà connectés disposent d’un total de 10 000 collectives par seconde pour utiliser votre API. Cloudfront et waf peuvent gérer 100 Ko par seconde afin de ne pas vous retenir dans ce scénario.
Une autre chose à noter si vous utilisez lambda derrière une passerelle api, vous pouvez utiliser lambda @ Edge et simplement ignorer la passerelle api ensemble. (Cela ne conviendra pas à la plupart des scénarios car lambda @ Edge est extrêmement limité, mais j’ai pensé que je le lancerais là-bas.)
Mais finalement WE BESOIN DE L’INTÉGRATION DE WAF AVEC API GATEWAY !! :)
Vous pouvez utiliser un nom de domaine personnalisé et pointer DNS vers la distribution avec WAF. Les demandes directement à la distribution API Gateway d'origine ne fonctionneront pas alors.
Il est possible de forcer l'accès via CloudFront en utilisant une fonction Lambda @ Edge pour SigV4 signature des requêtes Origin, puis en activant l'authentification IAM sur votre passerelle API. Cette stratégie peut être utilisée conjointement avec les clés API de votre distribution CloudFront ( guide pour CloudFront + API Key ).
En supposant que vous ayez déjà configuré API Gateway en tant qu’origine pour votre distribution CloudFront, vous devez d’abord créer une fonction Lambda @ Edge ( guide pour Lambda @ Edge setup ), puis vous assurer que son rôle d’exécution a accès à la passerelle API. auquel vous souhaitez accéder. Pour plus de simplicité, vous pouvez utiliser la stratégie IAM AmazonAPIGatewayInvokeFullAccess
gérée dans le rôle d'exécution de votre Lambda, ce qui lui permet d'accéder à toutes les passerelles API de votre compte.
Ensuite, si vous utilisez aws4 comme client de signature, votre code Lambda @ Edge ressemblera à ceci:
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;
Notez que si vous incluez un corps dans votre demande, vous devez configurer manuellement votre fonction Lambda @ Edge pour inclure le corps via la console ou le SDK ou configurer une ressource personnalisée CloudFormation pour appeler le SDK depuis CloudFormation ne supporte pas encore l'activer nativement