Skip to content

Commit

Permalink
Add useFipsEndpoint configuration (#3951)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Nov 11, 2021
1 parent 6e47cc7 commit d0620c4
Show file tree
Hide file tree
Showing 20 changed files with 6,634 additions and 732 deletions.
5 changes: 5 additions & 0 deletions .changes/next-release/feature-endpoint-e156c0ec.json
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "endpoint",
"description": "Add useFipsEndpoint configuration"
}
4 changes: 4 additions & 0 deletions lib/config-base.d.ts
Expand Up @@ -267,4 +267,8 @@ export abstract class ConfigurationOptions {
* regional endpoints.
*/
stsRegionalEndpoints?: "legacy"|"regional";
/**
* Enables FIPS compatible endpoints.
*/
useFipsEndpoint?: boolean;
}
10 changes: 8 additions & 2 deletions lib/config.js
Expand Up @@ -185,7 +185,10 @@ var PromisesDependency;
* @!attribute stsRegionalEndpoints
* @return ['legacy'|'regional'] whether to send sts request to global endpoints or
* regional endpoints.
* Defaults to 'legacy'
* Defaults to 'legacy'.
*
* @!attribute useFipsEndpoint
* @return [Boolean] Enables FIPS compatible endpoints. Defaults to `false`.
*/
AWS.Config = AWS.util.inherit({
/**
Expand Down Expand Up @@ -340,6 +343,8 @@ AWS.Config = AWS.util.inherit({
* @option options stsRegionalEndpoints ['legacy'|'regional'] whether to send sts request
* to global endpoints or regional endpoints.
* Defaults to 'legacy'.
* @option options useFipsEndpoint [Boolean] Enables FIPS compatible endpoints.
* Defaults to `false`.
*/
constructor: function Config(options) {
if (options === undefined) options = {};
Expand Down Expand Up @@ -563,7 +568,8 @@ AWS.Config = AWS.util.inherit({
endpointDiscoveryEnabled: undefined,
endpointCacheSize: 1000,
hostPrefixEnabled: true,
stsRegionalEndpoints: 'legacy'
stsRegionalEndpoints: 'legacy',
useFipsEndpoint: false
},

/**
Expand Down
71 changes: 50 additions & 21 deletions lib/node_loader.js
@@ -1,5 +1,9 @@
var util = require('./util');

var region_utils = require('./region/utils');
var isFipsRegion = region_utils.isFipsRegion;
var getRealRegion = region_utils.getRealRegion;

util.isBrowser = function() { return false; };
util.isNode = function() { return true; };

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

var getRegion = function() {
var env = process.env;
var region = env.AWS_REGION || env.AMAZON_REGION;
if (env[AWS.util.configOptInEnv]) {
var toCheck = [
{filename: env[AWS.util.sharedCredentialsFileEnv]},
{isConfig: true, filename: env[AWS.util.sharedConfigFileEnv]}
];
var iniLoader = AWS.util.iniLoader;
while (!region && toCheck.length) {
var configFile = {};
var fileInfo = toCheck.shift();
try {
configFile = iniLoader.loadFrom(fileInfo);
} catch (err) {
if (fileInfo.isConfig) throw err;
}
var profile = configFile[env.AWS_PROFILE || AWS.util.defaultProfile];
region = profile && profile.region;
}
}
return region;
};

var getBooleanValue = function(value) {
return value === 'true' ? true: value === 'false' ? false: undefined;
};

var USE_FIPS_ENDPOINT_CONFIG_OPTIONS = {
environmentVariableSelector: function(env) {
return getBooleanValue(env['AWS_USE_FIPS_ENDPOINT']);
},
configFileSelector: function(profile) {
return getBooleanValue(profile['use_fips_endpoint']);
},
default: false,
};

// Update configuration keys
AWS.util.update(AWS.Config.prototype.keys, {
credentials: function () {
Expand All @@ -116,27 +158,14 @@ AWS.util.update(AWS.Config.prototype.keys, {
return process.env.AWSJS_DEBUG ? console : null;
},
region: function() {
var env = process.env;
var region = env.AWS_REGION || env.AMAZON_REGION;
if (env[AWS.util.configOptInEnv]) {
var toCheck = [
{filename: env[AWS.util.sharedCredentialsFileEnv]},
{isConfig: true, filename: env[AWS.util.sharedConfigFileEnv]}
];
var iniLoader = AWS.util.iniLoader;
while (!region && toCheck.length) {
var configFile = {};
var fileInfo = toCheck.shift();
try {
configFile = iniLoader.loadFrom(fileInfo);
} catch (err) {
if (fileInfo.isConfig) throw err;
}
var profile = configFile[env.AWS_PROFILE || AWS.util.defaultProfile];
region = profile && profile.region;
}
}
return region;
var region = getRegion();
return region ? getRealRegion(region): undefined;
},
useFipsEndpoint: function() {
var region = getRegion();
return isFipsRegion(region)
? true
: util.loadConfig(USE_FIPS_ENDPOINT_CONFIG_OPTIONS);
}
});

Expand Down
21 changes: 21 additions & 0 deletions lib/region/utils.js
@@ -0,0 +1,21 @@
function isFipsRegion(region) {
return typeof region === 'string' && (region.startsWith('fips-') || region.endsWith('-fips'));
}

function isGlobalRegion(region) {
return typeof region === 'string' && ['aws-global', 'aws-us-gov-global'].includes(region);
}

function getRealRegion(region) {
return ['fips-aws-global', 'aws-fips', 'aws-global'].includes(region)
? 'us-east-1'
: ['fips-aws-us-gov-global', 'aws-us-gov-global'].includes(region)
? 'us-gov-west-1'
: region.replace(/fips-(dkr-|prod-)?|-fips/, '');
}

module.exports = {
isFipsRegion: isFipsRegion,
isGlobalRegion: isGlobalRegion,
getRealRegion: getRealRegion
};
29 changes: 2 additions & 27 deletions lib/region_config.js
Expand Up @@ -3,10 +3,6 @@ var regionConfig = require('./region_config_data.json');

function generateRegionPrefix(region) {
if (!region) return null;
if (isFipsRegion(region)) {
region = getRealRegion(region);
}

var parts = region.split('-');
if (parts.length < 3) return null;
return parts.slice(0, parts.length - 2).join('-') + '-*';
Expand Down Expand Up @@ -40,12 +36,12 @@ function applyConfig(service, config) {

function configureEndpoint(service) {
var keys = derivedKeys(service);
var region = service.config.region;
var useFipsEndpoint = service.config.useFipsEndpoint;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!key) continue;

var rules = isFipsRegion(region) ? regionConfig.fipsRules : regionConfig.rules;
var rules = useFipsEndpoint ? regionConfig.fipsRules : regionConfig.rules;
if (Object.prototype.hasOwnProperty.call(rules, key)) {
var config = rules[key];
if (typeof config === 'string') {
Expand All @@ -61,12 +57,6 @@ function configureEndpoint(service) {
);
}

// set FIPS signingRegion and endpoint.
if (isFipsRegion(service.config.region)) {
config = util.copy(config);
service.signingRegion = getRealRegion(service.config.region);
}

// set global endpoint
service.isGlobalEndpoint = !!config.globalEndpoint;
if (config.signingRegion) {
Expand Down Expand Up @@ -101,25 +91,10 @@ function getEndpointSuffix(region) {
return defaultSuffix;
}

function isFipsRegion(region) {
return region && (region.startsWith('fips-') || region.endsWith('-fips'));
}

function getRealRegion(region) {
return isFipsRegion(region)
? ['fips-aws-global', 'aws-fips'].includes(region)
? 'us-east-1'
: region === 'fips-aws-us-gov-global'
? 'us-gov-west-1'
: region.replace(/fips-(dkr-|prod-)?|-fips/, '')
: region;
}

/**
* @api private
*/
module.exports = {
configureEndpoint: configureEndpoint,
getEndpointSuffix: getEndpointSuffix,
getRealRegion: getRealRegion,
};
31 changes: 21 additions & 10 deletions lib/region_config_data.json
Expand Up @@ -6,12 +6,8 @@
"cn-*/*": {
"endpoint": "{service}.{region}.amazonaws.com.cn"
},
"us-iso-*/*": {
"endpoint": "{service}.{region}.c2s.ic.gov"
},
"us-isob-*/*": {
"endpoint": "{service}.{region}.sc2s.sgov.gov"
},
"us-iso-*/*": "usIso",
"us-isob-*/*": "usIsob",
"*/budgets": "globalSSL",
"*/cloudfront": "globalSSL",
"*/sts": "globalSSL",
Expand Down Expand Up @@ -67,6 +63,14 @@
"fipsRules": {
"*/*": "fipsStandard",
"us-gov-*/*": "fipsStandard",
"us-iso-*/*": {
"endpoint": "{service}-fips.{region}.c2s.ic.gov"
},
"us-iso-*/dms": "usIso",
"us-isob-*/*": {
"endpoint": "{service}-fips.{region}.sc2s.sgov.gov"
},
"us-isob-*/dms": "usIsob",
"cn-*/*": {
"endpoint": "{service}-fips.{region}.amazonaws.com.cn"
},
Expand All @@ -79,6 +83,7 @@
"*/runtime.sagemaker": {
"endpoint": "runtime-fips.sagemaker.{region}.amazonaws.com"
},
"*/iam": "fipsWithoutRegion",
"*/route53": "fipsWithoutRegion",
"*/transcribe": "fipsDotPrefix",
"*/waf": "fipsWithoutRegion",
Expand All @@ -105,12 +110,12 @@
"us-gov-*/ssm": "fipsWithServiceOnly",
"us-gov-*/sts": "fipsWithServiceOnly",
"us-gov-*/support": "fipsWithServiceOnly",
"fips-us-gov-west-1/states": "fipsWithServiceOnly",
"fips-us-iso-east-1/elasticfilesystem": {
"us-gov-west-1/states": "fipsWithServiceOnly",
"us-iso-east-1/elasticfilesystem": {
"endpoint": "elasticfilesystem-fips.{region}.c2s.ic.gov"
},
"fips-aws-us-gov-global/organizations": "fipsWithServiceOnly",
"fips-aws-us-gov-global/route53": {
"us-gov-west-1/organizations": "fipsWithServiceOnly",
"us-gov-west-1/route53": {
"endpoint": "route53.us-gov.amazonaws.com"
}
},
Expand All @@ -130,6 +135,12 @@
"endpoint": "{service}.{region}.amazonaws.com",
"signatureVersion": "s3"
},
"usIso": {
"endpoint": "{service}.{region}.c2s.ic.gov"
},
"usIsob": {
"endpoint": "{service}.{region}.sc2s.sgov.gov"
},
"fipsStandard": {
"endpoint": "{service}-fips.{region}.amazonaws.com"
},
Expand Down
15 changes: 14 additions & 1 deletion lib/service.js
Expand Up @@ -4,6 +4,7 @@ var regionConfig = require('./region_config');

var inherit = AWS.util.inherit;
var clientCount = 0;
var region_utils = require('./region/utils');

/**
* The service class representing an AWS service.
Expand All @@ -25,6 +26,18 @@ AWS.Service = inherit({
throw AWS.util.error(new Error(),
'Service must be constructed with `new\' operator');
}

if (config && config.region) {
var region = config.region;
if (region_utils.isFipsRegion(region)) {
config.region = region_utils.getRealRegion(region);
config.useFipsEndpoint = true;
}
if (region_utils.isGlobalRegion(region)) {
config.region = region_utils.getRealRegion(region);
}
}

var ServiceClass = this.loadServiceClass(config || {});
if (ServiceClass) {
var originalConfig = AWS.util.copy(config);
Expand Down Expand Up @@ -636,7 +649,7 @@ AWS.Service = inherit({

var e = endpoint;
e = e.replace(/\{service\}/g, this.api.endpointPrefix);
e = e.replace(/\{region\}/g, regionConfig.getRealRegion(this.config.region));
e = e.replace(/\{region\}/g, this.config.region);
e = e.replace(/\{scheme\}/g, this.config.sslEnabled ? 'https' : 'http');
return e;
},
Expand Down
3 changes: 2 additions & 1 deletion lib/services/s3util.js
Expand Up @@ -136,6 +136,7 @@ var s3util = {
var useArnRegion = s3util.loadUseArnRegionConfig(req);
var regionFromArn = req._parsedArn.region;
var clientRegion = req.service.config.region;
var useFipsEndpoint = req.service.config.useFipsEndpoint;

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

if (
clientRegion.indexOf('fips') >= 0 ||
useFipsEndpoint ||
regionFromArn.indexOf('fips') >= 0
) {
throw AWS.util.error(new Error(), {
Expand Down
10 changes: 5 additions & 5 deletions scripts/region-checker/allowlist.js
Expand Up @@ -4,9 +4,9 @@ var allowlist = {
25,
85,
86,
201,
255,
256
204,
258,
259
],
'/credentials/cognito_identity_credentials.js': [
78,
Expand All @@ -27,8 +27,8 @@ var allowlist = {
110,
112
],
'/region_config.js': [
110
'/region/utils.js': [
10
],
'/request.js': [
318,
Expand Down
4 changes: 1 addition & 3 deletions test/endpoint/fips/fips.spec.js
Expand Up @@ -3,7 +3,7 @@ const helpers = require('../../helpers');
const AWS = helpers.AWS;

function testApiCall(input, done) {
const { clientName, region, signingRegion, hostname } = input;
const { clientName, region, hostname } = input;

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

const req = client[Object.keys(client.api.operations)[0]]();
req.on('complete', function() {
expect(region).to.equal(client.config.region);
expect(signingRegion).to.equal(req.httpRequest.region);
expect(hostname).to.equal(req.httpRequest.endpoint.host);
done();
});
Expand Down

0 comments on commit d0620c4

Please sign in to comment.