Skip to content

Commit 43b128d

Browse files
committed
Merge branch 'improvement/CLDSRV-741-test-bypass-policy' into tmp/octopus/w/9.1/improvement/CLDSRV-741-test-bypass-policy
2 parents b99fa4a + 602d1b6 commit 43b128d

File tree

2 files changed

+278
-1
lines changed

2 files changed

+278
-1
lines changed

.github/docker/vault-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
},
1616
"sts": {
1717
"address": "127.0.0.1",
18-
"port": 8800
18+
"port": 8650
1919
}
2020
},
2121
"map": ["127.0.0.1:4300", "127.0.0.2:4301", "127.0.0.3:4302", "127.0.0.4:4303", "127.0.0.5:4304"],
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
const assert = require('assert');
2+
const { S3, IAM, STS } = require('aws-sdk');
3+
const { v4: uuid } = require('uuid');
4+
5+
const getConfig = require('../support/config');
6+
const BucketUtility = require('../../lib/utility/bucket-util');
7+
const { config } = require('../../../../../lib/Config');
8+
9+
const bucketName = `bp-bypass-${uuid()}`;
10+
const userName = `bp-bypass-user-${uuid()}`;
11+
const roleName = `bp-bypass-role-${uuid()}`;
12+
const objectKey = 'test-object';
13+
const objectContent = 'test content';
14+
15+
// this test needs a real vault to create user and role
16+
const isVaultScality = config.backends.auth !== 'mem';
17+
const internalPortBypassBP = config.internalPort;
18+
const vaultHost = config.vaultd?.host || 'localhost';
19+
20+
// will run for jobs s3c-ft-tests and in Integration tests
21+
const describeBypass = isVaultScality && internalPortBypassBP ? describe : describe.skip;
22+
describeBypass('Bucket Policy Bypass Port', () => {
23+
const bucketUtilAccount = new BucketUtility('default');
24+
const s3ClientAccount = bucketUtilAccount.s3;
25+
26+
const iamConfig = getConfig('default', { region: 'us-east-1' });
27+
iamConfig.endpoint = `http://${vaultHost}:8600`; // define outside of getConfig for Integration
28+
const iamClient = new IAM(iamConfig);
29+
30+
let policyAllowAllActions;
31+
32+
before(async () => {
33+
await bucketUtilAccount.createOne(bucketName);
34+
await s3ClientAccount
35+
.putObject({
36+
Bucket: bucketName,
37+
Key: objectKey,
38+
Body: objectContent,
39+
})
40+
.promise();
41+
42+
await s3ClientAccount
43+
.putBucketPolicy({
44+
Bucket: bucketName,
45+
Policy: JSON.stringify({
46+
Version: '2012-10-17',
47+
Statement: [
48+
{
49+
Sid: 'DenyAllAccess',
50+
Effect: 'Deny',
51+
Principal: '*',
52+
Action: 's3:*',
53+
Resource: [`arn:aws:s3:::${bucketName}`, `arn:aws:s3:::${bucketName}/*`],
54+
},
55+
],
56+
}),
57+
})
58+
.promise();
59+
60+
// create iam policy allow all actions for user and role
61+
const policyRes = await iamClient
62+
.createPolicy({
63+
PolicyName: 'bp-bypass-policy',
64+
PolicyDocument: JSON.stringify({
65+
Version: '2012-10-17',
66+
Statement: [
67+
{
68+
Sid: 'AllowAllActions',
69+
Effect: 'Allow',
70+
Action: '*',
71+
Resource: ['*'],
72+
},
73+
],
74+
}),
75+
})
76+
.promise();
77+
78+
policyAllowAllActions = policyRes.Policy;
79+
await iamClient.createUser({ UserName: userName }).promise();
80+
await iamClient
81+
.attachUserPolicy({
82+
UserName: userName,
83+
PolicyArn: policyAllowAllActions.Arn,
84+
})
85+
.promise();
86+
});
87+
88+
after(async () => {
89+
// Remove bucket policy first even if root account can cleanup
90+
await s3ClientAccount.deleteBucketPolicy({ Bucket: bucketName }).promise();
91+
await bucketUtilAccount.empty(bucketName);
92+
await bucketUtilAccount.deleteOne(bucketName);
93+
94+
if (policyAllowAllActions) {
95+
await iamClient
96+
.detachUserPolicy({
97+
UserName: userName,
98+
PolicyArn: policyAllowAllActions.Arn,
99+
})
100+
.promise();
101+
await iamClient.deletePolicy({ PolicyArn: policyAllowAllActions.Arn }).promise();
102+
}
103+
await iamClient.deleteUser({ UserName: userName }).promise();
104+
});
105+
106+
it('should allow account root access on s3 port', async () => {
107+
const getResponse = await s3ClientAccount
108+
.getObject({
109+
Bucket: bucketName,
110+
Key: objectKey,
111+
})
112+
.promise();
113+
assert(getResponse.Body, 'Should be able to get object');
114+
assert.strictEqual(getResponse.Body.toString(), objectContent);
115+
});
116+
117+
describe('IAM User Access Tests', () => {
118+
let userS3Client;
119+
let userInternalBypassBPS3Client;
120+
121+
before(async () => {
122+
const accessKeyResponse = await iamClient.createAccessKey({ UserName: userName }).promise();
123+
const { AccessKeyId, SecretAccessKey } = accessKeyResponse.AccessKey;
124+
125+
// Create S3 client for test user (regular port)
126+
const userConfig = getConfig('default', {
127+
credentials: {
128+
accessKeyId: AccessKeyId,
129+
secretAccessKey: SecretAccessKey,
130+
},
131+
});
132+
userS3Client = new S3(userConfig);
133+
134+
// Create S3 client for internal port - bypasses bucket policy
135+
const userInternalBypassBPConfig = getConfig('default', {
136+
credentials: {
137+
accessKeyId: AccessKeyId,
138+
secretAccessKey: SecretAccessKey,
139+
},
140+
});
141+
userInternalBypassBPConfig.endpoint = `http://localhost:${internalPortBypassBP}`;
142+
userInternalBypassBPS3Client = new S3(userInternalBypassBPConfig);
143+
});
144+
145+
it('should deny user access on s3 port', async () => {
146+
try {
147+
await userS3Client
148+
.getObject({
149+
Bucket: bucketName,
150+
Key: objectKey,
151+
})
152+
.promise();
153+
assert.fail('Expected AccessDenied error for getObject');
154+
} catch (err) {
155+
assert.strictEqual(err.code, 'AccessDenied');
156+
}
157+
});
158+
159+
it('should bypass user bucket policy on internal port', async () => {
160+
const getResponse = await userInternalBypassBPS3Client
161+
.getObject({
162+
Bucket: bucketName,
163+
Key: objectKey,
164+
})
165+
.promise();
166+
assert(getResponse.Body, 'Should be able to get object on internal port');
167+
assert.strictEqual(getResponse.Body.toString(), objectContent);
168+
});
169+
});
170+
171+
describe('Role Access Tests', () => {
172+
let roleS3Client;
173+
let roleInternalBypassBPS3Client;
174+
let stsClient;
175+
176+
before(async () => {
177+
const roleRes = await iamClient
178+
.createRole({
179+
RoleName: roleName,
180+
AssumeRolePolicyDocument: JSON.stringify({
181+
Version: '2012-10-17',
182+
Statement: [
183+
{
184+
Effect: 'Allow',
185+
Principal: '*',
186+
Action: 'sts:AssumeRole',
187+
},
188+
],
189+
}),
190+
})
191+
.promise();
192+
193+
await iamClient
194+
.attachRolePolicy({
195+
RoleName: roleName,
196+
PolicyArn: policyAllowAllActions.Arn,
197+
})
198+
.promise();
199+
200+
const { AccessKey } = await iamClient.createAccessKey({ UserName: userName }).promise();
201+
202+
const stsConfig = getConfig('default', {
203+
region: 'us-east-1',
204+
credentials: {
205+
accessKeyId: AccessKey.AccessKeyId,
206+
secretAccessKey: AccessKey.SecretAccessKey,
207+
},
208+
});
209+
stsConfig.endpoint = `http://${vaultHost}:8650`;
210+
stsClient = new STS(stsConfig);
211+
212+
// Assume role to get temporary credentials
213+
const { Credentials } = await stsClient
214+
.assumeRole({
215+
RoleArn: roleRes.Role.Arn,
216+
RoleSessionName: 'bp-bypass-session',
217+
})
218+
.promise();
219+
220+
// Create S3 client for role (regular port)
221+
const roleConfig = getConfig('default', {
222+
credentials: {
223+
accessKeyId: Credentials.AccessKeyId,
224+
secretAccessKey: Credentials.SecretAccessKey,
225+
sessionToken: Credentials.SessionToken,
226+
},
227+
});
228+
roleS3Client = new S3(roleConfig);
229+
230+
// Create S3 client for internal port - bypasses bucket policy
231+
const roleInternalBypassBPConfig = getConfig('default', {
232+
credentials: {
233+
accessKeyId: Credentials.AccessKeyId,
234+
secretAccessKey: Credentials.SecretAccessKey,
235+
sessionToken: Credentials.SessionToken,
236+
},
237+
});
238+
roleInternalBypassBPConfig.endpoint = `http://localhost:${internalPortBypassBP}`;
239+
roleInternalBypassBPS3Client = new S3(roleInternalBypassBPConfig);
240+
});
241+
242+
after(async () => {
243+
await iamClient
244+
.detachRolePolicy({
245+
RoleName: roleName,
246+
PolicyArn: policyAllowAllActions.Arn,
247+
})
248+
.promise();
249+
await iamClient.deleteRole({ RoleName: roleName }).promise();
250+
});
251+
252+
it('should deny role access on s3 port', async () => {
253+
try {
254+
await roleS3Client
255+
.getObject({
256+
Bucket: bucketName,
257+
Key: objectKey,
258+
})
259+
.promise();
260+
assert.fail('Expected AccessDenied error for getObject');
261+
} catch (err) {
262+
assert.strictEqual(err.code, 'AccessDenied');
263+
}
264+
});
265+
266+
it('should bypass role bucket policy on internal port', async () => {
267+
const getResponse = await roleInternalBypassBPS3Client
268+
.getObject({
269+
Bucket: bucketName,
270+
Key: objectKey,
271+
})
272+
.promise();
273+
assert(getResponse.Body, 'Should be able to get object on internal port');
274+
assert.strictEqual(getResponse.Body.toString(), objectContent);
275+
});
276+
});
277+
});

0 commit comments

Comments
 (0)