diff --git a/.changes/next-release/bugfix-S3-b27b69fd.json b/.changes/next-release/bugfix-S3-b27b69fd.json new file mode 100644 index 0000000000..2d628b972d --- /dev/null +++ b/.changes/next-release/bugfix-S3-b27b69fd.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "S3", + "description": "Support FIPS for S3 Accesspoint & Object Lambda" +} \ No newline at end of file diff --git a/lib/services/s3.js b/lib/services/s3.js index 2e63855ad8..a227a33eb6 100644 --- a/lib/services/s3.js +++ b/lib/services/s3.js @@ -150,11 +150,12 @@ AWS.util.update(AWS.S3.prototype, { if (request._parsedArn.service === 's3') { request.addListener('validate', s3util.validateS3AccessPointArn); request.addListener('validate', this.validateArnResourceType); + request.addListener('validate', this.validateArnRegion); } else if (request._parsedArn.service === 's3-outposts') { request.addListener('validate', s3util.validateOutpostsAccessPointArn); request.addListener('validate', s3util.validateOutpostsArn); + request.addListener('validate', s3util.validateArnRegion); } - request.addListener('validate', s3util.validateArnRegion); request.addListener('validate', s3util.validateArnAccount); request.addListener('validate', s3util.validateArnService); request.addListener('build', this.populateUriFromAccessPointArn); @@ -196,6 +197,13 @@ AWS.util.update(AWS.S3.prototype, { } }, + /** + * @api private + */ + validateArnRegion: function validateArnRegion(req) { + s3util.validateArnRegion(req, { allowFipsEndpoint: true }); + }, + /** * Validate resource-type supplied in S3 ARN */ @@ -351,6 +359,7 @@ AWS.util.update(AWS.S3.prototype, { var outpostsSuffix = isOutpostArn ? '.' + accessPointArn.outpostId: ''; var serviceName = isOutpostArn ? 's3-outposts': 's3-accesspoint'; + var fipsSuffix = !isOutpostArn && req.service.config.useFipsEndpoint ? '-fips': ''; var dualStackSuffix = !isOutpostArn && req.service.config.useDualstack ? '.dualstack' : ''; var endpoint = req.httpRequest.endpoint; @@ -359,7 +368,7 @@ AWS.util.update(AWS.S3.prototype, { endpoint.hostname = [ accessPointArn.accessPoint + '-' + accessPointArn.accountId + outpostsSuffix, - serviceName + dualStackSuffix, + serviceName + fipsSuffix + dualStackSuffix, useArnRegion ? accessPointArn.region : req.service.config.region, dnsSuffix ].join('.'); @@ -368,9 +377,10 @@ AWS.util.update(AWS.S3.prototype, { // should be in the format: "accesspoint/${accesspointName}" var serviceName = 's3-object-lambda'; var accesspointName = accessPointArn.resource.split('/')[1]; + var fipsSuffix = req.service.config.useFipsEndpoint ? '-fips': ''; endpoint.hostname = [ accesspointName + '-' + accessPointArn.accountId, - serviceName, + serviceName + fipsSuffix, useArnRegion ? accessPointArn.region : req.service.config.region, dnsSuffix ].join('.'); diff --git a/scripts/region-checker/allowlist.js b/scripts/region-checker/allowlist.js index 3d7a2ffaff..50e3d4b64c 100644 --- a/scripts/region-checker/allowlist.js +++ b/scripts/region-checker/allowlist.js @@ -37,17 +37,17 @@ var allowlist = { '/services/s3.js': [ 87, 88, - 252, - 254, - 267, - 273, - 629, - 631, - 750, - 761, - 762, - 763, - 768 + 260, + 262, + 275, + 281, + 639, + 641, + 760, + 771, + 772, + 773, + 778 ] }; diff --git a/test/services/s3.spec.js b/test/services/s3.spec.js index e661d64d67..820f38e07c 100644 --- a/test/services/s3.spec.js +++ b/test/services/s3.spec.js @@ -3461,34 +3461,42 @@ describe('AWS.S3', function() { }); }); - it('should correctly generate access point endpoint for pseudo regions', function() { - s3 = new AWS.S3({region: 'us-east-1'}); + it('should correctly generate access point endpoint for s3-external-1', function() { + var client = new AWS.S3({region: 'us-east-1'}); helpers.mockHttpResponse(200, {}, ''); - var request = s3.getObject({ - Bucket: 'arn:aws:s3:s3-external-1:123456789012:accesspoint/myendpoint', - Key: 'key' + var request = client.listObjects({ + Bucket: 'arn:aws:s3:s3-external-1:123456789012:accesspoint/myendpoint' }); var built = request.build(function() {}); expect( built.httpRequest.endpoint.hostname ).to.equal('myendpoint-123456789012.s3-accesspoint.s3-external-1.amazonaws.com'); + }); - var testFipsError = (s3) => { - helpers.mockHttpResponse(200, {}, ''); - request = s3.getObject({ - Bucket: 'arn:aws:s3:us-east-1:123456789012:accesspoint/myendpoint', - Key: 'key' - }); - var error; - request.build(function(err) { - error = err; - }); - expect(error.name).to.equal('InvalidConfiguration'); - expect(error.message).to.equal('ARN endpoint is not compatible with FIPS region'); - }; - testFipsError(new AWS.S3({region: 'fips-us-east-1'})); - testFipsError(new AWS.S3({region: 'us-east-1-fips'})); - testFipsError(new AWS.S3({region: 'us-east-1', useFipsEndpoint: true})); + it('should correctly generate access point endpoint when useFipsEndpoint=true', function() { + var client = new AWS.S3({region: 'us-west-2', useFipsEndpoint: true}); + helpers.mockHttpResponse(200, {}, ''); + var request = client.listObjects({ + Bucket: 'arn:aws:s3:us-west-2:123456789012:accesspoint/myendpoint' + }); + var built = request.build(function() {}); + expect( + built.httpRequest.endpoint.hostname + ).to.equal('myendpoint-123456789012.s3-accesspoint-fips.us-west-2.amazonaws.com'); + }); + + it('should throw when fips region is passed in ARN', function() { + var client = new AWS.S3({region: 'us-west-2', useFipsEndpoint: true}); + helpers.mockHttpResponse(200, {}, ''); + var request = client.listObjects({ + Bucket: 'arn:aws:s3:fips-us-west-2:123456789012:accesspoint/myendpoint' + }); + var error; + request.build(function(err) { + error = err; + }); + expect(error.name).to.equal('InvalidConfiguration'); + expect(error.message).to.equal('FIPS region not allowed in ARN'); }); it('should use regions from ARN if s3UseArnRegion config is set to false', function(done) {