Skip to content

Commit

Permalink
Added AWS IAM Policies Present plugin and test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
AkhtarAmir committed Apr 9, 2021
1 parent 9d5683d commit 9e9d103
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 0 deletions.
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ module.exports = {
'kmsKeyPolicy' : require(__dirname + '/plugins/aws/kms/kmsKeyPolicy.js'),
'kmsDefaultKeyUsage' : require(__dirname + '/plugins/aws/kms/kmsDefaultKeyUsage.js'),
'kmsAppTierCmk' : require(__dirname + '/plugins/aws/kms/kmsAppTierCmk.js'),
'iamPoliciesPresent' : require(__dirname + '/plugins/aws/iam/iamPoliciesPresent.js'),

'iamDbAuthenticationEnabled' : require(__dirname + '/plugins/aws/rds/iamDbAuthenticationEnabled.js'),
'rdsAutomatedBackups' : require(__dirname + '/plugins/aws/rds/rdsAutomatedBackups.js'),
Expand Down
107 changes: 107 additions & 0 deletions plugins/aws/iam/iamPoliciesPresent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
var async = require('async');
var helpers = require('../../../helpers/aws');

var managedAdminPolicy = 'arn:aws:iam::aws:policy/AdministratorAccess';

module.exports = {
title: 'IAM Policies Present',
category: 'IAM',
description: 'Ensure that required policies are present in all IAM roles.',
more_info: 'Validate the presence of required policies in IAM roles in order to follow your organizations\'s security and compliance requirements.',
link: 'https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html',
recommended_action: 'Modify IAM roles to attach required policies',
apis: ['IAM:listRoles', 'IAM:listRolePolicies', 'IAM:listAttachedRolePolicies'],
settings: {
iam_required_policy_names: {
name: 'IAM Required Policy Names',
description: 'A comma separated list of IAM policy names that all IAM roles should have',
regex: '^.*$',
default: ''
}
},

run: function(cache, settings, callback) {
var config = {
iam_required_policy_names: settings.iam_required_policy_names || this.settings.iam_required_policy_names.default
};

if (!config.iam_required_policy_names.length) return callback(null, results, source);

config.iam_required_policy_names = config.iam_required_policy_names.split(',');

var results = [];
var source = {};

var region = helpers.defaultRegion(settings);

var listRoles = helpers.addSource(cache, source,
['iam', 'listRoles', region]);

if (!listRoles) return callback(null, results, source);

if (listRoles.err || !listRoles.data) {
helpers.addResult(results, 3,
'Unable to query for IAM roles: ' + helpers.addError(listRoles));
return callback(null, results, source);
}

if (!listRoles.data.length) {
helpers.addResult(results, 0, 'No IAM roles found');
return callback(null, results, source);
}

async.each(listRoles.data, function(role, cb){
if (!role.RoleName) return cb();

// Get managed policies attached to role
var listAttachedRolePolicies = helpers.addSource(cache, source,
['iam', 'listAttachedRolePolicies', region, role.RoleName]);

// Get inline policies attached to role
var listRolePolicies = helpers.addSource(cache, source,
['iam', 'listRolePolicies', region, role.RoleName]);

if (!listAttachedRolePolicies || listAttachedRolePolicies.err) {
helpers.addResult(results, 3,
'Unable to query for IAM attached policy for role: ' + role.RoleName + ': ' + helpers.addError(listAttachedRolePolicies), region, role.Arn);
return cb();
}

if (!listRolePolicies || listRolePolicies.err) {
helpers.addResult(results, 3,
'Unable to query for IAM role policy for role: ' + role.RoleName + ': ' + helpers.addError(listRolePolicies), region, role.Arn);
return cb();
}

var attachedPolicies = [];
var difference = [];

// See if role has admin managed policy
if (listAttachedRolePolicies.data &&
listAttachedRolePolicies.data.AttachedPolicies) {

for (let policy of listAttachedRolePolicies.data.AttachedPolicies) {
attachedPolicies.push(policy.PolicyName);
}
}

if (listRolePolicies.data && listRolePolicies.data.PolicyNames) attachedPolicies = attachedPolicies.concat(listRolePolicies.data.PolicyNames);

for (let policy of config.iam_required_policy_names) {
if (!attachedPolicies.includes(policy)) difference.push(policy);
}

if (difference.length) {
helpers.addResult(results, 2,
`IAM role does not have these required policies attached: ${difference.join(', ')}`, region, role.RoleName);
} else {
helpers.addResult(results, 0,
'IAM role has all required policies attached', region, role.RoleName);
}

cb();
}, function(){
callback(null, results, source);
});
}
};
159 changes: 159 additions & 0 deletions plugins/aws/iam/iamPoliciesPresent.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
const expect = require('chai').expect;
var iamPoliciesPresent = require('./iamPoliciesPresent');


const listRoles = [
{
"Path": "/",
"RoleName": "test-role-1",
"RoleId": "AROAYE32SRU5VIMXXL3BH",
"Arn": "arn:aws:iam::000011112222:role/test-role-1",
"CreateDate": "2020-11-21T23:56:33Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::000011112222:root"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {}
}
]
},
"MaxSessionDuration": 3600
}
];

