Skip to content

Commit

Permalink
GCP IAM Member Admin (aquasecurity#890)
Browse files Browse the repository at this point in the history
  • Loading branch information
mehakseedat63 authored Sep 29, 2021
1 parent d4ebced commit 77cc589
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ module.exports = {
'serviceAccountKeyRotation' : require(__dirname + '/plugins/google/iam/serviceAccountKeyRotation.js'),
'serviceAccountManagedKeys' : require(__dirname + '/plugins/google/iam/serviceAccountManagedKeys.js'),
'corporateEmailsOnly' : require(__dirname + '/plugins/google/iam/corporateEmailsOnly.js'),
'memberAdmin' : require(__dirname + '/plugins/google/iam/memberAdmin.js'),

'privateEndpoint' : require(__dirname + '/plugins/google/kubernetes/privateEndpoint.js'),
'monitoringEnabled' : require(__dirname + '/plugins/google/kubernetes/monitoringEnabled.js'),
Expand Down
71 changes: 71 additions & 0 deletions plugins/google/iam/memberAdmin.js
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);
});
}
};
128 changes: 128 additions & 0 deletions plugins/google/iam/memberAdmin.spec.js
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);
})

})
});

0 comments on commit 77cc589

Please sign in to comment.