From 50ffd0a6eb937a8da0f6e271f7020adf401e6a6f Mon Sep 17 00:00:00 2001 From: Allan Zheng Date: Thu, 29 Apr 2021 09:09:28 -0700 Subject: [PATCH 1/3] feat(middleware-bucket-endpoint): allow FIPS, disallow global region All ARN-format bucket disallows global client region like s3-external-1 or s3 global endpoint(aws-global). Westeros and Output ARN allows FIPS region as long as the pseudo region compliant with the arn region --- .../src/bucketHostname.spec.ts | 109 +++++++++++------- .../src/bucketHostname.ts | 6 +- .../src/bucketHostnameUtils.ts | 15 ++- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts b/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts index 709b8211de1c..1a22b6562eee 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts @@ -234,37 +234,60 @@ describe("bucketHostname", () => { }); }); - describe("allows different client region with same signing scope", () => { - ["s3-external-1", "s3"].forEach((clientRegion) => { - const baseHostname = `${clientRegion}.amazonaws.com`; - it(`should use client region from base hostname ${baseHostname}`, () => { - const { bucketEndpoint, hostname } = bucketHostname({ - bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), - baseHostname, - isCustomEndpoint: false, - clientRegion: region, - clientSigningRegion: "us-east-1", - }); - expect(bucketEndpoint).toBe(true); - expect(hostname).toBe(`myendpoint-123456789012.s3-accesspoint.${clientRegion}.amazonaws.com`); - }); - }); - - ["s3-external-1", "s3"].forEach((clientRegion) => { - const baseHostname = `${clientRegion}.amazonaws.com`; - it(`should use ARN region with base hostname ${baseHostname}`, () => { - const { bucketEndpoint, hostname } = bucketHostname({ - bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), - baseHostname, - isCustomEndpoint: false, - clientRegion: region, - clientSigningRegion: "us-east-1", - useArnRegion: true, - }); - expect(bucketEndpoint).toBe(true); - expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com"); + describe("validate client region", () => { + [ + { baseHostname: "s3.amazonaws.com", region: "aws-global", signingRegion: "us-east-1" }, + { + baseHostname: "s3-external-1.amazonaws.com", + region: "s3-external-1", + signingRegion: "us-east-1", + }, + ].forEach(({ baseHostname, region, signingRegion }) => { + it(`should throw if supplied with global region ${region}`, () => { + try { + bucketHostname({ + bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), + baseHostname, + isCustomEndpoint: false, + clientRegion: region, + clientSigningRegion: signingRegion, + }); + fail("function should have thrown"); + } catch (e) { + expect(e).toBeDefined(); + } }); }); + // ["s3-external-1", "s3"].forEach((clientRegion) => { + // const baseHostname = `${clientRegion}.amazonaws.com`; + // it(`should use client region from base hostname ${baseHostname}`, () => { + // const { bucketEndpoint, hostname } = bucketHostname({ + // bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), + // baseHostname, + // isCustomEndpoint: false, + // clientRegion: region, + // clientSigningRegion: "us-east-1", + // }); + // expect(bucketEndpoint).toBe(true); + // expect(hostname).toBe(`myendpoint-123456789012.s3-accesspoint.${clientRegion}.amazonaws.com`); + // }); + // }); + + // ["s3-external-1", "s3"].forEach((clientRegion) => { + // const baseHostname = `${clientRegion}.amazonaws.com`; + // it(`should use ARN region with base hostname ${baseHostname}`, () => { + // const { bucketEndpoint, hostname } = bucketHostname({ + // bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), + // baseHostname, + // isCustomEndpoint: false, + // clientRegion: region, + // clientSigningRegion: "us-east-1", + // useArnRegion: true, + // }); + // expect(bucketEndpoint).toBe(true); + // expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com"); + // }); + // }); }); it("should throw if ARN region and client region are incompatible", () => { @@ -554,8 +577,8 @@ describe("bucketHostname", () => { }).toThrow(`Partition in ARN is incompatible, got "aws-cn" but expected "aws"`); }); - describe("not supports fips region", () => { - it("should throw if client region is fips", () => { + describe("fips region", () => { + it("should construct endpoint if client fips region matches arn region", () => { expect.assertions(2); expect(() => { bucketHostname({ @@ -797,18 +820,6 @@ describe("bucketHostname", () => { true, "mybanner-3123456789012.s3-object-lambda.us-east-1.amazonaws.com", ], - [ - "arn:aws:s3-object-lambda:us-east-1:4123456789012:accesspoint/mybanner", - "s3-external-1", - true, - "mybanner-4123456789012.s3-object-lambda.us-east-1.amazonaws.com", - ], - [ - "arn:aws:s3-object-lambda:us-east-1:5123456789012:accesspoint/mybanner", - "aws-global", - true, - "mybanner-5123456789012.s3-object-lambda.us-east-1.amazonaws.com", - ], ]; validLambdaExpectations.forEach((lambdaArn) => { const arn = lambdaArn[0]; @@ -895,6 +906,18 @@ describe("bucketHostname", () => { false, "Invalid ARN, Access Point ARN contains sub resources", ], + [ + "arn:aws:s3-object-lambda:us-east-1:4123456789012:accesspoint/mybanner", + "s3-external-1", + false, + "mybanner-4123456789012.s3-object-lambda.us-east-1.amazonaws.com", + ], + [ + "arn:aws:s3-object-lambda:us-east-1:5123456789012:accesspoint/mybanner", + "aws-global", + false, + "mybanner-5123456789012.s3-object-lambda.us-east-1.amazonaws.com", + ], ]; invalidLambdaConfigurations.forEach((lambdaArn) => { diff --git a/packages/middleware-bucket-endpoint/src/bucketHostname.ts b/packages/middleware-bucket-endpoint/src/bucketHostname.ts index cabdfe9ac43b..5451a55df671 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostname.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostname.ts @@ -9,6 +9,7 @@ import { isDnsCompatibleBucketName, validateAccountId, validateArnEndpointOptions, + validateClientRegion, validateDNSHostLabel, validateNoDualstack, validateNoFIPS, @@ -66,7 +67,7 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo validateService(service); validatePartition(partition, { clientPartition }); validateAccountId(accountId); - validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion }); + validateClientRegion(clientRegion); const { accesspointName, outpostId } = getArnResources(resource); const DNSHostLabel = `${accesspointName}-${accountId}`; validateDNSHostLabel(DNSHostLabel, { tlsCompatible }); @@ -74,6 +75,7 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo const endpointRegion = useArnRegion ? region : clientRegion; const signingRegion = useArnRegion ? region : clientSigningRegion; if (service === "s3-object-lambda") { + validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion, allowFipsRegion: true }); validateNoDualstack(dualstackEndpoint); return { bucketEndpoint: true, @@ -83,6 +85,7 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo }; } else if (outpostId) { // if this is an Outpost ARN + validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion }); validateOutpostService(service); validateDNSHostLabel(outpostId, { tlsCompatible }); validateNoDualstack(dualstackEndpoint); @@ -96,6 +99,7 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo }; } // construct endpoint from Accesspoint ARN + validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion, allowFipsRegion: true }); validateS3Service(service); const hostnamePrefix = `${DNSHostLabel}`; return { diff --git a/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts b/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts index f7b6f9aad079..208c49ec021e 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts @@ -124,6 +124,7 @@ export const validateRegion = ( region: string, options: { useArnRegion?: boolean; + allowFipsRegion?: boolean; clientRegion: string; clientSigningRegion: string; } @@ -131,6 +132,9 @@ export const validateRegion = ( if (region === "") { throw new Error("ARN region is empty"); } + if (!options.allowFipsRegion && isFipsRegion(region)) { + throw new Error("Endpoint does not support FIPS region"); + } if ( !options.useArnRegion && !isEqualRegions(region, options.clientRegion) && @@ -138,8 +142,15 @@ export const validateRegion = ( ) { throw new Error(`Region in ARN is incompatible, got ${region} but expected ${options.clientRegion}`); } - if (options.useArnRegion && isFipsRegion(region)) { - throw new Error("Endpoint does not support FIPS region"); +}; + +/** + * + * @param region + */ +export const validateClientRegion = (region: string) => { + if (["s3-external-1", "aws-global"].includes(getPseudoRegion(region))) { + throw new Error(`Client region ${region} is not regional`); } }; From 35334b6dad85b6223a52e5c4da148e90f416d914 Mon Sep 17 00:00:00 2001 From: Allan Zheng Date: Thu, 13 May 2021 17:31:46 -0700 Subject: [PATCH 2/3] feat(middleware-bucket-endpoint): arn supports fips and handle global regions 3 formats of ARN s3 accepts is touched in this change: Outposts ARN, AccessPoint ARN, and ObjectLambda ARN. Here are the changes: * All of the 3 ARN formats no longer accept s3 global region: s3-global("s3.amazonaws.com"), s3-external-1("s3-external-1.amazonaws.com"). * Outposts ARN no longer support FIPS region, e.g. fips-us-gov-east-1 * AccessPoint ARN accepts FIPS regions with a special endpoint format. * ObjectLambda ARN accepts FIPS regions with a special endpoint format. --- .../src/bucketHostname.spec.ts | 181 +++++++++++------- .../src/bucketHostname.ts | 23 ++- .../src/bucketHostnameUtils.ts | 15 +- 3 files changed, 135 insertions(+), 84 deletions(-) diff --git a/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts b/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts index 1a22b6562eee..6b6fb70befa7 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts @@ -258,36 +258,6 @@ describe("bucketHostname", () => { } }); }); - // ["s3-external-1", "s3"].forEach((clientRegion) => { - // const baseHostname = `${clientRegion}.amazonaws.com`; - // it(`should use client region from base hostname ${baseHostname}`, () => { - // const { bucketEndpoint, hostname } = bucketHostname({ - // bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), - // baseHostname, - // isCustomEndpoint: false, - // clientRegion: region, - // clientSigningRegion: "us-east-1", - // }); - // expect(bucketEndpoint).toBe(true); - // expect(hostname).toBe(`myendpoint-123456789012.s3-accesspoint.${clientRegion}.amazonaws.com`); - // }); - // }); - - // ["s3-external-1", "s3"].forEach((clientRegion) => { - // const baseHostname = `${clientRegion}.amazonaws.com`; - // it(`should use ARN region with base hostname ${baseHostname}`, () => { - // const { bucketEndpoint, hostname } = bucketHostname({ - // bucketName: parseArn("arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint"), - // baseHostname, - // isCustomEndpoint: false, - // clientRegion: region, - // clientSigningRegion: "us-east-1", - // useArnRegion: true, - // }); - // expect(bucketEndpoint).toBe(true); - // expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com"); - // }); - // }); }); it("should throw if ARN region and client region are incompatible", () => { @@ -356,43 +326,75 @@ describe("bucketHostname", () => { describe("allows fips client region", () => { const bucketArn = parseArn("arn:aws-us-gov:s3:us-gov-east-1:123456789012:accesspoint:myendpoint"); + const clientRegion = "fips-us-gov-east-1"; + const clientPartition = "aws-us-gov"; it("should use client region", () => { const { bucketEndpoint, hostname } = bucketHostname({ bucketName: bucketArn, - baseHostname: "s3.fips-us-gov-east-1.amazonaws.com", + baseHostname: `s3.${clientRegion}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: "us-gov-east-1", - clientPartition: "aws-us-gov", + clientRegion, + clientPartition, }); expect(bucketEndpoint).toBe(true); - expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.fips-us-gov-east-1.amazonaws.com"); + expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.us-gov-east-1.amazonaws.com"); }); it("should use ARN region", () => { const { bucketEndpoint, hostname } = bucketHostname({ bucketName: bucketArn, - baseHostname: "s3.fips-us-gov-east-1.amazonaws.com", + baseHostname: `s3.${clientRegion}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: "us-gov-east-1", - clientPartition: "aws-us-gov", + clientRegion, + clientPartition, useArnRegion: true, }); expect(bucketEndpoint).toBe(true); - expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.us-gov-east-1.amazonaws.com"); + expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.us-gov-east-1.amazonaws.com"); }); it("should allow dualstack", () => { const { bucketEndpoint, hostname } = bucketHostname({ bucketName: bucketArn, - baseHostname: "s3.fips-us-gov-east-1.amazonaws.com", + baseHostname: `s3.${clientRegion}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: "us-gov-east-1", - clientPartition: "aws-us-gov", + clientRegion, + clientPartition, useArnRegion: true, dualstackEndpoint: true, }); expect(bucketEndpoint).toBe(true); - expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint.dualstack.us-gov-east-1.amazonaws.com"); + expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com"); + }); + }); + + describe("validates FIPS client region matching ARN region", () => { + const bucketArn = parseArn("arn:aws-us-gov:s3:us-gov-west-1:123456789012:accesspoint:myendpoint"); + const clientRegion = "fips-us-gov-east-1"; + const clientPartition = "aws-us-gov"; + it("should throw client region doesn't match arn region", () => { + expect(() => + bucketHostname({ + bucketName: bucketArn, + baseHostname: `s3.${clientRegion}.amazonaws.com`, + isCustomEndpoint: false, + clientRegion, + clientPartition, + }) + ).toThrowError(); + }); + + it("should throw client region doesn't match arn region and uses ARN region", () => { + expect(() => + bucketHostname({ + bucketName: bucketArn, + baseHostname: `s3.${clientRegion}.amazonaws.com`, + isCustomEndpoint: false, + clientRegion, + clientPartition, + useArnRegion: true, + }) + ).toThrowError(); }); }); @@ -578,32 +580,34 @@ describe("bucketHostname", () => { }); describe("fips region", () => { - it("should construct endpoint if client fips region matches arn region", () => { + it("should throw if client is using fips region", () => { + const clientRegion = "fips-us-gov-east-1"; + const clientPartition = "aws-us-gov"; expect.assertions(2); expect(() => { bucketHostname({ bucketName: parseArn( "arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint" ), - baseHostname: "s3.fips-us-gov-east-1.amazonaws.com", + baseHostname: `s3.${clientRegion}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: "us-gov-east-1", - clientPartition: "aws-us-gov", + clientRegion, + clientPartition, }); - }).toThrow("FIPS region is not supported with Outpost, got fips-us-gov-east-1"); + }).toThrow("FIPS region is not supported"); expect(() => { bucketHostname({ bucketName: parseArn( "arn:aws-us-gov:s3-outposts:fips-us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint" ), - baseHostname: "s3.fips-us-gov-east-1.amazonaws.com", + baseHostname: `s3.${clientRegion}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: "us-gov-east-1", - clientPartition: "aws-us-gov", + clientRegion, + clientPartition, useArnRegion: true, }); - }).toThrow("Endpoint does not support FIPS region"); + }).toThrow("FIPS region is not supported"); }); it("should allow if region is not fips", () => { @@ -801,7 +805,13 @@ describe("bucketHostname", () => { describe("object lambda general test cases", () => { it("should match expectations in valid configurations", () => { - const validLambdaExpectations: [string, string, boolean, string][] = [ + const validLambdaExpectations: [ + arn: string, + clientRegion: string, + useArnRegion: boolean, + expectedEndpoint: string, + clientPartition?: string + ][] = [ [ "arn:aws:s3-object-lambda:us-west-2:1123456789012:accesspoint/mybanner", "us-west-2", @@ -820,26 +830,43 @@ describe("bucketHostname", () => { true, "mybanner-3123456789012.s3-object-lambda.us-east-1.amazonaws.com", ], + [ + "arn:aws-us-gov:s3-object-lambda:us-gov-east-1:123456789012:accesspoint/mybanner", + "fips-us-gov-east-1", + false, + "mybanner-123456789012.s3-object-lambda-fips.us-gov-east-1.amazonaws.com", + "aws-us-gov", + ], + [ + "arn:aws-us-gov:s3-object-lambda:us-gov-east-1:123456789012:accesspoint/mybanner", + "fips-us-gov-east-1", + true, + "mybanner-123456789012.s3-object-lambda-fips.us-gov-east-1.amazonaws.com", + "aws-us-gov", + ], ]; - validLambdaExpectations.forEach((lambdaArn) => { - const arn = lambdaArn[0]; - const region = lambdaArn[1]; - const useArnRegion = lambdaArn[2]; - const exoectedEndpoint = lambdaArn[3]; + validLambdaExpectations.forEach(([arn, clientRegion, useArnRegion, expectedEndpoint, clientPartition]) => { const { bucketEndpoint, hostname } = bucketHostname({ bucketName: parseArn(arn), baseHostname: `s3.${region}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: region, - useArnRegion: useArnRegion, + clientRegion, + useArnRegion, + clientPartition, }); expect(bucketEndpoint).toBe(true); - expect(hostname).toBe(exoectedEndpoint); + expect(hostname).toBe(expectedEndpoint); }); }); it("should match not work with invalid configurations", () => { - const invalidLambdaConfigurations: [string, string, boolean, string][] = [ + const invalidLambdaConfigurations: [ + arn: string, + clientRegion: string, + useArnRegion: boolean, + expectedError: string, + clientPartition?: string + ][] = [ [ "arn:aws:s3-object-lambda:us-east-1:123456789012:accesspoint/mybanner", "us-west-2", @@ -910,30 +937,42 @@ describe("bucketHostname", () => { "arn:aws:s3-object-lambda:us-east-1:4123456789012:accesspoint/mybanner", "s3-external-1", false, - "mybanner-4123456789012.s3-object-lambda.us-east-1.amazonaws.com", + "Client region s3-external-1 is not regional", ], [ "arn:aws:s3-object-lambda:us-east-1:5123456789012:accesspoint/mybanner", "aws-global", false, - "mybanner-5123456789012.s3-object-lambda.us-east-1.amazonaws.com", + "Client region aws-global is not regional", + ], + [ + "arn:aws-us-gov:s3-object-lambda:us-gov-west-1:123456789012:accesspoint/mybanner", + "fips-us-gov-east-1", + false, + "Client FIPS region fips-us-gov-east-1 doesn't match region us-gov-west-1 in ARN", + "aws-us-gov", + ], + [ + "arn:aws-us-gov:s3-object-lambda:us-gov-west-1:123456789012:accesspoint/mybanner", + "fips-us-gov-east-1", + true, + "Client FIPS region fips-us-gov-east-1 doesn't match region us-gov-west-1 in ARN", + "aws-us-gov", ], ]; - invalidLambdaConfigurations.forEach((lambdaArn) => { - const arn = lambdaArn[0]; - const region = lambdaArn[1]; - const useArnRegion = lambdaArn[2]; + invalidLambdaConfigurations.forEach(([arn, clientRegion, useArnRegion, expectedError, clientPartition]) => { try { bucketHostname({ bucketName: parseArn(arn), - baseHostname: "s3.us-west-2.amazonaws.com", + baseHostname: `s3.${region}.amazonaws.com`, isCustomEndpoint: false, - clientRegion: region, - useArnRegion: useArnRegion, + useArnRegion, + clientRegion, + clientPartition, }); // should never get here - expect.assertions(1); + fail(); } catch (e) { // should throw since these are error cases expect(1).toEqual(1); diff --git a/packages/middleware-bucket-endpoint/src/bucketHostname.ts b/packages/middleware-bucket-endpoint/src/bucketHostname.ts index 5451a55df671..58af25f36bbd 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostname.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostname.ts @@ -3,19 +3,21 @@ import { BucketHostnameParams, DOT_PATTERN, getArnResources, + getPseudoRegion, getSuffix, getSuffixForArnEndpoint, isBucketNameOptions, isDnsCompatibleBucketName, + isFipsRegion, validateAccountId, validateArnEndpointOptions, - validateClientRegion, validateDNSHostLabel, validateNoDualstack, validateNoFIPS, validateOutpostService, validatePartition, validateRegion, + validateRegionalClient, validateS3Service, validateService, } from "./bucketHostnameUtils"; @@ -43,11 +45,8 @@ export const bucketHostname = (options: BucketHostnameParams | ArnHostnameParams }; const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boolean }): BucketHostname => { - const { isCustomEndpoint, baseHostname } = options; - const [clientRegion, hostnameSuffix] = isCustomEndpoint - ? [options.clientRegion, baseHostname] - : // Infer client region and hostname suffix from hostname from endpoints.json, like `s3.us-west-2.amazonaws.com` - getSuffixForArnEndpoint(baseHostname); + const { isCustomEndpoint, baseHostname, clientRegion } = options; + const hostnameSuffix = isCustomEndpoint ? baseHostname : getSuffixForArnEndpoint(baseHostname)[1]; const { pathStyleEndpoint, @@ -67,7 +66,7 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo validateService(service); validatePartition(partition, { clientPartition }); validateAccountId(accountId); - validateClientRegion(clientRegion); + validateRegionalClient(clientRegion); const { accesspointName, outpostId } = getArnResources(resource); const DNSHostLabel = `${accesspointName}-${accountId}`; validateDNSHostLabel(DNSHostLabel, { tlsCompatible }); @@ -79,7 +78,9 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo validateNoDualstack(dualstackEndpoint); return { bucketEndpoint: true, - hostname: `${DNSHostLabel}.${service}.${endpointRegion}.${hostnameSuffix}`, + hostname: `${DNSHostLabel}.${service}${isFipsRegion(clientRegion) ? "-fips" : ""}.${getPseudoRegion( + endpointRegion + )}.${hostnameSuffix}`, signingRegion, signingService: service, }; @@ -105,7 +106,11 @@ const getEndpointFromArn = (options: ArnHostnameParams & { isCustomEndpoint: boo return { bucketEndpoint: true, hostname: `${hostnamePrefix}${ - isCustomEndpoint ? "" : `.s3-accesspoint${dualstackEndpoint ? ".dualstack" : ""}.${endpointRegion}` + isCustomEndpoint + ? "" + : `.s3-accesspoint${isFipsRegion(clientRegion) ? "-fips" : ""}${ + dualstackEndpoint ? ".dualstack" : "" + }.${getPseudoRegion(endpointRegion)}` }.${hostnameSuffix}`, signingRegion, }; diff --git a/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts b/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts index 208c49ec021e..eaa1c934ef0d 100644 --- a/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts +++ b/packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts @@ -132,8 +132,12 @@ export const validateRegion = ( if (region === "") { throw new Error("ARN region is empty"); } - if (!options.allowFipsRegion && isFipsRegion(region)) { - throw new Error("Endpoint does not support FIPS region"); + if (isFipsRegion(options.clientRegion)) { + if (!options.allowFipsRegion) { + throw new Error("FIPS region is not supported"); + } else if (!isEqualRegions(region, options.clientRegion)) { + throw new Error(`Client FIPS region ${options.clientRegion} doesn't match region ${region} in ARN`); + } } if ( !options.useArnRegion && @@ -148,13 +152,16 @@ export const validateRegion = ( * * @param region */ -export const validateClientRegion = (region: string) => { +export const validateRegionalClient = (region: string) => { if (["s3-external-1", "aws-global"].includes(getPseudoRegion(region))) { throw new Error(`Client region ${region} is not regional`); } }; -const isFipsRegion = (region: string) => region.startsWith("fips-") || region.endsWith("-fips"); +/** + * @internal + */ +export const isFipsRegion = (region: string) => region.startsWith("fips-") || region.endsWith("-fips"); const isEqualRegions = (regionA: string, regionB: string) => regionA === regionB || getPseudoRegion(regionA) === regionB || regionA === getPseudoRegion(regionB); From f7bf3918fd2cf5175cac1c92253cf08dc0c8fd77 Mon Sep 17 00:00:00 2001 From: Allan Zheng Date: Fri, 14 May 2021 10:10:00 -0700 Subject: [PATCH 3/3] fix(middleware-sdk-s3-control): unit test error message --- .../src/process-arnables-plugin/plugin.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 8e14d905f73a..e6aa48d56836 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 @@ -187,7 +187,7 @@ describe("getProcessArnablesMiddleware", () => { }, }); } catch (e) { - expect(e.message).toContain("FIPS region is not supported with Outpost, got fips-us-gov-east-1"); + expect(e.message).toContain("FIPS region is not supported"); } }); @@ -210,7 +210,7 @@ describe("getProcessArnablesMiddleware", () => { }, }); } catch (e) { - expect(e.message).toContain("does not support FIPS region"); + expect(e.message).toContain("FIPS region is not supported"); } }); @@ -432,7 +432,7 @@ describe("getProcessArnablesMiddleware", () => { }, }); } catch (e) { - expect(e.message).toContain("FIPS region is not supported with Outpost, got fips-us-gov-east-1"); + expect(e.message).toContain("FIPS region is not supported"); } }); @@ -455,7 +455,7 @@ describe("getProcessArnablesMiddleware", () => { }, }); } catch (e) { - expect(e.message).toContain("does not support FIPS region"); + expect(e.message).toContain("FIPS region is not supported"); } });