mirror of
https://github.com/inspec/inspec
synced 2024-11-23 05:03:07 +00:00
Improvements and matcher renaming on aws_iam_password_policy (#2638)
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
33787124a7
commit
6c0422fbf0
6 changed files with 127 additions and 51 deletions
|
@ -11,17 +11,17 @@ Use the `aws_iam_password_policy` InSpec audit resource to test properties of th
|
|||
|
||||
## Syntax
|
||||
|
||||
An `aws_iam_password_policy` resource block takes no parameters, but uses several matchers.
|
||||
An `aws_iam_password_policy` resource block takes no parameters. Several properties and matchers are available.
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('requires_lowercase_characters?') { should be true }
|
||||
it { should require_lowercase_characters }
|
||||
end
|
||||
|
||||
<br>
|
||||
|
||||
## Properties
|
||||
|
||||
* `allows_users_to_change_password?`, `expires_passwords`, `max_password_age`, `minimum_password_length`, `number_of_passwords_to_remember`, `prevents_password_reuse?`, `requires_lowercase_characters` , `requires_uppercase_characters?`, `requires_numbers?`, `requires_symbols?`
|
||||
* `max_password_age_in_days`, `minimum_password_length`, `number_of_passwords_to_remember`
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -30,35 +30,35 @@ The following examples show how to use this InSpec audit resource.
|
|||
### Test that the IAM Password Policy requires lowercase characters, uppercase characters, numbers, symbols, and a minimum length greater than eight
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('requires_lowercase_characters?') { should be true }
|
||||
its('requires_uppercase_characters?') { should be true }
|
||||
its('requires_numbers?') { should be true }
|
||||
its('requires_symbols?') { should be true }
|
||||
it { should require_lowercase_characters }
|
||||
it { should require_uppercase_characters }
|
||||
it { should require_symbols }
|
||||
it { should require_numbers }
|
||||
its('minimum_password_length') { should be > 8 }
|
||||
end
|
||||
|
||||
### Test that the IAM Password Policy allows users to change their password
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('allows_user_to_change_password?') { should be true }
|
||||
it { should allow_users_to_change_passwords }
|
||||
end
|
||||
|
||||
### Test that the IAM Password Policy expires passwords
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('expires_passwords?') { should be true }
|
||||
it { should expire_passwords }
|
||||
end
|
||||
|
||||
### Test that the IAM Password Policy has a max password age
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('max_password_age') { should be > 90 * 86400 }
|
||||
its('max_password_age_in_days') { should be 90 }
|
||||
end
|
||||
|
||||
### Test that the IAM Password Policy prevents password reuse
|
||||
|
||||
describe aws_iam_password_policy do
|
||||
its('prevents_password_reuse?') { should be true }
|
||||
it { should prevent_password_reuse }
|
||||
end
|
||||
|
||||
### Test that the IAM Password Policy requires users to remember 3 previous passwords
|
||||
|
@ -71,4 +71,4 @@ The following examples show how to use this InSpec audit resource.
|
|||
|
||||
## Matchers
|
||||
|
||||
For a full list of available matchers please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
||||
* `allows_users_to_change_passwords`, `expire_passwords`, `prevent_password_reuse`, `require_lowercase_characters` , `require_uppercase_characters`, `require_numbers`, `require_symbols`
|
||||
|
|
|
@ -17,11 +17,20 @@ EOX
|
|||
# TODO: rewrite to avoid direct injection, match other resources, use AwsSingularResourceMixin
|
||||
def initialize(conn = nil)
|
||||
catch_aws_errors do
|
||||
iam_resource = conn ? conn.iam_resource : inspec_runner.backend.aws_resource(Aws::IAM::Resource, {})
|
||||
@policy = iam_resource.account_password_policy
|
||||
begin
|
||||
if conn
|
||||
# We're in a mocked unit test.
|
||||
@policy = conn.iam_resource.account_password_policy
|
||||
else
|
||||
# Don't use the resource approach. It's a CRUD operation
|
||||
# - if the policy does not exist, you get back a blank object to populate and save.
|
||||
# Using the Client will throw an exception if no policy exists.
|
||||
@policy = inspec_runner.backend.aws_client(Aws::IAM::Client).get_account_password_policy.password_policy
|
||||
end
|
||||
rescue Aws::IAM::Errors::NoSuchEntity
|
||||
@policy = nil
|
||||
end
|
||||
end
|
||||
rescue Aws::IAM::Errors::NoSuchEntity
|
||||
@policy = nil
|
||||
end
|
||||
|
||||
# TODO: DRY up, see https://github.com/chef/inspec/issues/2633
|
||||
|
@ -49,54 +58,59 @@ EOX
|
|||
inspec if respond_to?(:inspec)
|
||||
end
|
||||
|
||||
def to_s
|
||||
'IAM Password-Policy'
|
||||
end
|
||||
|
||||
def exists?
|
||||
!@policy.nil?
|
||||
end
|
||||
|
||||
def requires_lowercase_characters?
|
||||
@policy.require_lowercase_characters
|
||||
end
|
||||
|
||||
def requires_uppercase_characters?
|
||||
@policy.require_uppercase_characters
|
||||
end
|
||||
#-------------------------- Properties ----------------------------#
|
||||
|
||||
def minimum_password_length
|
||||
@policy.minimum_password_length
|
||||
end
|
||||
|
||||
def requires_numbers?
|
||||
@policy.require_numbers
|
||||
end
|
||||
|
||||
def requires_symbols?
|
||||
@policy.require_symbols
|
||||
end
|
||||
|
||||
def allows_users_to_change_password?
|
||||
@policy.allow_users_to_change_password
|
||||
end
|
||||
|
||||
def expires_passwords?
|
||||
@policy.expire_passwords
|
||||
end
|
||||
|
||||
def max_password_age
|
||||
raise 'this policy does not expire passwords' unless expires_passwords?
|
||||
def max_password_age_in_days
|
||||
raise 'this policy does not expire passwords' unless expire_passwords?
|
||||
@policy.max_password_age
|
||||
end
|
||||
|
||||
def prevents_password_reuse?
|
||||
!@policy.password_reuse_prevention.nil?
|
||||
end
|
||||
|
||||
def number_of_passwords_to_remember
|
||||
raise 'this policy does not prevent password reuse' \
|
||||
unless prevents_password_reuse?
|
||||
unless prevent_password_reuse?
|
||||
@policy.password_reuse_prevention
|
||||
end
|
||||
|
||||
def to_s
|
||||
'IAM Password-Policy'
|
||||
#-------------------------- Matchers ----------------------------#
|
||||
[
|
||||
:require_lowercase_characters,
|
||||
:require_uppercase_characters,
|
||||
:require_symbols,
|
||||
:require_numbers,
|
||||
:expire_passwords,
|
||||
].each do |matcher_stem|
|
||||
# Create our predicates (for example, 'require_symbols?')
|
||||
stem_with_question_mark = (matcher_stem.to_s + '?').to_sym
|
||||
define_method stem_with_question_mark do
|
||||
@policy.send(matcher_stem)
|
||||
end
|
||||
# RSpec will expose that as (for example) `be_require_symbols`.
|
||||
# To undo that, we have to make a matcher alias.
|
||||
stem_with_be = ('be_' + matcher_stem.to_s).to_sym
|
||||
RSpec::Matchers.alias_matcher matcher_stem, stem_with_be
|
||||
end
|
||||
|
||||
# This one has an awkward name mapping
|
||||
def allow_users_to_change_passwords?
|
||||
@policy.allow_users_to_change_password
|
||||
end
|
||||
RSpec::Matchers.alias_matcher :allow_users_to_change_passwords, :be_allow_users_to_change_passwords
|
||||
|
||||
# This one has custom logic and renaming
|
||||
def prevent_password_reuse?
|
||||
!@policy.password_reuse_prevention.nil?
|
||||
end
|
||||
RSpec::Matchers.alias_matcher :prevent_password_reuse, :be_prevent_password_reuse
|
||||
end
|
||||
|
|
|
@ -6,6 +6,21 @@ variable "login_profile_pgp_key" {
|
|||
default = "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"
|
||||
}
|
||||
|
||||
#======================================================#
|
||||
# Accoount Password Policy
|
||||
#======================================================#
|
||||
# Only one of these is allowed
|
||||
resource "aws_iam_account_password_policy" "fixture" {
|
||||
minimum_password_length = 10
|
||||
require_lowercase_characters = true
|
||||
require_numbers = true
|
||||
require_uppercase_characters = true
|
||||
require_symbols = true
|
||||
allow_users_to_change_password = true
|
||||
max_password_age = 365
|
||||
password_reuse_prevention = 7
|
||||
}
|
||||
|
||||
#======================================================#
|
||||
# IAM Users
|
||||
#======================================================#
|
||||
|
|
33
test/aws/default/verify/controls/aws_iam_password_policy.rb
Normal file
33
test/aws/default/verify/controls/aws_iam_password_policy.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# There are other tests in the "minimal" test account.
|
||||
|
||||
#---------------------- Recall ------------------------#
|
||||
# Password policy is a per-account singleton. If it's been configured, it exists.
|
||||
control "aws_iam_password_policy existence" do
|
||||
describe aws_iam_password_policy do
|
||||
it { should exist }
|
||||
end
|
||||
end
|
||||
|
||||
#------------- Properties -------------#
|
||||
|
||||
control "aws_iam_password_policy properties" do
|
||||
describe aws_iam_password_policy do
|
||||
its('max_password_age_in_days') { should cmp 365 }
|
||||
its('number_of_passwords_to_remember') { should cmp 7 }
|
||||
end
|
||||
end
|
||||
|
||||
#------------- Matchers - Positive Case -------------#
|
||||
|
||||
control "aws_iam_password_policy matchers" do
|
||||
describe aws_iam_password_policy do
|
||||
it { should require_lowercase_characters }
|
||||
it { should require_uppercase_characters }
|
||||
it { should require_numbers }
|
||||
it { should require_symbols }
|
||||
it { should allow_users_to_change_passwords }
|
||||
it { should expire_passwords }
|
||||
it { should prevent_password_reuse }
|
||||
end
|
||||
end
|
||||
|
14
test/aws/minimal/verify/controls/aws_iam_password_policy.rb
Normal file
14
test/aws/minimal/verify/controls/aws_iam_password_policy.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
#---------------------- Recall ------------------------#
|
||||
# Password policy is a per-account singleton. If it's been configured, it exists.
|
||||
control "aws_iam_password_policy properties" do
|
||||
describe aws_iam_password_policy do
|
||||
it { should_not exist }
|
||||
end
|
||||
end
|
||||
|
||||
#------------- Properties - Negative Case -------------#
|
||||
# No negative tests yet - we'd need a third account
|
||||
|
||||
#------------- Matchers - Negative Case -------------#
|
||||
# No negative tests yet - we'd need a third account
|
|
@ -30,7 +30,7 @@ class AwsIamPasswordPolicyTest < Minitest::Test
|
|||
@mock_resource.expect :account_password_policy, @mock_policy
|
||||
|
||||
e = assert_raises Exception do
|
||||
AwsIamPasswordPolicy.new(@mock_conn).max_password_age
|
||||
AwsIamPasswordPolicy.new(@mock_conn).max_password_age_in_days
|
||||
end
|
||||
|
||||
assert_equal e.message, 'this policy does not expire passwords'
|
||||
|
@ -39,13 +39,13 @@ class AwsIamPasswordPolicyTest < Minitest::Test
|
|||
def test_prevents_password_reuse_returns_true_when_not_nil
|
||||
configure_policy_password_reuse_prevention(value: Object.new)
|
||||
|
||||
assert AwsIamPasswordPolicy.new(@mock_conn).prevents_password_reuse?
|
||||
assert AwsIamPasswordPolicy.new(@mock_conn).prevent_password_reuse?
|
||||
end
|
||||
|
||||
def test_prevents_password_reuse_returns_false_when_nil
|
||||
configure_policy_password_reuse_prevention(value: nil)
|
||||
|
||||
refute AwsIamPasswordPolicy.new(@mock_conn).prevents_password_reuse?
|
||||
refute AwsIamPasswordPolicy.new(@mock_conn).prevent_password_reuse?
|
||||
end
|
||||
|
||||
def test_number_of_passwords_to_remember_throws_when_nil
|
||||
|
|
Loading…
Reference in a new issue