ami: imdsv2 support

https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/

One has to get a token now via PUT. This is because there is a bunch of
open proxies out there which blindly forwarded everything to internal network
including metadata requests. They have found that PUT requests don't cleanly
proxy and also AWS rejects token requests with X-Forwarded-For.
This commit is contained in:
Girish Ramakrishnan
2024-07-26 18:59:32 +02:00
parent 6056ba6475
commit 468d4dd9b0
2 changed files with 14 additions and 1 deletions

View File

@@ -50,7 +50,16 @@ async function providerTokenAuth(req, res, next) {
if (system.getProvider() === 'ami') {
if (typeof req.body.providerToken !== 'string' || !req.body.providerToken) return next(new HttpError(400, 'providerToken must be a non empty string'));
const [error, response] = await safe(superagent.get('http://169.254.169.254/latest/meta-data/instance-id').timeout(30 * 1000).ok(() => true));
// IMDSv2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-options.html
// https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/
const imdsIp = req.body.ipv4Config?.provider === 'noop' ? '[fd00:ec2::254]' : '169.254.169.254'; // use ipv4config carefully, it's not validated yet at this point
const [tokenError, tokenResponse] = await safe(superagent.put(`http://${imdsIp}/latest/api/token`).set('x-aws-ec2-metadata-token-ttl-seconds', 600).timeout(30 * 1000).ok(() => true));
if (tokenError) return next(new HttpError(422, `Network error getting EC2 metadata session token: ${tokenError.message}`));
if (tokenResponse.status !== 200) return next(new HttpError(422, `Unable to get EC2 meta data session token. statusCode: ${tokenResponse.status}`));
const imdsToken = tokenResponse.text;
const [error, response] = await safe(superagent.get(`http://${imdsIp}/latest/meta-data/instance-id`).set('x-aws-ec2-metadata-token', imdsToken).timeout(30 * 1000).ok(() => true));
if (error) return next(new HttpError(422, `Network error getting EC2 metadata: ${error.message}`));
if (response.status !== 200) return next(new HttpError(422, `Unable to get EC2 meta data. statusCode: ${response.status}`));
if (response.text !== req.body.providerToken) return next(new HttpError(422, 'Instance ID does not match'));