forked from aquasecurity/cloudsploit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GCP IAM Member Admin (aquasecurity#890)
- Loading branch information
1 parent
d4ebced
commit 77cc589
Showing
3 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
var async = require('async'); | ||
var helpers = require('../../../helpers/google'); | ||
|
||
module.exports = { | ||
title: 'Member Admin', | ||
category: 'IAM', | ||
description: 'Ensure that IAM members do not use primitive roles such as owner, editor or viewer.', | ||
more_info: 'For best security practices, use only predefined IAM roles and do not use primitive roles to prevent any unauthorized access to your resources.', | ||
link: 'https://cloud.google.com/iam/docs/overview', | ||
recommended_action: 'Ensure that no IAM member has a primitive role.', | ||
apis: ['projects:getIamPolicy', 'projects:get'], | ||
|
||
run: function(cache, settings, callback) { | ||
var results = []; | ||
var source = {}; | ||
var regions = helpers.regions(); | ||
|
||
let projects = helpers.addSource(cache, source, | ||
['projects','get', 'global']); | ||
|
||
if (!projects || projects.err || !projects.data || !projects.data.length) { | ||
helpers.addResult(results, 3, | ||
'Unable to query for projects: ' + helpers.addError(projects), 'global', null, null, (projects) ? projects.err : null); | ||
return callback(null, results, source); | ||
} | ||
|
||
var project = projects.data[0].name; | ||
|
||
async.each(regions.projects, function(region, rcb){ | ||
let iamPolicies = helpers.addSource(cache, source, | ||
['projects', 'getIamPolicy', region]); | ||
|
||
if (!iamPolicies) return rcb(); | ||
|
||
if (iamPolicies.err || !iamPolicies.data) { | ||
helpers.addResult(results, 3, 'Unable to query for IAM policies', region, null, null, iamPolicies.err); | ||
return rcb(); | ||
} | ||
|
||
if (!iamPolicies.data.length) { | ||
helpers.addResult(results, 0, 'No IAM policies found', region); | ||
return rcb(); | ||
} | ||
|
||
var iamPolicy = iamPolicies.data[0]; | ||
var primitiveRoleExists = false; | ||
|
||
iamPolicy.bindings.forEach(roleBinding => { | ||
if (roleBinding.role && ['roles/editor', 'roles/viewer', 'roles/owner'].includes(roleBinding.role)) { | ||
primitiveRoleExists = true; | ||
roleBinding.members.forEach(member => { | ||
let accountName = (member.includes(':')) ? member.split(':')[1] : member; | ||
let memberType = member.startsWith('serviceAccount') ? 'serviceAccounts' : 'users'; | ||
let resource = helpers.createResourceName(memberType, accountName, project); | ||
helpers.addResult(results, 2, | ||
`The account has the primitive role: ${roleBinding.role}`, region, resource); | ||
}); | ||
} | ||
}); | ||
|
||
if (!primitiveRoleExists) { | ||
helpers.addResult(results, 0, 'No accounts have primitive roles', region); | ||
} | ||
|
||
rcb(); | ||
}, function(){ | ||
// Global checking goes here | ||
callback(null, results, source); | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
var expect = require('chai').expect; | ||
var plugin = require('./memberAdmin'); | ||
|
||
const createCache = (err, data) => { | ||
return { | ||
projects: { | ||
getIamPolicy: { | ||
'global': { | ||
err: err, | ||
data: data | ||
} | ||
}, | ||
get: { | ||
'global': { | ||
data: [ { name: 'testproj' } ] | ||
} | ||
} | ||
}, | ||
} | ||
}; | ||
|
||
describe('memberAdmin', function () { | ||
describe('run', function () { | ||
it('should give passing result if no iam policies are found', function (done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.be.above(0); | ||
expect(results[0].status).to.equal(0); | ||
expect(results[0].message).to.include('No IAM policies found'); | ||
expect(results[0].region).to.equal('global'); | ||
done() | ||
}; | ||
|
||
const cache = createCache( | ||
null, | ||
[], | ||
); | ||
|
||
plugin.run(cache, {}, callback); | ||
}); | ||
|
||
it('should give passing result if no user has a primitive role', function (done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.be.above(0); | ||
expect(results[0].status).to.equal(0); | ||
expect(results[0].message).to.include('No accounts have primitive roles'); | ||
expect(results[0].region).to.equal('global'); | ||
done() | ||
}; | ||
|
||
const cache = createCache( | ||
null, | ||
[ | ||
{ | ||
"version": 1, | ||
"etag": "BwWXO8yOKJo=", | ||
"bindings": [ | ||
{ | ||
"role": "roles/cloudbuild.builds.builder", | ||
"members": [ | ||
"serviceAccount:[email protected]" | ||
] | ||
}, | ||
{ | ||
"role": "roles/cloudbuild.serviceAgent", | ||
"members": [ | ||
"serviceAccount:[email protected]" | ||
] | ||
}, | ||
{ | ||
"role": "roles/compute.admin", | ||
"members": [ | ||
"serviceAccount:[email protected]" | ||
] | ||
} | ||
] | ||
} | ||
] | ||
); | ||
|
||
plugin.run(cache, {}, callback); | ||
}); | ||
|
||
it('should give failing result if a user has a primitive role', function (done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.be.above(0); | ||
expect(results[0].status).to.equal(2); | ||
expect(results[0].message).to.include('The account has the primitive role'); | ||
expect(results[0].region).to.equal('global'); | ||
done() | ||
}; | ||
|
||
const cache = createCache( | ||
null, | ||
[ | ||
{ | ||
"version": 1, | ||
"etag": "BwWXO8yOKJo=", | ||
"bindings": [ | ||
{ | ||
"role": "roles/editor", | ||
"members": [ | ||
"serviceAccount:[email protected]", | ||
"serviceAccount:[email protected]" | ||
] | ||
}, | ||
{ | ||
"role": "roles/owner", | ||
"members": [ | ||
"user:[email protected]" | ||
] | ||
}, | ||
{ | ||
"role": "roles/viewer", | ||
"members": [ | ||
"serviceAccount:[email protected]", | ||
"serviceAccount:[email protected]" | ||
] | ||
} | ||
] | ||
} | ||
] | ||
); | ||
|
||
plugin.run(cache, {}, callback); | ||
}) | ||
|
||
}) | ||
}); |