Skip to content

Commit

Permalink
feat(ec2-metadata-service): add IMDSv1 fallback handling
Browse files Browse the repository at this point in the history
  • Loading branch information
siddsriv committed Mar 15, 2024
1 parent 9852698 commit a8fd616
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 23 deletions.
5 changes: 0 additions & 5 deletions packages/ec2-metadata-service/jest.config.js

This file was deleted.

4 changes: 2 additions & 2 deletions packages/ec2-metadata-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
"tslib": "^2.5.0",
"@smithy/node-http-handler": "^2.3.1",
"@smithy/util-stream": "^2.1.1",
"@aws-sdk/types": "3.515.0",
"@aws-sdk/types": "*",
"@aws-sdk/protocol-http": "3.374.0",
"@smithy/node-config-provider": "2.2.5"
"@smithy/node-config-provider": "^2.2.1"
},
"devDependencies": {
"@tsconfig/recommended": "1.0.1",
Expand Down
8 changes: 5 additions & 3 deletions packages/ec2-metadata-service/src/ConfigLoaders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { LoadedConfigSelectors } from "@smithy/node-config-provider";

import { Endpoint } from "./Endpoint";
import { EndpointMode } from "./EndpointMode";

/**
Expand All @@ -16,7 +18,7 @@ export const CONFIG_ENDPOINT_NAME = "ec2_metadata_service_endpoint";
export const ENDPOINT_SELECTORS: LoadedConfigSelectors<string | undefined> = {
environmentVariableSelector: (env) => env[ENV_ENDPOINT_NAME],
configFileSelector: (profile) => profile[CONFIG_ENDPOINT_NAME],
default: undefined,
default: Endpoint.IPv4,
};

/**
Expand Down Expand Up @@ -50,10 +52,10 @@ export const PROFILE_AWS_EC2_METADATA_V1_DISABLED = "ec2_metadata_v1_disabled";
*/
export const IMDSv1_DISABLED_SELECTORS: LoadedConfigSelectors<boolean | undefined> = {
environmentVariableSelector: (env) =>
env[AWS_EC2_METADATA_V1_DISABLED] ? env[AWS_EC2_METADATA_V1_DISABLED].toLowerCase() !== "false" : undefined,
env[AWS_EC2_METADATA_V1_DISABLED] ? env[AWS_EC2_METADATA_V1_DISABLED] !== "false" : undefined,
configFileSelector: (profile) =>
profile[PROFILE_AWS_EC2_METADATA_V1_DISABLED]
? profile[PROFILE_AWS_EC2_METADATA_V1_DISABLED].toLowerCase() !== "false"
? profile[PROFILE_AWS_EC2_METADATA_V1_DISABLED] !== "false"
: undefined,
default: false,
};
39 changes: 26 additions & 13 deletions packages/ec2-metadata-service/src/MetadataService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HttpRequest } from "@aws-sdk/protocol-http";
import { HttpHandlerOptions } from "@aws-sdk/types";
import { loadConfig } from "@smithy/node-config-provider";
import { NodeHttpHandler } from "@smithy/node-http-handler";
import { sdkStreamMixin } from "@smithy/util-stream";
import { loadConfig } from "@smithy/node-config-provider";

import { ENDPOINT_SELECTORS, IMDSv1_DISABLED_SELECTORS } from "./ConfigLoaders";
import { MetadataServiceOptions } from "./MetadataServiceOptions";

Expand Down Expand Up @@ -33,23 +34,37 @@ export class MetadataService {
async request(
path: string,
options: { method?: string; headers?: Record<string, string> },
withToken?: boolean
withToken?: boolean // withToken should be set to true, if the request is to be made in accordance with IMDSv2
): Promise<string> {
const { endpoint, ec2MetadataV1Disabled } = await this.config;
if (this.disableFetchToken && !withToken && ec2MetadataV1Disabled) {
throw new Error(
"In IMDSv1 fallback mode and ec2MetadataV1Disabled option is set to true, no request can be made."
);
}
const handler = new NodeHttpHandler();
const endpointUrl = new URL(endpoint);
const headers = options.headers || {}; // Using provided headers or default to an empty object
const headers = options.headers || {};

if (this.disableFetchToken && withToken) {
throw new Error("The disableFetchToken option and the withToken argument are both set to true.");
}
/**
* Make request with token.
* If IMDSv1 is disabled and disableFetchToken is true, throw an error
* Refer: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
*/
if (this.disableFetchToken && ec2MetadataV1Disabled) {
throw new Error("IMDSv1 is disabled and fetching token is disabled, cannot make the request.");
}
/**
* Make request with token if disableFetchToken is not true and withToken is true.
* Note that making the request call with token will result in an additional request to fetch the token.
*/
if (withToken) {
headers["x-aws-ec2-metadata-token"] = await this.fetchMetadataToken();
if (withToken && !this.disableFetchToken) {
try {
headers["x-aws-ec2-metadata-token"] = await this.fetchMetadataToken();
} catch (err) {
if (ec2MetadataV1Disabled) {
// If IMDSv1 is disabled and token fetch fails (IMDSv2 fails), rethrow the error
throw err;
}
// If token fetch fails and IMDSv1 is not disabled, proceed without token (IMDSv1 fallback)
}
}
const request = new HttpRequest({
method: options.method || "GET", // Default to GET if no method is specified
Expand All @@ -75,8 +90,6 @@ export class MetadataService {
/**
* Refer: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html
*/

// Define the request to fetch the metadata token
const { endpoint } = await this.config;
const handler = new NodeHttpHandler();
const endpointUrl = new URL(endpoint);
Expand Down

0 comments on commit a8fd616

Please sign in to comment.