Skip to content

Commit d0620c4

Browse files
authored
Add useFipsEndpoint configuration (#3951)
1 parent 6e47cc7 commit d0620c4

20 files changed

+6634
-732
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "endpoint",
4+
"description": "Add useFipsEndpoint configuration"
5+
}

lib/config-base.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,8 @@ export abstract class ConfigurationOptions {
267267
* regional endpoints.
268268
*/
269269
stsRegionalEndpoints?: "legacy"|"regional";
270+
/**
271+
* Enables FIPS compatible endpoints.
272+
*/
273+
useFipsEndpoint?: boolean;
270274
}

lib/config.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,10 @@ var PromisesDependency;
185185
* @!attribute stsRegionalEndpoints
186186
* @return ['legacy'|'regional'] whether to send sts request to global endpoints or
187187
* regional endpoints.
188-
* Defaults to 'legacy'
188+
* Defaults to 'legacy'.
189+
*
190+
* @!attribute useFipsEndpoint
191+
* @return [Boolean] Enables FIPS compatible endpoints. Defaults to `false`.
189192
*/
190193
AWS.Config = AWS.util.inherit({
191194
/**
@@ -340,6 +343,8 @@ AWS.Config = AWS.util.inherit({
340343
* @option options stsRegionalEndpoints ['legacy'|'regional'] whether to send sts request
341344
* to global endpoints or regional endpoints.
342345
* Defaults to 'legacy'.
346+
* @option options useFipsEndpoint [Boolean] Enables FIPS compatible endpoints.
347+
* Defaults to `false`.
343348
*/
344349
constructor: function Config(options) {
345350
if (options === undefined) options = {};
@@ -563,7 +568,8 @@ AWS.Config = AWS.util.inherit({
563568
endpointDiscoveryEnabled: undefined,
564569
endpointCacheSize: 1000,
565570
hostPrefixEnabled: true,
566-
stsRegionalEndpoints: 'legacy'
571+
stsRegionalEndpoints: 'legacy',
572+
useFipsEndpoint: false
567573
},
568574

569575
/**

lib/node_loader.js

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
var util = require('./util');
22

3+
var region_utils = require('./region/utils');
4+
var isFipsRegion = region_utils.isFipsRegion;
5+
var getRealRegion = region_utils.getRealRegion;
6+
37
util.isBrowser = function() { return false; };
48
util.isNode = function() { return true; };
59

@@ -96,6 +100,44 @@ AWS.CredentialProviderChain.defaultProviders = [
96100
function () { return new AWS.EC2MetadataCredentials(); }
97101
];
98102

103+
var getRegion = function() {
104+
var env = process.env;
105+
var region = env.AWS_REGION || env.AMAZON_REGION;
106+
if (env[AWS.util.configOptInEnv]) {
107+
var toCheck = [
108+
{filename: env[AWS.util.sharedCredentialsFileEnv]},
109+
{isConfig: true, filename: env[AWS.util.sharedConfigFileEnv]}
110+
];
111+
var iniLoader = AWS.util.iniLoader;
112+
while (!region && toCheck.length) {
113+
var configFile = {};
114+
var fileInfo = toCheck.shift();
115+
try {
116+
configFile = iniLoader.loadFrom(fileInfo);
117+
} catch (err) {
118+
if (fileInfo.isConfig) throw err;
119+
}
120+
var profile = configFile[env.AWS_PROFILE || AWS.util.defaultProfile];
121+
region = profile && profile.region;
122+
}
123+
}
124+
return region;
125+
};
126+
127+
var getBooleanValue = function(value) {
128+
return value === 'true' ? true: value === 'false' ? false: undefined;
129+
};
130+
131+
var USE_FIPS_ENDPOINT_CONFIG_OPTIONS = {
132+
environmentVariableSelector: function(env) {
133+
return getBooleanValue(env['AWS_USE_FIPS_ENDPOINT']);
134+
},
135+
configFileSelector: function(profile) {
136+
return getBooleanValue(profile['use_fips_endpoint']);
137+
},
138+
default: false,
139+
};
140+
99141
// Update configuration keys
100142
AWS.util.update(AWS.Config.prototype.keys, {
101143
credentials: function () {
@@ -116,27 +158,14 @@ AWS.util.update(AWS.Config.prototype.keys, {
116158
return process.env.AWSJS_DEBUG ? console : null;
117159
},
118160
region: function() {
119-
var env = process.env;
120-
var region = env.AWS_REGION || env.AMAZON_REGION;
121-
if (env[AWS.util.configOptInEnv]) {
122-
var toCheck = [
123-
{filename: env[AWS.util.sharedCredentialsFileEnv]},
124-
{isConfig: true, filename: env[AWS.util.sharedConfigFileEnv]}
125-
];
126-
var iniLoader = AWS.util.iniLoader;
127-
while (!region && toCheck.length) {
128-
var configFile = {};
129-
var fileInfo = toCheck.shift();
130-
try {
131-
configFile = iniLoader.loadFrom(fileInfo);
132-
} catch (err) {
133-
if (fileInfo.isConfig) throw err;
134-
}
135-
var profile = configFile[env.AWS_PROFILE || AWS.util.defaultProfile];
136-
region = profile && profile.region;
137-
}
138-
}
139-
return region;
161+
var region = getRegion();
162+
return region ? getRealRegion(region): undefined;
163+
},
164+
useFipsEndpoint: function() {
165+
var region = getRegion();
166+
return isFipsRegion(region)
167+
? true
168+
: util.loadConfig(USE_FIPS_ENDPOINT_CONFIG_OPTIONS);
140169
}
141170
});
142171

lib/region/utils.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function isFipsRegion(region) {
2+
return typeof region === 'string' && (region.startsWith('fips-') || region.endsWith('-fips'));
3+
}
4+
5+
function isGlobalRegion(region) {
6+
return typeof region === 'string' && ['aws-global', 'aws-us-gov-global'].includes(region);
7+
}
8+
9+
function getRealRegion(region) {
10+
return ['fips-aws-global', 'aws-fips', 'aws-global'].includes(region)
11+
? 'us-east-1'
12+
: ['fips-aws-us-gov-global', 'aws-us-gov-global'].includes(region)
13+
? 'us-gov-west-1'
14+
: region.replace(/fips-(dkr-|prod-)?|-fips/, '');
15+
}
16+
17+
module.exports = {
18+
isFipsRegion: isFipsRegion,
19+
isGlobalRegion: isGlobalRegion,
20+
getRealRegion: getRealRegion
21+
};

lib/region_config.js

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ var regionConfig = require('./region_config_data.json');
33

44
function generateRegionPrefix(region) {
55
if (!region) return null;
6-
if (isFipsRegion(region)) {
7-
region = getRealRegion(region);
8-
}
9-
106
var parts = region.split('-');
117
if (parts.length < 3) return null;
128
return parts.slice(0, parts.length - 2).join('-') + '-*';
@@ -40,12 +36,12 @@ function applyConfig(service, config) {
4036

4137
function configureEndpoint(service) {
4238
var keys = derivedKeys(service);
43-
var region = service.config.region;
39+
var useFipsEndpoint = service.config.useFipsEndpoint;
4440
for (var i = 0; i < keys.length; i++) {
4541
var key = keys[i];
4642
if (!key) continue;
4743

48-
var rules = isFipsRegion(region) ? regionConfig.fipsRules : regionConfig.rules;
44+
var rules = useFipsEndpoint ? regionConfig.fipsRules : regionConfig.rules;
4945
if (Object.prototype.hasOwnProperty.call(rules, key)) {
5046
var config = rules[key];
5147
if (typeof config === 'string') {
@@ -61,12 +57,6 @@ function configureEndpoint(service) {
6157
);
6258
}
6359

64-
// set FIPS signingRegion and endpoint.
65-
if (isFipsRegion(service.config.region)) {
66-
config = util.copy(config);
67-
service.signingRegion = getRealRegion(service.config.region);
68-
}
69-
7060
// set global endpoint
7161
service.isGlobalEndpoint = !!config.globalEndpoint;
7262
if (config.signingRegion) {
@@ -101,25 +91,10 @@ function getEndpointSuffix(region) {
10191
return defaultSuffix;
10292
}
10393

104-
function isFipsRegion(region) {
105-
return region && (region.startsWith('fips-') || region.endsWith('-fips'));
106-
}
107-
108-
function getRealRegion(region) {
109-
return isFipsRegion(region)
110-
? ['fips-aws-global', 'aws-fips'].includes(region)
111-
? 'us-east-1'
112-
: region === 'fips-aws-us-gov-global'
113-
? 'us-gov-west-1'
114-
: region.replace(/fips-(dkr-|prod-)?|-fips/, '')
115-
: region;
116-
}
117-
11894
/**
11995
* @api private
12096
*/
12197
module.exports = {
12298
configureEndpoint: configureEndpoint,
12399
getEndpointSuffix: getEndpointSuffix,
124-
getRealRegion: getRealRegion,
125100
};

lib/region_config_data.json

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
"cn-*/*": {
77
"endpoint": "{service}.{region}.amazonaws.com.cn"
88
},
9-
"us-iso-*/*": {
10-
"endpoint": "{service}.{region}.c2s.ic.gov"
11-
},
12-
"us-isob-*/*": {
13-
"endpoint": "{service}.{region}.sc2s.sgov.gov"
14-
},
9+
"us-iso-*/*": "usIso",
10+
"us-isob-*/*": "usIsob",
1511
"*/budgets": "globalSSL",
1612
"*/cloudfront": "globalSSL",
1713
"*/sts": "globalSSL",
@@ -67,6 +63,14 @@
6763
"fipsRules": {
6864
"*/*": "fipsStandard",
6965
"us-gov-*/*": "fipsStandard",
66+
"us-iso-*/*": {
67+
"endpoint": "{service}-fips.{region}.c2s.ic.gov"
68+
},
69+
"us-iso-*/dms": "usIso",
70+
"us-isob-*/*": {
71+
"endpoint": "{service}-fips.{region}.sc2s.sgov.gov"
72+
},
73+
"us-isob-*/dms": "usIsob",
7074
"cn-*/*": {
7175
"endpoint": "{service}-fips.{region}.amazonaws.com.cn"
7276
},
@@ -79,6 +83,7 @@
7983
"*/runtime.sagemaker": {
8084
"endpoint": "runtime-fips.sagemaker.{region}.amazonaws.com"
8185
},
86+
"*/iam": "fipsWithoutRegion",
8287
"*/route53": "fipsWithoutRegion",
8388
"*/transcribe": "fipsDotPrefix",
8489
"*/waf": "fipsWithoutRegion",
@@ -105,12 +110,12 @@
105110
"us-gov-*/ssm": "fipsWithServiceOnly",
106111
"us-gov-*/sts": "fipsWithServiceOnly",
107112
"us-gov-*/support": "fipsWithServiceOnly",
108-
"fips-us-gov-west-1/states": "fipsWithServiceOnly",
109-
"fips-us-iso-east-1/elasticfilesystem": {
113+
"us-gov-west-1/states": "fipsWithServiceOnly",
114+
"us-iso-east-1/elasticfilesystem": {
110115
"endpoint": "elasticfilesystem-fips.{region}.c2s.ic.gov"
111116
},
112-
"fips-aws-us-gov-global/organizations": "fipsWithServiceOnly",
113-
"fips-aws-us-gov-global/route53": {
117+
"us-gov-west-1/organizations": "fipsWithServiceOnly",
118+
"us-gov-west-1/route53": {
114119
"endpoint": "route53.us-gov.amazonaws.com"
115120
}
116121
},
@@ -130,6 +135,12 @@
130135
"endpoint": "{service}.{region}.amazonaws.com",
131136
"signatureVersion": "s3"
132137
},
138+
"usIso": {
139+
"endpoint": "{service}.{region}.c2s.ic.gov"
140+
},
141+
"usIsob": {
142+
"endpoint": "{service}.{region}.sc2s.sgov.gov"
143+
},
133144
"fipsStandard": {
134145
"endpoint": "{service}-fips.{region}.amazonaws.com"
135146
},

lib/service.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var regionConfig = require('./region_config');
44

55
var inherit = AWS.util.inherit;
66
var clientCount = 0;
7+
var region_utils = require('./region/utils');
78

89
/**
910
* The service class representing an AWS service.
@@ -25,6 +26,18 @@ AWS.Service = inherit({
2526
throw AWS.util.error(new Error(),
2627
'Service must be constructed with `new\' operator');
2728
}
29+
30+
if (config && config.region) {
31+
var region = config.region;
32+
if (region_utils.isFipsRegion(region)) {
33+
config.region = region_utils.getRealRegion(region);
34+
config.useFipsEndpoint = true;
35+
}
36+
if (region_utils.isGlobalRegion(region)) {
37+
config.region = region_utils.getRealRegion(region);
38+
}
39+
}
40+
2841
var ServiceClass = this.loadServiceClass(config || {});
2942
if (ServiceClass) {
3043
var originalConfig = AWS.util.copy(config);
@@ -636,7 +649,7 @@ AWS.Service = inherit({
636649

637650
var e = endpoint;
638651
e = e.replace(/\{service\}/g, this.api.endpointPrefix);
639-
e = e.replace(/\{region\}/g, regionConfig.getRealRegion(this.config.region));
652+
e = e.replace(/\{region\}/g, this.config.region);
640653
e = e.replace(/\{scheme\}/g, this.config.sslEnabled ? 'https' : 'http');
641654
return e;
642655
},

lib/services/s3util.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ var s3util = {
136136
var useArnRegion = s3util.loadUseArnRegionConfig(req);
137137
var regionFromArn = req._parsedArn.region;
138138
var clientRegion = req.service.config.region;
139+
var useFipsEndpoint = req.service.config.useFipsEndpoint;
139140

140141
if (!regionFromArn) {
141142
throw AWS.util.error(new Error(), {
@@ -145,7 +146,7 @@ var s3util = {
145146
}
146147

147148
if (
148-
clientRegion.indexOf('fips') >= 0 ||
149+
useFipsEndpoint ||
149150
regionFromArn.indexOf('fips') >= 0
150151
) {
151152
throw AWS.util.error(new Error(), {

scripts/region-checker/allowlist.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ var allowlist = {
44
25,
55
85,
66
86,
7-
201,
8-
255,
9-
256
7+
204,
8+
258,
9+
259
1010
],
1111
'/credentials/cognito_identity_credentials.js': [
1212
78,
@@ -27,8 +27,8 @@ var allowlist = {
2727
110,
2828
112
2929
],
30-
'/region_config.js': [
31-
110
30+
'/region/utils.js': [
31+
10
3232
],
3333
'/request.js': [
3434
318,

test/endpoint/fips/fips.spec.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const helpers = require('../../helpers');
33
const AWS = helpers.AWS;
44

55
function testApiCall(input, done) {
6-
const { clientName, region, signingRegion, hostname } = input;
6+
const { clientName, region, hostname } = input;
77

88
if (!AWS[clientName]) {
99
throw new Error(`${clientName} does not exist`);
@@ -13,8 +13,6 @@ function testApiCall(input, done) {
1313

1414
const req = client[Object.keys(client.api.operations)[0]]();
1515
req.on('complete', function() {
16-
expect(region).to.equal(client.config.region);
17-
expect(signingRegion).to.equal(req.httpRequest.region);
1816
expect(hostname).to.equal(req.httpRequest.endpoint.host);
1917
done();
2018
});

0 commit comments

Comments
 (0)