diff --git a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts index 0167e1cddd8e..86c28ea6b79e 100644 --- a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts +++ b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts @@ -57,13 +57,17 @@ export interface EndpointsResolvedConfig extends Required export const resolveEndpointsConfig = ( input: T & EndpointsInputConfig & PreviouslyResolved -): T & EndpointsResolvedConfig => ({ - ...input, - tls: input.tls ?? true, - endpoint: input.endpoint - ? normalizeEndpoint({ ...input, endpoint: input.endpoint }) - : () => getEndpointFromRegion(input), - isCustomEndpoint: input.endpoint ? true : false, - useDualstackEndpoint: normalizeBoolean(input.useDualstackEndpoint!), - useFipsEndpoint: normalizeBoolean(input.useFipsEndpoint!), -}); +): T & EndpointsResolvedConfig => { + const useDualstackEndpoint = normalizeBoolean(input.useDualstackEndpoint!); + const useFipsEndpoint = normalizeBoolean(input.useFipsEndpoint!); + return { + ...input, + tls: input.tls ?? true, + endpoint: input.endpoint + ? normalizeEndpoint({ ...input, endpoint: input.endpoint }) + : () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }), + isCustomEndpoint: input.endpoint ? true : false, + useDualstackEndpoint, + useFipsEndpoint, + }; +}; diff --git a/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.spec.ts b/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.spec.ts index 1d0dcc7efb96..7e40182a21a8 100644 --- a/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.spec.ts +++ b/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.spec.ts @@ -4,8 +4,16 @@ describe(getEndpointFromRegion.name, () => { const mockRegion = jest.fn(); const mockUrlParser = jest.fn(); const mockRegionInfoProvider = jest.fn(); + const mockUseFipsEndpoint = jest.fn(); + const mockUseDualstackEndpoint = jest.fn(); - const mockInput = { region: mockRegion, urlParser: mockUrlParser, regionInfoProvider: mockRegionInfoProvider }; + const mockInput = { + region: mockRegion, + urlParser: mockUrlParser, + regionInfoProvider: mockRegionInfoProvider, + useDualstackEndpoint: mockUseDualstackEndpoint, + useFipsEndpoint: mockUseFipsEndpoint, + }; const mockRegionValue = "mockRegion"; const mockEndpoint = { @@ -19,6 +27,8 @@ describe(getEndpointFromRegion.name, () => { mockRegion.mockResolvedValue(mockRegionValue); mockUrlParser.mockResolvedValue(mockEndpoint); mockRegionInfoProvider.mockResolvedValue(mockRegionInfo); + mockUseFipsEndpoint.mockResolvedValue(false); + mockUseDualstackEndpoint.mockResolvedValue(false); }); afterEach(() => { @@ -28,7 +38,10 @@ describe(getEndpointFromRegion.name, () => { describe("tls", () => { afterEach(() => { - expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue); + expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, { + useDualstackEndpoint: false, + useFipsEndpoint: false, + }); }); it("uses protocol https when not defined", async () => { @@ -81,14 +94,20 @@ describe(getEndpointFromRegion.name, () => { } catch (error) { expect(error.message).toEqual(errorMsg); } - expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue); + expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, { + useDualstackEndpoint: false, + useFipsEndpoint: false, + }); expect(mockUrlParser).not.toHaveBeenCalled(); }); it("returns parsed endpoint", async () => { const endpoint = await getEndpointFromRegion(mockInput); expect(endpoint).toEqual(mockEndpoint); - expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue); + expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue, { + useDualstackEndpoint: false, + useFipsEndpoint: false, + }); expect(mockUrlParser).toHaveBeenCalledWith(`https://${mockRegionInfo.hostname}`); }); }); diff --git a/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.ts b/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.ts index 5125f53df345..8483cf6e30d4 100644 --- a/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.ts +++ b/packages/config-resolver/src/endpointsConfig/utils/getEndpointFromRegion.ts @@ -5,6 +5,8 @@ interface GetEndpointFromRegionOptions { tls?: boolean; regionInfoProvider: RegionInfoProvider; urlParser: UrlParser; + useDualstackEndpoint: Provider; + useFipsEndpoint: Provider; } export const getEndpointFromRegion = async (input: GetEndpointFromRegionOptions) => { @@ -16,7 +18,9 @@ export const getEndpointFromRegion = async (input: GetEndpointFromRegionOptions) throw new Error("Invalid region in client config"); } - const { hostname } = (await input.regionInfoProvider(region)) ?? {}; + const useDualstackEndpoint = await input.useDualstackEndpoint(); + const useFipsEndpoint = await input.useFipsEndpoint(); + const { hostname } = (await input.regionInfoProvider(region, { useDualstackEndpoint, useFipsEndpoint })) ?? {}; if (!hostname) { throw new Error("Cannot resolve hostname from client config"); } diff --git a/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.spec.ts b/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.spec.ts index 77c0f3376499..c17fa6d2c5f5 100644 --- a/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.spec.ts +++ b/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.spec.ts @@ -34,6 +34,7 @@ describe("bucketEndpointMiddleware", () => { .fn() .mockResolvedValue({ hostname: "foo.us-foo-2.amazonaws.com", partition: "aws-foo", signingRegion: mockRegion }), useArnRegion: jest.fn().mockResolvedValue(false), + useFipsEndpoint: () => Promise.resolve(false), useDualstackEndpoint: () => Promise.resolve(false), }; diff --git a/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.ts b/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.ts index 3a01e05e4ed6..02effb668114 100644 --- a/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.ts +++ b/packages/middleware-bucket-endpoint/src/bucketEndpointMiddleware.ts @@ -31,9 +31,11 @@ export const bucketEndpointMiddleware = } else if (validateArn(bucketName)) { const bucketArn = parseArn(bucketName); const clientRegion = getPseudoRegion(await options.region()); - const { partition, signingRegion = clientRegion } = (await options.regionInfoProvider(clientRegion)) || {}; + const useDualstackEndpoint = await options.useDualstackEndpoint(); + const useFipsEndpoint = await options.useFipsEndpoint(); + const { partition, signingRegion = clientRegion } = + (await options.regionInfoProvider(clientRegion, { useDualstackEndpoint, useFipsEndpoint })) || {}; const useArnRegion = await options.useArnRegion(); - const dualstackEndpoint = await options.useDualstackEndpoint(); const { hostname, bucketEndpoint, @@ -43,7 +45,7 @@ export const bucketEndpointMiddleware = bucketName: bucketArn, baseHostname: request.hostname, accelerateEndpoint: options.useAccelerateEndpoint, - dualstackEndpoint, + dualstackEndpoint: useDualstackEndpoint, pathStyleEndpoint: options.forcePathStyle, tlsCompatible: request.protocol === "https:", useArnRegion, diff --git a/packages/middleware-bucket-endpoint/src/configurations.ts b/packages/middleware-bucket-endpoint/src/configurations.ts index d6fa52bf2ca0..e1bb03f0d2d2 100644 --- a/packages/middleware-bucket-endpoint/src/configurations.ts +++ b/packages/middleware-bucket-endpoint/src/configurations.ts @@ -28,6 +28,7 @@ interface PreviouslyResolved { isCustomEndpoint: boolean; region: Provider; regionInfoProvider: RegionInfoProvider; + useFipsEndpoint: Provider; useDualstackEndpoint: Provider; } @@ -49,6 +50,10 @@ export interface BucketEndpointResolvedConfig { * Resolved value for input config {@link BucketEndpointInputConfig.useAccelerateEndpoint} */ useAccelerateEndpoint: boolean; + /** + * Enables FIPS compatible endpoints. + */ + useFipsEndpoint: Provider; /** * Enables IPv6/IPv4 dualstack endpoint. */ diff --git a/packages/middleware-sdk-s3-control/src/configurations.ts b/packages/middleware-sdk-s3-control/src/configurations.ts index 96bdf7a0f873..f6509fd204ea 100644 --- a/packages/middleware-sdk-s3-control/src/configurations.ts +++ b/packages/middleware-sdk-s3-control/src/configurations.ts @@ -12,6 +12,7 @@ interface PreviouslyResolved { isCustomEndpoint: boolean; region: Provider; regionInfoProvider: RegionInfoProvider; + useFipsEndpoint: Provider; useDualstackEndpoint: Provider; } @@ -21,6 +22,10 @@ export interface S3ControlResolvedConfig { * @internal */ isCustomEndpoint: boolean; + /** + * Enables FIPS compatible endpoints. + */ + useFipsEndpoint: Provider; /** * Enables IPv6/IPv4 dualstack endpoint. */ diff --git a/packages/middleware-sdk-s3-control/src/process-arnables-plugin/parse-outpost-arnables.ts b/packages/middleware-sdk-s3-control/src/process-arnables-plugin/parse-outpost-arnables.ts index 21b3bc2194d6..1632da8fdf8a 100644 --- a/packages/middleware-sdk-s3-control/src/process-arnables-plugin/parse-outpost-arnables.ts +++ b/packages/middleware-sdk-s3-control/src/process-arnables-plugin/parse-outpost-arnables.ts @@ -36,11 +36,14 @@ export const parseOutpostArnablesMiddleaware = if (!parameter) return next(args); const clientRegion = await options.region(); - const { regionInfoProvider } = options; const useArnRegion = await options.useArnRegion(); + const useFipsEndpoint = await options.useFipsEndpoint(); const useDualstackEndpoint = await options.useDualstackEndpoint(); const baseRegion = getPseudoRegion(clientRegion); - const { partition: clientPartition, signingRegion = baseRegion } = (await regionInfoProvider(baseRegion))!; + const { partition: clientPartition, signingRegion = baseRegion } = (await options.regionInfoProvider(baseRegion, { + useFipsEndpoint, + useDualstackEndpoint, + }))!; const validatorOptions: ValidateOutpostsArnOptions = { useDualstackEndpoint, clientRegion, diff --git a/packages/middleware-sdk-s3-control/src/process-arnables-plugin/plugin.spec.ts b/packages/middleware-sdk-s3-control/src/process-arnables-plugin/plugin.spec.ts index bb69e89238ff..a0bea2141dfe 100644 --- a/packages/middleware-sdk-s3-control/src/process-arnables-plugin/plugin.spec.ts +++ b/packages/middleware-sdk-s3-control/src/process-arnables-plugin/plugin.spec.ts @@ -10,11 +10,13 @@ describe("getProcessArnablesMiddleware", () => { region: string; regionInfoProvider?: Provider; useAccelerateEndpoint?: boolean; + useFipsEndpoint?: Provider; useDualstackEndpoint?: Provider; useArnRegion?: boolean; }; const setupPluginOptions = (options: FakeOptions): S3ControlResolvedConfig => { return { + useFipsEndpoint: () => Promise.resolve(false), useDualstackEndpoint: () => Promise.resolve(false), ...options, regionInfoProvider: options.regionInfoProvider ?? jest.fn().mockResolvedValue({ partition: "aws" }), diff --git a/packages/middleware-sdk-sts/src/index.ts b/packages/middleware-sdk-sts/src/index.ts index 01230da21ae6..53ebed0d4fa0 100644 --- a/packages/middleware-sdk-sts/src/index.ts +++ b/packages/middleware-sdk-sts/src/index.ts @@ -2,6 +2,7 @@ import { AwsAuthInputConfig, AwsAuthResolvedConfig, resolveAwsAuthConfig } from import { Client, Credentials, HashConstructor, Pluggable, Provider, RegionInfoProvider } from "@aws-sdk/types"; export interface StsAuthInputConfig extends AwsAuthInputConfig {} + interface PreviouslyResolved { credentialDefaultProvider: (input: any) => Provider; region: string | Provider; @@ -9,7 +10,10 @@ interface PreviouslyResolved { signingName?: string; serviceId: string; sha256: HashConstructor; + useFipsEndpoint: Provider; + useDualstackEndpoint: Provider; } + export interface StsAuthResolvedConfig extends AwsAuthResolvedConfig { /** * Reference to STSClient class constructor. diff --git a/packages/middleware-signing/src/configuration.spec.ts b/packages/middleware-signing/src/configuration.spec.ts index cdf6b41ed8c7..6734ac6dd6b4 100644 --- a/packages/middleware-signing/src/configuration.spec.ts +++ b/packages/middleware-signing/src/configuration.spec.ts @@ -3,7 +3,6 @@ import { HttpRequest } from "@aws-sdk/protocol-http"; import { resolveAwsAuthConfig, resolveSigV4AuthConfig } from "./configurations"; describe("AuthConfig", () => { - describe("resolveAwsAuthConfig", () => { const inputParams = { credentialDefaultProvider: () => () => Promise.resolve({ accessKeyId: "key", secretAccessKey: "secret" }), @@ -15,6 +14,8 @@ describe("AuthConfig", () => { digest: jest.fn().mockReturnValue("SHA256 hash"), }), credentials: jest.fn().mockResolvedValue({ accessKeyId: "key", secretAccessKey: "secret" }), + useFipsEndpoint: () => Promise.resolve(false), + useDualstackEndpoint: () => Promise.resolve(false), }; beforeEach(() => { diff --git a/packages/middleware-signing/src/configurations.ts b/packages/middleware-signing/src/configurations.ts index ae99d2e91802..ca09d5ad8770 100644 --- a/packages/middleware-signing/src/configurations.ts +++ b/packages/middleware-signing/src/configurations.ts @@ -1,6 +1,7 @@ import { memoize } from "@aws-sdk/property-provider"; import { SignatureV4, SignatureV4CryptoInit, SignatureV4Init } from "@aws-sdk/signature-v4"; import { Credentials, HashConstructor, Provider, RegionInfo, RegionInfoProvider, RequestSigner } from "@aws-sdk/types"; +import { options } from "yargs"; // 5 minutes buffer time the refresh the credential before it really expires const CREDENTIAL_EXPIRE_WINDOW = 300000; @@ -73,6 +74,8 @@ interface PreviouslyResolved { signingName?: string; serviceId: string; sha256: HashConstructor; + useFipsEndpoint: Provider; + useDualstackEndpoint: Provider; } interface SigV4PreviouslyResolved { @@ -118,7 +121,16 @@ export const resolveAwsAuthConfig = ( //construct a provider inferring signing from region. signer = () => normalizeProvider(input.region)() - .then(async (region) => [(await input.regionInfoProvider(region)) || {}, region] as [RegionInfo, string]) + .then( + async (region) => + [ + (await input.regionInfoProvider(region, { + useFipsEndpoint: await input.useFipsEndpoint(), + useDualstackEndpoint: await input.useDualstackEndpoint(), + })) || {}, + region, + ] as [RegionInfo, string] + ) .then(([regionInfo, region]) => { const { signingRegion, signingService } = regionInfo; //update client's singing region and signing service config if they are resolved.