Skip to content

chore(middleware): replace isFipsRegion checks with useFipsEndpoint config #2988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ describe("bucketEndpointMiddleware", () => {
baseHostname: requestInput.hostname,
clientRegion: mockRegion,
accelerateEndpoint: false,
fipsEndpoint: false,
dualstackEndpoint: false,
pathStyleEndpoint: false,
tlsCompatible: true,
Expand Down Expand Up @@ -95,6 +96,7 @@ describe("bucketEndpointMiddleware", () => {
clientRegion: mockRegion,
accelerateEndpoint: true,
dualstackEndpoint: true,
fipsEndpoint: false,
pathStyleEndpoint: true,
tlsCompatible: false,
isCustomEndpoint: true,
Expand Down Expand Up @@ -129,6 +131,7 @@ describe("bucketEndpointMiddleware", () => {
baseHostname: requestInput.hostname,
clientRegion: mockRegion,
accelerateEndpoint: false,
fipsEndpoint: false,
dualstackEndpoint: false,
pathStyleEndpoint: false,
tlsCompatible: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { parse as parseArn, validate as validateArn } from "@aws-sdk/util-arn-parser";

import { bucketHostname } from "./bucketHostname";
import { getPseudoRegion } from "./bucketHostnameUtils";
import { BucketEndpointResolvedConfig } from "./configurations";

export const bucketEndpointMiddleware =
Expand All @@ -30,7 +29,7 @@ export const bucketEndpointMiddleware =
request.hostname = bucketName;
} else if (validateArn(bucketName)) {
const bucketArn = parseArn(bucketName);
const clientRegion = getPseudoRegion(await options.region());
const clientRegion = await options.region();
const useDualstackEndpoint = await options.useDualstackEndpoint();
const useFipsEndpoint = await options.useFipsEndpoint();
const { partition, signingRegion = clientRegion } =
Expand All @@ -46,6 +45,7 @@ export const bucketEndpointMiddleware =
baseHostname: request.hostname,
accelerateEndpoint: options.useAccelerateEndpoint,
dualstackEndpoint: useDualstackEndpoint,
fipsEndpoint: useFipsEndpoint,
pathStyleEndpoint: options.forcePathStyle,
tlsCompatible: request.protocol === "https:",
useArnRegion,
Expand All @@ -68,14 +68,16 @@ export const bucketEndpointMiddleware =
request.hostname = hostname;
replaceBucketInPath = bucketEndpoint;
} else {
const clientRegion = getPseudoRegion(await options.region());
const clientRegion = await options.region();
const dualstackEndpoint = await options.useDualstackEndpoint();
const fipsEndpoint = await options.useFipsEndpoint();
const { hostname, bucketEndpoint } = bucketHostname({
bucketName,
clientRegion,
baseHostname: request.hostname,
accelerateEndpoint: options.useAccelerateEndpoint,
dualstackEndpoint,
fipsEndpoint,
pathStyleEndpoint: options.forcePathStyle,
tlsCompatible: request.protocol === "https:",
isCustomEndpoint: options.isCustomEndpoint,
Expand Down
23 changes: 16 additions & 7 deletions packages/middleware-bucket-endpoint/src/bucketHostname.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,9 @@ describe("bucketHostname", () => {
});
});

describe("allows fips client region", () => {
describe("allows client region with fipsEndpoint", () => {
const bucketArn = parseArn("arn:aws-us-gov:s3:us-gov-east-1:123456789012:accesspoint:myendpoint");
const clientRegion = "fips-us-gov-east-1";
const clientRegion = "us-gov-east-1";
const clientPartition = "aws-us-gov";
it("should use client region", () => {
const { bucketEndpoint, hostname } = bucketHostname({
Expand All @@ -335,6 +335,7 @@ describe("bucketHostname", () => {
isCustomEndpoint: false,
clientRegion,
clientPartition,
fipsEndpoint: true,
});
expect(bucketEndpoint).toBe(true);
expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.us-gov-east-1.amazonaws.com");
Expand All @@ -348,6 +349,7 @@ describe("bucketHostname", () => {
clientRegion,
clientPartition,
useArnRegion: true,
fipsEndpoint: true,
});
expect(bucketEndpoint).toBe(true);
expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.us-gov-east-1.amazonaws.com");
Expand All @@ -362,15 +364,16 @@ describe("bucketHostname", () => {
clientPartition,
useArnRegion: true,
dualstackEndpoint: true,
fipsEndpoint: true,
});
expect(bucketEndpoint).toBe(true);
expect(hostname).toBe("myendpoint-123456789012.s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com");
});
});

describe("validates FIPS client region matching ARN region", () => {
describe("validates client region with fips endpoint 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 clientRegion = "us-gov-east-1";
const clientPartition = "aws-us-gov";
it("should throw client region doesn't match arn region", () => {
expect(() =>
Expand All @@ -380,6 +383,7 @@ describe("bucketHostname", () => {
isCustomEndpoint: false,
clientRegion,
clientPartition,
fipsEndpoint: true,
})
).toThrowError();
});
Expand All @@ -393,6 +397,7 @@ describe("bucketHostname", () => {
clientRegion,
clientPartition,
useArnRegion: true,
fipsEndpoint: true,
})
).toThrowError();
});
Expand Down Expand Up @@ -693,7 +698,7 @@ describe("bucketHostname", () => {

describe("fips region", () => {
it("should throw if client is using fips region", () => {
const clientRegion = "fips-us-gov-east-1";
const clientRegion = "us-gov-east-1";
const clientPartition = "aws-us-gov";
expect.assertions(2);
expect(() => {
Expand All @@ -705,6 +710,7 @@ describe("bucketHostname", () => {
isCustomEndpoint: false,
clientRegion,
clientPartition,
fipsEndpoint: true,
});
}).toThrow("FIPS region is not supported");

Expand All @@ -718,6 +724,7 @@ describe("bucketHostname", () => {
clientRegion,
clientPartition,
useArnRegion: true,
fipsEndpoint: true,
});
}).toThrow("FIPS region is not supported");
});
Expand Down Expand Up @@ -962,9 +969,10 @@ describe("bucketHostname", () => {
bucketName: parseArn(arn),
baseHostname: `s3.${region}.amazonaws.com`,
isCustomEndpoint: false,
clientRegion,
clientRegion: clientRegion.startsWith("fips-") ? clientRegion.replace(/fips-/, "") : clientRegion,
useArnRegion,
clientPartition,
fipsEndpoint: clientRegion.startsWith("fips-"),
});
expect(bucketEndpoint).toBe(true);
expect(hostname).toBe(expectedEndpoint);
Expand Down Expand Up @@ -1080,8 +1088,9 @@ describe("bucketHostname", () => {
baseHostname: `s3.${region}.amazonaws.com`,
isCustomEndpoint: false,
useArnRegion,
clientRegion,
clientRegion: clientRegion.startsWith("fips-") ? clientRegion.replace(/fips-/, "") : clientRegion,
clientPartition,
fipsEndpoint: clientRegion.startsWith("fips-"),
});
// should never get here
fail();
Expand Down
35 changes: 23 additions & 12 deletions packages/middleware-bucket-endpoint/src/bucketHostname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {
BucketHostnameParams,
DOT_PATTERN,
getArnResources,
getPseudoRegion,
getSuffix,
getSuffixForArnEndpoint,
isBucketNameOptions,
isDnsCompatibleBucketName,
isFipsRegion,
validateAccountId,
validateArnEndpointOptions,
validateCustomEndpoint,
Expand Down Expand Up @@ -48,6 +46,7 @@ const getEndpointFromBucketName = ({
baseHostname,
bucketName,
dualstackEndpoint = false,
fipsEndpoint = false,
pathStyleEndpoint = false,
tlsCompatible = true,
isCustomEndpoint = false,
Expand Down Expand Up @@ -79,6 +78,7 @@ const getEndpointFromArn = (options: ArnHostnameParams): BucketHostname => {
const {
pathStyleEndpoint,
accelerateEndpoint = false,
fipsEndpoint = false,
tlsCompatible = true,
bucketName,
clientPartition = "aws",
Expand Down Expand Up @@ -106,6 +106,7 @@ const getEndpointFromArn = (options: ArnHostnameParams): BucketHostname => {

const getEndpointFromObjectLambdaArn = ({
dualstackEndpoint = false,
fipsEndpoint = false,
tlsCompatible = true,
useArnRegion,
clientRegion,
Expand All @@ -120,7 +121,13 @@ const getEndpointFromObjectLambdaArn = ({
}): BucketHostname => {
const { accountId, region, service } = bucketName;
validateRegionalClient(clientRegion);
validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion, allowFipsRegion: true });
validateRegion(region, {
useArnRegion,
clientRegion,
clientSigningRegion,
allowFipsRegion: true,
useFipsEndpoint: fipsEndpoint,
});
validateNoDualstack(dualstackEndpoint);
const DNSHostLabel = `${accesspointName}-${accountId}`;
validateDNSHostLabel(DNSHostLabel, { tlsCompatible });
Expand All @@ -130,9 +137,7 @@ const getEndpointFromObjectLambdaArn = ({

return {
bucketEndpoint: true,
hostname: `${DNSHostLabel}.${service}${isFipsRegion(clientRegion) ? "-fips" : ""}.${getPseudoRegion(
endpointRegion
)}.${hostnameSuffix}`,
hostname: `${DNSHostLabel}.${service}${fipsEndpoint ? "-fips" : ""}.${endpointRegion}.${hostnameSuffix}`,
signingRegion,
signingService: service,
};
Expand Down Expand Up @@ -165,22 +170,23 @@ const getEndpointFromOutpostArn = ({
bucketName,
outpostId,
dualstackEndpoint = false,
fipsEndpoint = false,
tlsCompatible = true,
accesspointName,
isCustomEndpoint,
hostnameSuffix,
}: ArnHostnameParams & { outpostId: string; accesspointName: string; hostnameSuffix: string }): BucketHostname => {
// if this is an Outpost ARN
validateRegionalClient(clientRegion);
validateRegion(bucketName.region, { useArnRegion, clientRegion, clientSigningRegion });
validateRegion(bucketName.region, { useArnRegion, clientRegion, clientSigningRegion, useFipsEndpoint: fipsEndpoint });
const DNSHostLabel = `${accesspointName}-${bucketName.accountId}`;
validateDNSHostLabel(DNSHostLabel, { tlsCompatible });
const endpointRegion = useArnRegion ? bucketName.region : clientRegion;
const signingRegion = useArnRegion ? bucketName.region : clientSigningRegion;
validateOutpostService(bucketName.service);
validateDNSHostLabel(outpostId, { tlsCompatible });
validateNoDualstack(dualstackEndpoint);
validateNoFIPS(endpointRegion);
validateNoFIPS(fipsEndpoint);
const hostnamePrefix = `${DNSHostLabel}.${outpostId}`;
return {
bucketEndpoint: true,
Expand All @@ -196,14 +202,21 @@ const getEndpointFromAccessPointArn = ({
clientSigningRegion = clientRegion,
bucketName,
dualstackEndpoint = false,
fipsEndpoint = false,
tlsCompatible = true,
accesspointName,
isCustomEndpoint,
hostnameSuffix,
}: ArnHostnameParams & { accesspointName: string; hostnameSuffix: string }): BucketHostname => {
// construct endpoint from Accesspoint ARN
validateRegionalClient(clientRegion);
validateRegion(bucketName.region, { useArnRegion, clientRegion, clientSigningRegion, allowFipsRegion: true });
validateRegion(bucketName.region, {
useArnRegion,
clientRegion,
clientSigningRegion,
allowFipsRegion: true,
useFipsEndpoint: fipsEndpoint,
});
const hostnamePrefix = `${accesspointName}-${bucketName.accountId}`;
validateDNSHostLabel(hostnamePrefix, { tlsCompatible });
const endpointRegion = useArnRegion ? bucketName.region : clientRegion;
Expand All @@ -214,9 +227,7 @@ const getEndpointFromAccessPointArn = ({
hostname: `${hostnamePrefix}${
isCustomEndpoint
? ""
: `.s3-accesspoint${isFipsRegion(clientRegion) ? "-fips" : ""}${
dualstackEndpoint ? ".dualstack" : ""
}.${getPseudoRegion(endpointRegion)}`
: `.s3-accesspoint${fipsEndpoint ? "-fips" : ""}${dualstackEndpoint ? ".dualstack" : ""}.${endpointRegion}`
}.${hostnameSuffix}`,
signingRegion,
};
Expand Down
26 changes: 8 additions & 18 deletions packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface BucketHostnameParams {
clientRegion: string;
accelerateEndpoint?: boolean;
dualstackEndpoint?: boolean;
fipsEndpoint?: boolean;
pathStyleEndpoint?: boolean;
tlsCompatible?: boolean;
}
Expand All @@ -35,12 +36,6 @@ export const isBucketNameOptions = (
options: BucketHostnameParams | ArnHostnameParams
): options is BucketHostnameParams => typeof options.bucketName === "string";

/**
* Get pseudo region from supplied region. For example, if supplied with `fips-us-west-2`, it returns `us-west-2`.
* @internal
*/
export const getPseudoRegion = (region: string) => (isFipsRegion(region) ? region.replace(/fips-|-fips/, "") : region);

/**
* Determines whether a given string is DNS compliant per the rules outlined by
* S3. Length, capitaization, and leading dot restrictions are enforced by the
Expand Down Expand Up @@ -128,12 +123,13 @@ export const validateRegion = (
allowFipsRegion?: boolean;
clientRegion: string;
clientSigningRegion: string;
useFipsEndpoint: boolean;
}
) => {
if (region === "") {
throw new Error("ARN region is empty");
}
if (isFipsRegion(options.clientRegion)) {
if (options.useFipsEndpoint) {
if (!options.allowFipsRegion) {
throw new Error("FIPS region is not supported");
} else if (!isEqualRegions(region, options.clientRegion)) {
Expand All @@ -154,18 +150,12 @@ export const validateRegion = (
* @param region
*/
export const validateRegionalClient = (region: string) => {
if (["s3-external-1", "aws-global"].includes(getPseudoRegion(region))) {
if (["s3-external-1", "aws-global"].includes(region)) {
throw new Error(`Client region ${region} is not regional`);
}
};

/**
* @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);
const isEqualRegions = (regionA: string, regionB: string) => regionA === regionB;

/**
* Validate an account ID
Expand Down Expand Up @@ -250,11 +240,11 @@ export const validateNoDualstack = (dualstackEndpoint?: boolean) => {
};

/**
* Validate region is not appended or prepended with a `fips-`
* Validate fips endpoint is not set up.
* @internal
*/
export const validateNoFIPS = (region?: string) => {
if (isFipsRegion(region ?? "")) throw new Error(`FIPS region is not supported with Outpost, got ${region}`);
export const validateNoFIPS = (useFipsEndpoint?: boolean) => {
if (useFipsEndpoint) throw new Error(`FIPS region is not supported with Outpost.`);
};

/**
Expand Down
1 change: 0 additions & 1 deletion packages/middleware-bucket-endpoint/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export * from "./bucketHostname";
export * from "./configurations";
export {
getArnResources,
getPseudoRegion,
getSuffixForArnEndpoint,
validateOutpostService,
validatePartition,
Expand Down
Loading