inspec 2.1.43 → 2.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -22
- data/Rakefile +4 -4
- data/docs/resources/{aws_config_delivery_channel.md → aws_config_delivery_channel.md.erb} +37 -20
- data/docs/resources/aws_config_recorder.md.erb +11 -1
- data/docs/resources/aws_iam_user.md.erb +53 -2
- data/docs/resources/aws_iam_users.md.erb +194 -11
- data/docs/resources/docker.md.erb +1 -1
- data/docs/resources/users.md.erb +1 -1
- data/examples/kitchen-puppet/.kitchen.yml +1 -0
- data/examples/kitchen-puppet/modules/.gitkeep +0 -0
- data/lib/bundles/inspec-compliance/README.md +8 -0
- data/lib/bundles/inspec-compliance/api.rb +50 -6
- data/lib/bundles/inspec-compliance/api/login.rb +44 -3
- data/lib/bundles/inspec-compliance/cli.rb +10 -4
- data/lib/bundles/inspec-compliance/http.rb +39 -0
- data/lib/bundles/inspec-compliance/target.rb +8 -1
- data/lib/fetchers/url.rb +40 -3
- data/lib/inspec/base_cli.rb +3 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/aws/aws_config_delivery_channel.rb +14 -20
- data/lib/resources/aws/aws_config_recorder.rb +21 -26
- data/lib/resources/aws/aws_iam_policy.rb +19 -2
- data/lib/resources/aws/aws_iam_role.rb +4 -0
- data/lib/resources/aws/aws_iam_user.rb +32 -1
- data/lib/resources/aws/aws_iam_users.rb +40 -2
- data/lib/resources/service.rb +1 -1
- metadata +4 -3
@@ -28,29 +28,23 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
|
|
28
28
|
allowed_scalar_type: String,
|
29
29
|
)
|
30
30
|
|
31
|
-
# Make sure channel_name is given as param
|
32
|
-
if validated_params[:channel_name].nil?
|
33
|
-
raise ArgumentError, 'You must provide a channel_name to aws_config_delivery_channel'
|
34
|
-
end
|
35
|
-
|
36
31
|
validated_params
|
37
32
|
end
|
38
33
|
|
39
34
|
def fetch_from_api
|
40
35
|
backend = BackendFactory.create(inspec_runner)
|
41
|
-
query = { delivery_channel_names: [@channel_name] }
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
return unless @exists
|
36
|
+
query = @channel_name ? { delivery_channel_names: [@channel_name] } : {}
|
37
|
+
response = backend.describe_delivery_channels(query)
|
38
|
+
|
39
|
+
@exists = !response.delivery_channels.empty?
|
40
|
+
return unless exists?
|
47
41
|
|
48
|
-
|
49
|
-
@channel_name =
|
50
|
-
@s3_bucket_name =
|
51
|
-
@s3_key_prefix =
|
52
|
-
@sns_topic_arn =
|
53
|
-
@delivery_frequency_in_hours =
|
42
|
+
channel = response.delivery_channels.first.to_h
|
43
|
+
@channel_name = channel[:name]
|
44
|
+
@s3_bucket_name = channel[:s3_bucket_name]
|
45
|
+
@s3_key_prefix = channel[:s3_key_prefix]
|
46
|
+
@sns_topic_arn = channel[:sns_topic_arn]
|
47
|
+
@delivery_frequency_in_hours = channel.dig(:config_snapshot_delivery_properties, :delivery_frequency)
|
54
48
|
frequencies = {
|
55
49
|
'One_Hour' => 1,
|
56
50
|
'TwentyFour_Hours' => 24,
|
@@ -59,6 +53,8 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
|
|
59
53
|
'Twelve_Hours' => 12,
|
60
54
|
}
|
61
55
|
@delivery_frequency_in_hours = frequencies[@delivery_frequency_in_hours]
|
56
|
+
rescue Aws::ConfigService::Errors::NoSuchDeliveryChannelException
|
57
|
+
@exists = false
|
62
58
|
end
|
63
59
|
|
64
60
|
class Backend
|
@@ -66,10 +62,8 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
|
|
66
62
|
BackendFactory.set_default_backend(self)
|
67
63
|
self.aws_client_class = Aws::ConfigService::Client
|
68
64
|
|
69
|
-
def describe_delivery_channels(query)
|
65
|
+
def describe_delivery_channels(query = {})
|
70
66
|
aws_service_client.describe_delivery_channels(query)
|
71
|
-
rescue Aws::ConfigService::Errors::NoSuchDeliveryChannelException
|
72
|
-
return {}
|
73
67
|
end
|
74
68
|
end
|
75
69
|
end
|
@@ -12,7 +12,7 @@ class AwsConfigurationRecorder < Inspec.resource(1)
|
|
12
12
|
supports platform: 'aws'
|
13
13
|
|
14
14
|
include AwsSingularResourceMixin
|
15
|
-
attr_reader :role_arn, :resource_types, :recorder_name
|
15
|
+
attr_reader :role_arn, :resource_types, :recorder_name
|
16
16
|
|
17
17
|
def to_s
|
18
18
|
"Configuration_Recorder: #{@recorder_name}"
|
@@ -27,11 +27,11 @@ class AwsConfigurationRecorder < Inspec.resource(1)
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def status
|
30
|
-
return unless @exists
|
30
|
+
return {} unless @exists
|
31
31
|
backend = BackendFactory.create(inspec_runner)
|
32
32
|
catch_aws_errors do
|
33
|
-
|
34
|
-
@status =
|
33
|
+
response = backend.describe_configuration_recorder_status(configuration_recorder_names: [@recorder_name])
|
34
|
+
@status = response.configuration_recorders_status.first.to_h
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -50,35 +50,30 @@ class AwsConfigurationRecorder < Inspec.resource(1)
|
|
50
50
|
allowed_scalar_type: String,
|
51
51
|
)
|
52
52
|
|
53
|
-
# Must give it a recorder_name
|
54
|
-
if validated_params[:recorder_name].nil?
|
55
|
-
raise ArgumentError, 'You must provide recorder_name to aws_config_recorder'
|
56
|
-
end
|
57
|
-
|
58
53
|
validated_params
|
59
54
|
end
|
60
55
|
|
61
56
|
def fetch_from_api
|
62
57
|
backend = BackendFactory.create(inspec_runner)
|
63
|
-
|
58
|
+
query = @recorder_name ? { configuration_recorder_names: [@recorder_name] } : {}
|
59
|
+
response = backend.describe_configuration_recorders(query)
|
64
60
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
return
|
71
|
-
end
|
72
|
-
@exists = !@resp.empty?
|
73
|
-
return unless @exists
|
74
|
-
|
75
|
-
@recorder = @resp.configuration_recorders.first.to_h
|
76
|
-
@recorder_name = @recorder[:name]
|
77
|
-
@role_arn = @recorder[:role_arn]
|
78
|
-
@recording_all_resource_types = @recorder[:recording_group][:all_supported]
|
79
|
-
@recording_all_global_types = @recorder[:recording_group][:include_global_resource_types]
|
80
|
-
@resource_types = @recorder[:recording_group][:resource_types]
|
61
|
+
@exists = !response.configuration_recorders.empty?
|
62
|
+
return unless exists?
|
63
|
+
|
64
|
+
if response.configuration_recorders.count > 1
|
65
|
+
raise ArgumentError, 'Internal error: unexpectedly received multiple AWS Config Recorder objects from API; expected to be singleton per-region. Please file a bug report at https://github.com/chef/inspec/issues .'
|
81
66
|
end
|
67
|
+
|
68
|
+
recorder = response.configuration_recorders.first.to_h
|
69
|
+
@recorder_name = recorder[:name]
|
70
|
+
@role_arn = recorder[:role_arn]
|
71
|
+
@recording_all_resource_types = recorder[:recording_group][:all_supported]
|
72
|
+
@recording_all_global_types = recorder[:recording_group][:include_global_resource_types]
|
73
|
+
@resource_types = recorder[:recording_group][:resource_types]
|
74
|
+
rescue Aws::ConfigService::Errors::NoSuchConfigurationRecorderException
|
75
|
+
@exists = false
|
76
|
+
return
|
82
77
|
end
|
83
78
|
|
84
79
|
class Backend
|
@@ -83,7 +83,14 @@ class AwsIamPolicy < Inspec.resource(1)
|
|
83
83
|
|
84
84
|
def statement_count
|
85
85
|
return nil unless exists?
|
86
|
-
|
86
|
+
# Typically it is an array of statements
|
87
|
+
if policy['Statement'].is_a? Array
|
88
|
+
policy['Statement'].count
|
89
|
+
else
|
90
|
+
# But if there is one statement, it is permissable to degenerate the array,
|
91
|
+
# and place the statement as a hash directly under the 'Statement' key
|
92
|
+
return 1
|
93
|
+
end
|
87
94
|
end
|
88
95
|
|
89
96
|
def has_statement?(raw_criteria = {})
|
@@ -141,6 +148,11 @@ class AwsIamPolicy < Inspec.resource(1)
|
|
141
148
|
end
|
142
149
|
|
143
150
|
def has_statement__normalize_statements
|
151
|
+
# Some single-statement policies place their statement
|
152
|
+
# directly in policy['Statement'], rather than in an
|
153
|
+
# Array within it. See arn:aws:iam::aws:policy/AWSCertificateManagerReadOnly
|
154
|
+
# Thus, coerce to Array.
|
155
|
+
policy['Statement'] = [policy['Statement']] if policy['Statement'].is_a? Hash
|
144
156
|
policy['Statement'].map do |statement|
|
145
157
|
# Coerce some values into arrays
|
146
158
|
%w{Action Resource}.each do |field|
|
@@ -177,7 +189,12 @@ class AwsIamPolicy < Inspec.resource(1)
|
|
177
189
|
def has_statement__array_criterion(crit_name, statement, criteria)
|
178
190
|
return true unless criteria.key?(crit_name)
|
179
191
|
check = criteria[crit_name]
|
180
|
-
|
192
|
+
# This is an array due to normalize_statements
|
193
|
+
# If it is nil, the statement does not have an entry for that dimension;
|
194
|
+
# but since we were asked to match on it (on nothing), we
|
195
|
+
# decide to never match
|
196
|
+
values = statement[crit_name]
|
197
|
+
return false if values.nil?
|
181
198
|
|
182
199
|
if check.is_a?(String)
|
183
200
|
# If check is a string, it only has to match one of the values
|
@@ -9,12 +9,15 @@ class AwsIamUser < Inspec.resource(1)
|
|
9
9
|
describe aws_iam_user(username: 'test_user') do
|
10
10
|
it { should have_mfa_enabled }
|
11
11
|
it { should_not have_console_password }
|
12
|
+
it { should_not have_inline_user_policies }
|
13
|
+
it { should_not have_attached_user_policies }
|
12
14
|
end
|
13
15
|
"
|
14
16
|
supports platform: 'aws'
|
15
17
|
|
16
18
|
include AwsSingularResourceMixin
|
17
|
-
attr_reader :access_keys, :
|
19
|
+
attr_reader :access_keys, :attached_policy_names, :attached_policy_arns, \
|
20
|
+
:has_console_password, :has_mfa_enabled, :inline_policy_names, :username
|
18
21
|
alias has_mfa_enabled? has_mfa_enabled
|
19
22
|
alias has_console_password? has_console_password
|
20
23
|
|
@@ -27,6 +30,16 @@ class AwsIamUser < Inspec.resource(1)
|
|
27
30
|
"IAM User #{username}"
|
28
31
|
end
|
29
32
|
|
33
|
+
def has_attached_policies?
|
34
|
+
return nil unless exists?
|
35
|
+
!attached_policy_names.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_inline_policies?
|
39
|
+
return nil unless exists?
|
40
|
+
!inline_policy_names.empty?
|
41
|
+
end
|
42
|
+
|
30
43
|
private
|
31
44
|
|
32
45
|
def validate_params(raw_params)
|
@@ -62,6 +75,10 @@ class AwsIamUser < Inspec.resource(1)
|
|
62
75
|
@aws_user_struct = backend.get_user(user_name: username)
|
63
76
|
rescue Aws::IAM::Errors::NoSuchEntity
|
64
77
|
@exists = false
|
78
|
+
@access_keys = []
|
79
|
+
@inline_policy_names = []
|
80
|
+
@attached_policy_arns = []
|
81
|
+
@attached_policy_names = []
|
65
82
|
return
|
66
83
|
end
|
67
84
|
end
|
@@ -84,6 +101,12 @@ class AwsIamUser < Inspec.resource(1)
|
|
84
101
|
@access_keys = backend.list_access_keys(user_name: username).access_key_metadata
|
85
102
|
# If the above call fails, we get nil here; but we promise access_keys will be an array.
|
86
103
|
@access_keys ||= []
|
104
|
+
|
105
|
+
@inline_policy_names = backend.list_user_policies(user_name: username).policy_names
|
106
|
+
|
107
|
+
attached_policies = backend.list_attached_user_policies(user_name: username).attached_policies
|
108
|
+
@attached_policy_arns = attached_policies.map { |p| p[:policy_arn] }
|
109
|
+
@attached_policy_names = attached_policies.map { |p| p[:policy_name] }
|
87
110
|
end
|
88
111
|
|
89
112
|
class Backend
|
@@ -106,6 +129,14 @@ class AwsIamUser < Inspec.resource(1)
|
|
106
129
|
def list_access_keys(criteria)
|
107
130
|
aws_service_client.list_access_keys(criteria)
|
108
131
|
end
|
132
|
+
|
133
|
+
def list_user_policies(criteria)
|
134
|
+
aws_service_client.list_user_policies(criteria)
|
135
|
+
end
|
136
|
+
|
137
|
+
def list_attached_user_policies(criteria)
|
138
|
+
aws_service_client.list_attached_user_policies(criteria)
|
139
|
+
end
|
109
140
|
end
|
110
141
|
end
|
111
142
|
end
|
@@ -12,6 +12,12 @@ class AwsIamUsers < Inspec.resource(1)
|
|
12
12
|
describe aws_iam_users.where(has_console_password?: true) do
|
13
13
|
it { should exist }
|
14
14
|
end
|
15
|
+
describe aws_iam_users.where(has_inline_policies?: true) do
|
16
|
+
it { should_not exist }
|
17
|
+
end
|
18
|
+
describe aws_iam_users.where(has_attached_policies?: true) do
|
19
|
+
it { should_not exist }
|
20
|
+
end
|
15
21
|
'
|
16
22
|
supports platform: 'aws'
|
17
23
|
|
@@ -23,10 +29,21 @@ class AwsIamUsers < Inspec.resource(1)
|
|
23
29
|
.add(:exists?) { |x| !x.entries.empty? }
|
24
30
|
.add(:has_mfa_enabled?, field: :has_mfa_enabled)
|
25
31
|
.add(:has_console_password?, field: :has_console_password)
|
32
|
+
.add(:has_inline_policies?, field: :has_inline_policies)
|
33
|
+
.add(:has_attached_policies?, field: :has_attached_policies)
|
26
34
|
.add(:password_ever_used?, field: :password_ever_used?)
|
27
35
|
.add(:password_never_used?, field: :password_never_used?)
|
28
36
|
.add(:password_last_used_days_ago, field: :password_last_used_days_ago)
|
29
|
-
.add(:
|
37
|
+
.add(:usernames, field: :user_name)
|
38
|
+
.add(:username) { |res| res.entries.map { |row| row[:user_name] } } # We should deprecate this; plural resources get plural properties
|
39
|
+
# Next three are needed to declare fields for use by the de-duped set
|
40
|
+
filter.add(:dupe_inline_policy_names, field: :inline_policy_names_source)
|
41
|
+
.add(:dupe_attached_policy_names, field: :attached_policy_names_source)
|
42
|
+
.add(:dupe_attached_policy_arns, field: :attached_policy_arns_source)
|
43
|
+
# These three are now able to access the above three in .entries
|
44
|
+
filter.add(:inline_policy_names) { |obj| obj.dupe_inline_policy_names.flatten.uniq }
|
45
|
+
.add(:attached_policy_names) { |obj| obj.dupe_attached_policy_names.flatten.uniq }
|
46
|
+
.add(:attached_policy_arns) { |obj| obj.dupe_attached_policy_arns.flatten.uniq }
|
30
47
|
filter.connect(self, :table)
|
31
48
|
|
32
49
|
def validate_params(raw_params)
|
@@ -49,12 +66,14 @@ class AwsIamUsers < Inspec.resource(1)
|
|
49
66
|
table
|
50
67
|
end
|
51
68
|
|
52
|
-
def fetch_from_api
|
69
|
+
def fetch_from_api # rubocop: disable Metrics/AbcSize
|
53
70
|
backend = BackendFactory.create(inspec_runner)
|
54
71
|
@table = fetch_from_api_paginated(backend)
|
55
72
|
|
56
73
|
# TODO: lazy columns - https://github.com/chef/inspec-aws/issues/100
|
57
74
|
@table.each do |user|
|
75
|
+
# Some of these throw exceptions to indicate empty results;
|
76
|
+
# others return empty arrays
|
58
77
|
begin
|
59
78
|
_login_profile = backend.get_login_profile(user_name: user[:user_name])
|
60
79
|
user[:has_console_password] = true
|
@@ -70,6 +89,17 @@ class AwsIamUsers < Inspec.resource(1)
|
|
70
89
|
user[:has_mfa_enabled] = false
|
71
90
|
end
|
72
91
|
user[:has_mfa_enabled?] = user[:has_mfa_enabled]
|
92
|
+
|
93
|
+
user[:inline_policy_names_source] = backend.list_user_policies(user_name: user[:user_name]).policy_names
|
94
|
+
user[:has_inline_policies] = !user[:inline_policy_names_source].empty?
|
95
|
+
user[:has_inline_policies?] = user[:has_inline_policies]
|
96
|
+
|
97
|
+
attached_policies = backend.list_attached_user_policies(user_name: user[:user_name]).attached_policies
|
98
|
+
user[:has_attached_policies] = !attached_policies.empty?
|
99
|
+
user[:has_attached_policies?] = user[:has_attached_policies]
|
100
|
+
user[:attached_policy_names_source] = attached_policies.map { |p| p[:policy_name] }
|
101
|
+
user[:attached_policy_arns_source] = attached_policies.map { |p| p[:policy_arn] }
|
102
|
+
|
73
103
|
password_last_used = user[:password_last_used]
|
74
104
|
user[:password_ever_used?] = !password_last_used.nil?
|
75
105
|
user[:password_never_used?] = password_last_used.nil?
|
@@ -103,6 +133,14 @@ class AwsIamUsers < Inspec.resource(1)
|
|
103
133
|
def list_mfa_devices(query)
|
104
134
|
aws_service_client.list_mfa_devices(query)
|
105
135
|
end
|
136
|
+
|
137
|
+
def list_user_policies(query)
|
138
|
+
aws_service_client.list_user_policies(query)
|
139
|
+
end
|
140
|
+
|
141
|
+
def list_attached_user_policies(query)
|
142
|
+
aws_service_client.list_attached_user_policies(query)
|
143
|
+
end
|
106
144
|
end
|
107
145
|
end
|
108
146
|
end
|
data/lib/resources/service.rb
CHANGED
@@ -162,7 +162,7 @@ module Inspec::Resources
|
|
162
162
|
elsif %w{aix}.include?(platform)
|
163
163
|
SrcMstr.new(inspec)
|
164
164
|
elsif %w{amazon}.include?(platform)
|
165
|
-
if os[:release]
|
165
|
+
if os[:release] =~ /^20\d\d/
|
166
166
|
Upstart.new(inspec, service_ctl)
|
167
167
|
else
|
168
168
|
Systemd.new(inspec, service_ctl)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.54
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train
|
@@ -312,7 +312,7 @@ files:
|
|
312
312
|
- docs/resources/aws_cloudtrail_trails.md.erb
|
313
313
|
- docs/resources/aws_cloudwatch_alarm.md.erb
|
314
314
|
- docs/resources/aws_cloudwatch_log_metric_filter.md.erb
|
315
|
-
- docs/resources/aws_config_delivery_channel.md
|
315
|
+
- docs/resources/aws_config_delivery_channel.md.erb
|
316
316
|
- docs/resources/aws_config_recorder.md.erb
|
317
317
|
- docs/resources/aws_ec2_instance.md.erb
|
318
318
|
- docs/resources/aws_iam_access_key.md.erb
|
@@ -475,6 +475,7 @@ files:
|
|
475
475
|
- examples/kitchen-puppet/README.md
|
476
476
|
- examples/kitchen-puppet/manifests/site.pp
|
477
477
|
- examples/kitchen-puppet/metadata.json
|
478
|
+
- examples/kitchen-puppet/modules/.gitkeep
|
478
479
|
- examples/kitchen-puppet/test/integration/default/web_spec.rb
|
479
480
|
- examples/meta-profile/README.md
|
480
481
|
- examples/meta-profile/controls/example.rb
|