const listRolePolicies = [
{
"PolicyNames": [
"S3-Full"
]
}
];

const listAttachedRolePolicies = [
{
"ResponseMetadata": {
"RequestId": 'f7d427cc-970b-47af-9b7d-3e06121f83da'
},
"AttachedPolicies": [
{
"PolicyName": 'AdministratorAccess',
"PolicyArn": 'arn:aws:iam::aws:policy/AdministratorAccess'
}
],
"IsTruncated": false
}
];

const createCache = (listRoles, listAttachedRolePolicies, listRolePolicies, listRolesErr, listAttachedRolePoliciesErr, listRolePoliciesErr) => {
var roleName = (listRoles && listRoles.length) ? listRoles[0].RoleName : null;
return {
iam: {
listRoles: {
'us-east-1': {
err: listRolesErr,
data: listRoles
}
},
listAttachedRolePolicies: {
'us-east-1': {
[roleName]: {
err: listAttachedRolePoliciesErr,
data: listAttachedRolePolicies
}
}
},
listRolePolicies: {
'us-east-1': {
[roleName]: {
err: listRolePoliciesErr,
data: listRolePolicies
}
}
}
}
};
};

const createNullCache = () => {
return {
lambda: {
listRoles: {
'us-east-1': null
}
}
};
};

describe('iamPoliciesPresent', function () {
describe('run', function () {

it('should PASS if IAM role has all required policies attached', function (done) {
const cache = createCache([listRoles[0]], listAttachedRolePolicies[0], listRolePolicies[0]);
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full,AdministratorAccess' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].region).to.equal('us-east-1');
done();
});
});

it('should FAIL if IAM role does not have required policies attached', function (done) {
const cache = createCache([listRoles[0]], {}, listRolePolicies[0]);
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'AdministratorAccess' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].region).to.equal('us-east-1');
done();
});
});

it('should PASS if on IAM roles found', function (done) {
const cache = createCache([]);
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
done();
});
});

it('should UNKNOWN if unable to list IAM roles', function (done) {
const cache = createCache(null, null, null, { message: 'Unable to list IAM roles'});
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(3);
done();
});
});

it('should UNKNOWN if unable to list attached role policies', function (done) {
const cache = createCache([listRoles[0]], {}, null, { message: 'Unable to list attached role policies'});
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(3);
done();
});
});

it('should UNKNOWN if unable to list role policies', function (done) {
const cache = createCache([listRoles[0]], listAttachedRolePolicies[0], {}, null, null, { message: 'Unable to query role policies'});
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(3);
done();
});
});

it('should not return anything if list roles response not found', function (done) {
const cache = createNullCache();
iamPoliciesPresent.run(cache, { iam_required_policy_names: 'S3-Full' }, (err, results) => {
expect(results.length).to.equal(0);
done();
});
});
});
});

0 comments on commit 9e9d103

Please sign in to comment.