-
Notifications
You must be signed in to change notification settings - Fork 673
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added AWS IAM Policies Present plugin and test cases
- Loading branch information
1 parent
9d5683d
commit 9e9d103
Showing
3 changed files
with
267 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,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); | ||
}); | ||
} | ||
}; |
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,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(); | ||
}); | ||
}); | ||
}); | ||
}); |