Skip to content

Commit 747bfb5

Browse files
committed
CLDSRV-789: bucket logging check that the source and destination bucket
are in the same account
1 parent 1f159ff commit 747bfb5

File tree

3 files changed

+123
-3
lines changed

3 files changed

+123
-3
lines changed

lib/api/bucketPutLogging.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const monitoring = require('../utilities/monitoringHandler');
77
const { errorInstances } = require('arsenal');
88
const { config } = require('../Config');
99

10+
const ERROR_MSG_SOURCE_TARGET_BUCKET_OWNER_MISMATCH =
11+
'The owner for the bucket to be logged and the target bucket must be the same.';
12+
1013
function bucketPutLogging(authInfo, request, log, callback) {
1114
log.debug('processing request', { method: 'bucketPutLogging' });
1215

@@ -43,11 +46,16 @@ function bucketPutLogging(authInfo, request, log, callback) {
4346
return next(null, bucket);
4447
}
4548

46-
return metadata.getBucket(loggingEnabled.TargetBucket, log, err => {
49+
return metadata.getBucket(loggingEnabled.TargetBucket, log, (err, targetBucket) => {
4750
if (err) {
4851
return next(errorInstances.InvalidTargetBucketForLogging.customizeDescription(err.description));
4952
}
5053

54+
if (targetBucket.getOwner() !== bucket.getOwner()) {
55+
return next(errorInstances.InvalidTargetBucketForLogging
56+
.customizeDescription(ERROR_MSG_SOURCE_TARGET_BUCKET_OWNER_MISMATCH));
57+
}
58+
5159
return next(null, bucket);
5260
});
5361
},

tests/functional/aws-node-sdk/test/bucket/putBucketLogging.js

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ const validLoggingConfigWithGrants = {
3636
},
3737
};
3838

39+
const itSkipIfAWS = process.env.AWS_ON_AIR ? it.skip : it;
40+
3941
describe('PUT bucket logging', () => {
4042
withV4(sigCfg => {
4143
const bucketUtil = new BucketUtility('default', sigCfg);
@@ -102,7 +104,7 @@ describe('PUT bucket logging', () => {
102104
});
103105
});
104106

105-
it('should return NotImplemented if TargetGrants is present', done => {
107+
itSkipIfAWS('should return NotImplemented if TargetGrants is present', done => {
106108
_testPutBucketLoggingError(s3, validLoggingConfigWithGrants, 501, 'NotImplemented', done);
107109
});
108110

@@ -137,7 +139,7 @@ describe('PUT bucket logging', () => {
137139
});
138140
});
139141

140-
it('should return MethodNotAllowed if user is not bucket owner', done => {
142+
itSkipIfAWS('should return MethodNotAllowed if user is not bucket owner', done => {
141143
_testPutBucketLoggingError(otherAccountS3, validLoggingConfig, 405, 'MethodNotAllowed', done);
142144
});
143145

@@ -151,6 +153,73 @@ describe('PUT bucket logging', () => {
151153
};
152154
return _testPutBucketLoggingError(s3, invalidConfig, 400, 'InvalidTargetBucketForLogging', done);
153155
});
156+
157+
it('should allow logging when target bucket is owned by same account', done => {
158+
// Both buckets created by same account, should succeed
159+
s3.putBucketLogging({
160+
Bucket: bucketName,
161+
BucketLoggingStatus: validLoggingConfig,
162+
}, err => {
163+
assert.ifError(err);
164+
// Verify the config was set
165+
s3.getBucketLogging({ Bucket: bucketName }, (err, data) => {
166+
assert.ifError(err);
167+
assert(data.LoggingEnabled);
168+
assert.strictEqual(data.LoggingEnabled.TargetBucket, targetBucket);
169+
return done();
170+
});
171+
});
172+
});
173+
});
174+
175+
describe('with cross-account target bucket', () => {
176+
const otherAccountTargetBucket = 'other-account-target-bucket';
177+
178+
beforeEach(done => {
179+
process.stdout.write('Creating buckets\n');
180+
return s3.createBucket({ Bucket: bucketName }, err => {
181+
if (err) {
182+
return done(err);
183+
}
184+
return otherAccountS3.createBucket({ Bucket: otherAccountTargetBucket }, done);
185+
});
186+
});
187+
188+
afterEach(done => {
189+
process.stdout.write('Deleting buckets\n');
190+
Promise.allSettled([
191+
bucketUtil.deleteOne(bucketName),
192+
otherAccountBucketUtility.deleteOne(otherAccountTargetBucket),
193+
]).then(results => {
194+
const errors = results
195+
.filter(r => r.status === 'rejected' && r.reason?.code !== 'NoSuchBucket')
196+
.map(r => r.reason);
197+
if (errors.length > 0) {
198+
return done(errors[0]);
199+
}
200+
return done();
201+
});
202+
});
203+
204+
it('should return InvalidTargetBucketForLogging when target bucket is owned by different account', done => {
205+
// Try to set logging from first account's bucket to second account's bucket
206+
const crossAccountConfig = {
207+
LoggingEnabled: {
208+
TargetBucket: otherAccountTargetBucket,
209+
TargetPrefix: 'logs/',
210+
},
211+
};
212+
213+
s3.putBucketLogging({
214+
Bucket: bucketName,
215+
BucketLoggingStatus: crossAccountConfig,
216+
}, err => {
217+
assert(err, 'Expected error but found none');
218+
assert.strictEqual(err.code, 'InvalidTargetBucketForLogging');
219+
assert.strictEqual(err.statusCode, 400);
220+
done();
221+
});
222+
});
154223
});
155224
});
156225
});

tests/unit/api/bucketPutLogging.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,47 @@ describe('bucketPutLogging API', () => {
305305
done();
306306
});
307307
});
308+
309+
it('should allow logging when target bucket is owned by requester', done => {
310+
// Both buckets are created by authInfo, so this should succeed
311+
const loggingXML = createValidLoggingXML(targetBucket);
312+
const request = createLoggingRequest(bucketName, loggingXML);
313+
314+
bucketPutLogging(authInfo, request, log, err => {
315+
assert.ifError(err);
316+
metadata.getBucket(bucketName, log, (err, bucket) => {
317+
assert.ifError(err);
318+
const loggingConfig = bucket.getBucketLoggingStatus();
319+
assert(loggingConfig);
320+
assert.strictEqual(loggingConfig.getLoggingEnabled().TargetBucket, targetBucket);
321+
done();
322+
});
323+
});
324+
});
325+
326+
it('should reject logging when source and target bucket account mismatch', done => {
327+
// Create a target bucket with different account
328+
const otherAccountTargetBucket = 'other-account-target-bucket';
329+
const otherAccountRequest = {
330+
bucketName: otherAccountTargetBucket,
331+
namespace,
332+
headers: { host: `${otherAccountTargetBucket}.s3.amazonaws.com` },
333+
url: '/',
334+
actionImplicitDenies: false,
335+
};
336+
337+
bucketPut(otherAuthInfo, otherAccountRequest, log, err => {
338+
assert.ifError(err);
339+
340+
// Now try to set logging from authInfo's bucket to otherAuthInfo's bucket
341+
const loggingXML = createValidLoggingXML(otherAccountTargetBucket);
342+
const request = createLoggingRequest(bucketName, loggingXML);
343+
344+
bucketPutLogging(authInfo, request, log, err => {
345+
assert(err);
346+
assert.strictEqual(err.is.InvalidTargetBucketForLogging, true);
347+
done();
348+
});
349+
});
350+
});
308351
});

0 commit comments

Comments
 (0)