Skip to content

Commit b5155d8

Browse files
authored
Merge pull request #9267 from naveenpaul1/bucket_arn_upgrade
IAM | Bucket policy Principal upgrade script
2 parents 2af53d0 + 3c1fcc3 commit b5155d8

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

src/endpoint/s3/s3_rest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ async function authorize_request_policy(req) {
237237
public_access_block,
238238
} = await req.object_sdk.read_bucket_sdk_policy_info(req.params.bucket);
239239

240+
dbg.log2('authorize_request_policy:', { bucket_owner, owner_account });
240241
const auth_token = req.object_sdk.get_auth_token();
241242
const arn_path = _get_arn_from_req_path(req);
242243
const method = _get_method_from_req(req);

src/test/integration_tests/internal/test_upgrade_scripts.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
// setup coretest first to prepare the env
55
const _ = require('lodash');
66
const coretest = require('../../utils/coretest/coretest');
7-
const { rpc_client, EMAIL } = coretest;
7+
const { rpc_client, EMAIL, POOL_LIST } = coretest;
88
coretest.setup({ pools_to_create: [coretest.POOL_LIST[1]] });
99
const { S3 } = require('@aws-sdk/client-s3');
1010
const { NodeHttpHandler } = require("@smithy/node-http-handler");
1111
const http = require('http');
12+
const SensitiveString = require('../../../util/sensitive_string');
1213
const system_store = require('../../../server/system_services/system_store').get_instance();
1314
const upgrade_bucket_policy = require('../../../upgrade/upgrade_scripts/5.15.6/upgrade_bucket_policy');
15+
const upgrade_bucket_policy_principal = require('../../../upgrade/upgrade_scripts/5.21.0/upgrade_bucket_policy_principal');
1416
const upgrade_bucket_cors = require('../../../upgrade/upgrade_scripts/5.19.0/upgrade_bucket_cors');
1517
const remove_mongo_pool = require('../../../upgrade/upgrade_scripts/5.20.0/remove_mongo_pool');
1618
const dbg = require('../../../util/debug_module')(__filename);
@@ -20,6 +22,7 @@ const config = require('../../../../config');
2022

2123
const BKT = 'test-bucket';
2224
const BKT1 = 'test-bucket1';
25+
const iam_username = 'iam_username';
2326
/** @type {S3} */
2427
let s3;
2528

@@ -199,8 +202,85 @@ mocha.describe('test upgrade scripts', async function() {
199202
assert.strictEqual(updated_bucket.tiering.tiers[0].tier.mirrors[0].spread_pools[0].name, default_pool_name);
200203
});
201204

205+
mocha.it('test upgrade bucket policy to ARN version 5.21.0', async function() {
206+
const old_policy = {
207+
Version: '2012-10-17',
208+
Statement: [{
209+
Sid: 'id-1',
210+
Effect: 'Allow',
211+
Principal: {
212+
"AWS": [new SensitiveString(EMAIL)],
213+
},
214+
Action: ['s3:GetObject', 's3:*'],
215+
Resource: [`arn:aws:s3:::*`]
216+
},
217+
{
218+
Effect: 'Deny',
219+
Principal: {
220+
"AWS": [new SensitiveString(iam_username)],
221+
},
222+
Action: ['s3:PutObject'],
223+
Resource: [`arn:aws:s3:::*`]
224+
},
225+
]
226+
};
227+
// clean all leftover bucket policies as upgrade script doesn't work on updated policies
228+
await _clean_all_bucket_policies();
229+
230+
const bucket = system_store.data.buckets.find(bucket_obj => bucket_obj.name.unwrap() === BKT);
231+
await system_store.make_changes({
232+
update: {
233+
buckets: [{
234+
_id: bucket._id,
235+
s3_policy: old_policy
236+
}]
237+
}
238+
});
239+
const account = system_store.data.accounts.find(acc => acc.email.unwrap() === EMAIL);
240+
const nsr = 's3_bucket_policy_nsr';
241+
const iam_acc = {
242+
name: iam_username,
243+
email: iam_username,
244+
has_login: false,
245+
s3_access: true,
246+
default_resource: process.env.NC_CORETEST ? nsr : POOL_LIST[1].name,
247+
};
248+
await rpc_client.account.create_account(iam_acc);
249+
250+
const iam_account = system_store.data.accounts.find(acc => acc.email.unwrap() === iam_username);
251+
await system_store.make_changes({
252+
update: {
253+
accounts: [{
254+
_id: iam_account._id,
255+
owner: account._id.toString(),
256+
}]
257+
}
258+
});
259+
260+
await upgrade_bucket_policy_principal.run({ dbg, system_store, system_server: null });
261+
const res = await s3.getBucketPolicy({ // should work - bucket policy should fit current schema
262+
Bucket: BKT,
263+
});
264+
const new_policy = JSON.parse(res.Policy);
265+
266+
assert.strictEqual(new_policy.Statement.length, old_policy.Statement.length);
267+
assert.strictEqual(new_policy.Version, old_policy.Version);
268+
assert.strictEqual(new_policy.Statement[0].Sid, old_policy.Statement[0].Sid);
269+
assert.strictEqual(new_policy.Statement[0].Effect, 'Allow');
270+
assert.strictEqual(new_policy.Statement[0].Action[0], 's3:GetObject');
271+
assert.strictEqual(new_policy.Statement[0].Action[1], 's3:*');
272+
assert.strictEqual(new_policy.Statement[0].Resource[0], old_policy.Statement[0].Resource[0]);
273+
274+
assert.strictEqual(new_policy.Statement[0].Principal.AWS[0], `arn:aws:iam::${account._id.toString()}:root`);
275+
assert.strictEqual(new_policy.Statement[1].Principal.AWS[0], `arn:aws:iam::${iam_account._id.toString()}:user/${iam_account.email.unwrap()}`);
276+
});
277+
202278
mocha.after(async function() {
203279
await s3.deleteBucket({ Bucket: BKT });
204280
await s3.deleteBucket({ Bucket: BKT1 });
281+
const iam_acc = {
282+
email: iam_username,
283+
};
284+
await rpc_client.account.delete_account(iam_acc);
205285
});
206286
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Copyright (C) 2023 NooBaa */
2+
"use strict";
3+
4+
const iam_constants = require('../../../endpoint/iam/iam_constants');
5+
6+
function _create_arn(dbg, principals, system_store) {
7+
if (!principals.AWS) return;
8+
const principal_arns = [];
9+
for (const principal of principals.AWS) {
10+
if (principal === '*') continue;
11+
const account = system_store.data.accounts.find(acc => acc.email.unwrap() === principal.unwrap());
12+
if (!account) {
13+
dbg.log0(`Could not find the account with email: ${principal}`);
14+
continue;
15+
}
16+
let arn;
17+
if (account.owner) {
18+
const iam_path = account.iam_path || iam_constants.IAM_DEFAULT_PATH;
19+
arn = `arn:aws:iam::${account._id.toString()}:user${iam_path}${account.email.unwrap()}`;
20+
} else {
21+
arn = `arn:aws:iam::${account._id.toString()}:root`;
22+
}
23+
principal_arns.push(arn);
24+
}
25+
return { AWS: principal_arns };
26+
}
27+
28+
async function run({ dbg, system_store, system_server }) {
29+
try {
30+
dbg.log0('Starting bucket policy Principal upgrade...');
31+
const buckets = [];
32+
for (const bucket of system_store.data.buckets) {
33+
// Do not update if there are no bucket policy.
34+
if (!bucket.s3_policy) continue;
35+
if (bucket.s3_policy.Statement !== undefined) {
36+
const new_policy = bucket.s3_policy;
37+
new_policy.Statement = bucket.s3_policy.Statement.map(statement => ({
38+
...statement,
39+
Principal: _create_arn(dbg, statement.Principal, system_store),
40+
}));
41+
buckets.push({
42+
_id: bucket._id,
43+
s3_policy: new_policy,
44+
});
45+
}
46+
}
47+
48+
if (buckets.length > 0) {
49+
dbg.log0(`Replacing bucket policy Principal for ${buckets.length} buckets.`);
50+
await system_store.make_changes({ update: { buckets } });
51+
} else {
52+
dbg.log0('Upgrading buckets policy Principal: no upgrade needed...');
53+
}
54+
55+
} catch (err) {
56+
dbg.error('Got error while upgrading buckets policy Principal:', err);
57+
throw err;
58+
}
59+
}
60+
61+
62+
module.exports = {
63+
run,
64+
description: 'Update bucket policy Principal to ARN format'
65+
};

0 commit comments

Comments
 (0)