inspec 4.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +63 -0
  3. data/inspec.gemspec +36 -0
  4. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/Gemfile +11 -0
  5. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/inspec-plugin-template.gemspec +43 -0
  6. data/lib/plugins/inspec-init/templates/profiles/aws/README.md +192 -0
  7. data/lib/plugins/inspec-init/templates/profiles/aws/attributes.yml +2 -0
  8. data/lib/plugins/inspec-init/templates/profiles/aws/controls/example.rb +39 -0
  9. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +22 -0
  10. data/lib/plugins/inspec-init/templates/profiles/azure/README.md +56 -0
  11. data/lib/plugins/inspec-init/templates/profiles/azure/controls/example.rb +14 -0
  12. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +14 -0
  13. data/lib/plugins/inspec-init/templates/profiles/gcp/README.md +66 -0
  14. data/lib/plugins/inspec-init/templates/profiles/gcp/attributes.yml +2 -0
  15. data/lib/plugins/inspec-init/templates/profiles/gcp/controls/example.rb +27 -0
  16. data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +19 -0
  17. data/lib/resource_support/aws.rb +76 -0
  18. data/lib/resource_support/aws/aws_backend_base.rb +12 -0
  19. data/lib/resource_support/aws/aws_backend_factory_mixin.rb +12 -0
  20. data/lib/resource_support/aws/aws_plural_resource_mixin.rb +24 -0
  21. data/lib/resource_support/aws/aws_resource_mixin.rb +69 -0
  22. data/lib/resource_support/aws/aws_singular_resource_mixin.rb +27 -0
  23. data/lib/resources/aws/aws_billing_report.rb +107 -0
  24. data/lib/resources/aws/aws_billing_reports.rb +74 -0
  25. data/lib/resources/aws/aws_cloudtrail_trail.rb +97 -0
  26. data/lib/resources/aws/aws_cloudtrail_trails.rb +51 -0
  27. data/lib/resources/aws/aws_cloudwatch_alarm.rb +67 -0
  28. data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +105 -0
  29. data/lib/resources/aws/aws_config_delivery_channel.rb +74 -0
  30. data/lib/resources/aws/aws_config_recorder.rb +99 -0
  31. data/lib/resources/aws/aws_ebs_volume.rb +127 -0
  32. data/lib/resources/aws/aws_ebs_volumes.rb +69 -0
  33. data/lib/resources/aws/aws_ec2_instance.rb +162 -0
  34. data/lib/resources/aws/aws_ec2_instances.rb +69 -0
  35. data/lib/resources/aws/aws_ecs_cluster.rb +88 -0
  36. data/lib/resources/aws/aws_eks_cluster.rb +105 -0
  37. data/lib/resources/aws/aws_elb.rb +85 -0
  38. data/lib/resources/aws/aws_elbs.rb +84 -0
  39. data/lib/resources/aws/aws_flow_log.rb +106 -0
  40. data/lib/resources/aws/aws_iam_access_key.rb +112 -0
  41. data/lib/resources/aws/aws_iam_access_keys.rb +153 -0
  42. data/lib/resources/aws/aws_iam_group.rb +62 -0
  43. data/lib/resources/aws/aws_iam_groups.rb +56 -0
  44. data/lib/resources/aws/aws_iam_password_policy.rb +121 -0
  45. data/lib/resources/aws/aws_iam_policies.rb +57 -0
  46. data/lib/resources/aws/aws_iam_policy.rb +311 -0
  47. data/lib/resources/aws/aws_iam_role.rb +60 -0
  48. data/lib/resources/aws/aws_iam_root_user.rb +82 -0
  49. data/lib/resources/aws/aws_iam_user.rb +145 -0
  50. data/lib/resources/aws/aws_iam_users.rb +160 -0
  51. data/lib/resources/aws/aws_kms_key.rb +100 -0
  52. data/lib/resources/aws/aws_kms_keys.rb +58 -0
  53. data/lib/resources/aws/aws_rds_instance.rb +74 -0
  54. data/lib/resources/aws/aws_route_table.rb +67 -0
  55. data/lib/resources/aws/aws_route_tables.rb +64 -0
  56. data/lib/resources/aws/aws_s3_bucket.rb +142 -0
  57. data/lib/resources/aws/aws_s3_bucket_object.rb +87 -0
  58. data/lib/resources/aws/aws_s3_buckets.rb +52 -0
  59. data/lib/resources/aws/aws_security_group.rb +314 -0
  60. data/lib/resources/aws/aws_security_groups.rb +71 -0
  61. data/lib/resources/aws/aws_sns_subscription.rb +82 -0
  62. data/lib/resources/aws/aws_sns_topic.rb +57 -0
  63. data/lib/resources/aws/aws_sns_topics.rb +60 -0
  64. data/lib/resources/aws/aws_sqs_queue.rb +66 -0
  65. data/lib/resources/aws/aws_subnet.rb +92 -0
  66. data/lib/resources/aws/aws_subnets.rb +56 -0
  67. data/lib/resources/aws/aws_vpc.rb +77 -0
  68. data/lib/resources/aws/aws_vpcs.rb +55 -0
  69. data/lib/resources/azure/azure_backend.rb +379 -0
  70. data/lib/resources/azure/azure_generic_resource.rb +55 -0
  71. data/lib/resources/azure/azure_resource_group.rb +151 -0
  72. data/lib/resources/azure/azure_virtual_machine.rb +262 -0
  73. data/lib/resources/azure/azure_virtual_machine_data_disk.rb +131 -0
  74. metadata +202 -0
@@ -0,0 +1,99 @@
1
+ require "resource_support/aws/aws_singular_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-configservice"
4
+
5
+ class AwsConfigurationRecorder < Inspec.resource(1)
6
+ name "aws_config_recorder"
7
+ desc "Verifies settings for AWS Configuration Recorder"
8
+ example <<~EXAMPLE
9
+ describe aws_config_recorder('My_Recorder') do
10
+ it { should exist }
11
+ it { should be_recording }
12
+ it { should be_all_supported }
13
+ it { should have_include_global_resource_types }
14
+ end
15
+ EXAMPLE
16
+ supports platform: "aws"
17
+
18
+ include AwsSingularResourceMixin
19
+ attr_reader :role_arn, :resource_types, :recorder_name
20
+
21
+ def to_s
22
+ "Configuration_Recorder: #{@recorder_name}"
23
+ end
24
+
25
+ def recording_all_resource_types?
26
+ @recording_all_resource_types
27
+ end
28
+
29
+ def recording_all_global_types?
30
+ @recording_all_global_types
31
+ end
32
+
33
+ def status
34
+ return {} unless @exists
35
+
36
+ backend = BackendFactory.create(inspec_runner)
37
+ catch_aws_errors do
38
+ response = backend.describe_configuration_recorder_status(configuration_recorder_names: [@recorder_name])
39
+ @status = response.configuration_recorders_status.first.to_h
40
+ end
41
+ end
42
+
43
+ def recording?
44
+ return unless @exists
45
+
46
+ status[:recording]
47
+ end
48
+
49
+ private
50
+
51
+ def validate_params(raw_params)
52
+ validated_params = check_resource_param_names(
53
+ raw_params: raw_params,
54
+ allowed_params: [:recorder_name],
55
+ allowed_scalar_name: :recorder_name,
56
+ allowed_scalar_type: String
57
+ )
58
+
59
+ validated_params
60
+ end
61
+
62
+ def fetch_from_api
63
+ backend = BackendFactory.create(inspec_runner)
64
+ query = @recorder_name ? { configuration_recorder_names: [@recorder_name] } : {}
65
+ response = backend.describe_configuration_recorders(query)
66
+
67
+ @exists = !response.configuration_recorders.empty?
68
+ return unless exists?
69
+
70
+ if response.configuration_recorders.count > 1
71
+ 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 ."
72
+ end
73
+
74
+ recorder = response.configuration_recorders.first.to_h
75
+ @recorder_name = recorder[:name]
76
+ @role_arn = recorder[:role_arn]
77
+ @recording_all_resource_types = recorder[:recording_group][:all_supported]
78
+ @recording_all_global_types = recorder[:recording_group][:include_global_resource_types]
79
+ @resource_types = recorder[:recording_group][:resource_types]
80
+ rescue Aws::ConfigService::Errors::NoSuchConfigurationRecorderException
81
+ @exists = false
82
+ nil
83
+ end
84
+
85
+ class Backend
86
+ class AwsClientApi < AwsBackendBase
87
+ BackendFactory.set_default_backend(self)
88
+ self.aws_client_class = Aws::ConfigService::Client
89
+
90
+ def describe_configuration_recorders(query)
91
+ aws_service_client.describe_configuration_recorders(query)
92
+ end
93
+
94
+ def describe_configuration_recorder_status(query)
95
+ aws_service_client.describe_configuration_recorder_status(query)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,127 @@
1
+ require "resource_support/aws/aws_singular_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-ec2"
4
+
5
+ class AwsEbsVolume < Inspec.resource(1)
6
+ name "aws_ebs_volume"
7
+ desc "Verifies settings for an EBS volume"
8
+
9
+ example <<~EXAMPLE
10
+ describe aws_ebs_volume('vol-123456') do
11
+ it { should be_encrypted }
12
+ its('size') { should cmp 8 }
13
+ end
14
+
15
+ describe aws_ebs_volume(name: 'my-volume') do
16
+ its('encrypted') { should eq true }
17
+ its('iops') { should cmp 100 }
18
+ end
19
+ EXAMPLE
20
+ supports platform: "aws"
21
+
22
+ # TODO: rewrite to avoid direct injection, match other resources, use AwsSingularResourceMixin
23
+ def initialize(opts, conn = nil)
24
+ @opts = opts
25
+ @display_name = opts.is_a?(Hash) ? @opts[:name] : opts
26
+ @ec2_client = conn ? conn.ec2_client : inspec_runner.backend.aws_client(Aws::EC2::Client)
27
+ @ec2_resource = conn ? conn.ec2_resource : inspec_runner.backend.aws_resource(Aws::EC2::Resource, {})
28
+ end
29
+
30
+ # TODO: DRY up, see https://github.com/chef/inspec/issues/2633
31
+ # Copied from resource_support/aws/aws_resource_mixin.rb
32
+ def catch_aws_errors
33
+ yield
34
+ rescue Aws::Errors::MissingCredentialsError
35
+ # The AWS error here is unhelpful:
36
+ # "unable to sign request without credentials set"
37
+ Inspec::Log.error "It appears that you have not set your AWS credentials. You may set them using environment variables, or using the 'aws://region/aws_credentials_profile' target. See https://www.inspec.io/docs/reference/platforms for details."
38
+ fail_resource("No AWS credentials available")
39
+ rescue Aws::Errors::ServiceError => e
40
+ fail_resource(e.message)
41
+ end
42
+
43
+ # TODO: DRY up, see https://github.com/chef/inspec/issues/2633
44
+ # Copied from resource_support/aws/aws_singular_resource_mixin.rb
45
+ def inspec_runner
46
+ # When running under inspec-cli, we have an 'inspec' method that
47
+ # returns the runner. When running under unit tests, we don't
48
+ # have that, but we still have to call this to pass something
49
+ # (nil is OK) to the backend.
50
+ # TODO: remove with https://github.com/chef/inspec-aws/issues/216
51
+ # TODO: remove after rewrite to include AwsSingularResource
52
+ inspec if respond_to?(:inspec)
53
+ end
54
+
55
+ def id
56
+ return @volume_id if defined?(@volume_id)
57
+
58
+ catch_aws_errors do
59
+ if @opts.is_a?(Hash)
60
+ first = @ec2_resource.volumes(
61
+ {
62
+ filters: [{
63
+ name: "tag:Name",
64
+ values: [@opts[:name]],
65
+ }],
66
+ }
67
+ ).first
68
+ # catch case where the volume is not known
69
+ @volume_id = first.id unless first.nil?
70
+ else
71
+ @volume_id = @opts
72
+ end
73
+ end
74
+ end
75
+ alias volume_id id
76
+
77
+ def exists?
78
+ !volume.nil?
79
+ end
80
+
81
+ def encrypted?
82
+ volume.encrypted
83
+ end
84
+
85
+ # attributes that we want to expose
86
+ %w{
87
+ availability_zone encrypted iops kms_key_id size snapshot_id state volume_type
88
+ }.each do |attribute|
89
+ define_method attribute do
90
+ catch_aws_errors do
91
+ volume.send(attribute) if volume
92
+ end
93
+ end
94
+ end
95
+
96
+ # Don't document this - it's a bit hard to use. Our current doctrine
97
+ # is to use dumb things, like arrays of strings - use security_group_ids instead.
98
+ def security_groups
99
+ catch_aws_errors do
100
+ @security_groups ||= volume.security_groups.map do |sg|
101
+ { id: sg.group_id, name: sg.group_name }
102
+ end
103
+ end
104
+ end
105
+
106
+ def security_group_ids
107
+ catch_aws_errors do
108
+ @security_group_ids ||= volume.security_groups.map(&:group_id)
109
+ end
110
+ end
111
+
112
+ def tags
113
+ catch_aws_errors do
114
+ @tags ||= volume.tags.map { |tag| { key: tag.key, value: tag.value } }
115
+ end
116
+ end
117
+
118
+ def to_s
119
+ "EBS Volume #{@display_name}"
120
+ end
121
+
122
+ private
123
+
124
+ def volume
125
+ catch_aws_errors { @volume ||= @ec2_resource.volume(id) }
126
+ end
127
+ end
@@ -0,0 +1,69 @@
1
+ require "resource_support/aws/aws_plural_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-ec2"
4
+
5
+ class AwsEbsVolumes < Inspec.resource(1)
6
+ name "aws_ebs_volumes"
7
+ desc "Verifies settings for AWS EBS Volumes in bulk"
8
+ example <<~EXAMPLE
9
+ describe aws_ebs_volumes do
10
+ it { should exist }
11
+ end
12
+ EXAMPLE
13
+ supports platform: "aws"
14
+
15
+ include AwsPluralResourceMixin
16
+ def validate_params(resource_params)
17
+ unless resource_params.empty?
18
+ raise ArgumentError, "aws_ebs_volumes does not accept resource parameters."
19
+ end
20
+
21
+ resource_params
22
+ end
23
+
24
+ # Underlying FilterTable implementation.
25
+ filter = FilterTable.create
26
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
27
+ filter.register_column(:volume_ids, field: :volume_id)
28
+ filter.install_filter_methods_on_resource(self, :table)
29
+
30
+ def to_s
31
+ "EBS Volumes"
32
+ end
33
+
34
+ def fetch_from_api
35
+ backend = BackendFactory.create(inspec_runner)
36
+ @table = []
37
+ pagination_opts = {}
38
+ loop do
39
+ api_result = backend.describe_volumes(pagination_opts)
40
+ @table += unpack_describe_volumes_response(api_result.volumes)
41
+ break unless api_result.next_token
42
+
43
+ pagination_opts = { next_token: api_result.next_token }
44
+ end
45
+ end
46
+
47
+ def unpack_describe_volumes_response(volumes)
48
+ volume_rows = []
49
+ volumes.each do |res|
50
+ volume_rows += res.attachments.map do |volume_struct|
51
+ {
52
+ volume_id: volume_struct.volume_id,
53
+ }
54
+ end
55
+ end
56
+ volume_rows
57
+ end
58
+
59
+ class Backend
60
+ class AwsClientApi < AwsBackendBase
61
+ BackendFactory.set_default_backend(self)
62
+ self.aws_client_class = Aws::EC2::Client
63
+
64
+ def describe_volumes(query)
65
+ aws_service_client.describe_volumes(query)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,162 @@
1
+ require "resource_support/aws/aws_singular_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-ec2"
4
+
5
+ class AwsEc2Instance < Inspec.resource(1)
6
+ name "aws_ec2_instance"
7
+ desc "Verifies settings for an EC2 instance"
8
+
9
+ example <<~EXAMPLE
10
+ describe aws_ec2_instance('i-123456') do
11
+ it { should be_running }
12
+ it { should have_roles }
13
+ end
14
+
15
+ describe aws_ec2_instance(name: 'my-instance') do
16
+ it { should be_running }
17
+ it { should have_roles }
18
+ end
19
+ EXAMPLE
20
+ supports platform: "aws"
21
+
22
+ # TODO: rewrite to avoid direct injection, match other resources, use AwsSingularResourceMixin
23
+ def initialize(opts, conn = nil)
24
+ @opts = opts
25
+ @opts.is_a?(Hash) ? @display_name = @opts[:name] : @display_name = opts
26
+ @ec2_client = conn ? conn.ec2_client : inspec_runner.backend.aws_client(Aws::EC2::Client)
27
+ @ec2_resource = conn ? conn.ec2_resource : inspec_runner.backend.aws_resource(Aws::EC2::Resource, {})
28
+ @iam_resource = conn ? conn.iam_resource : inspec_runner.backend.aws_resource(Aws::IAM::Resource, {})
29
+ end
30
+
31
+ # TODO: DRY up, see https://github.com/chef/inspec/issues/2633
32
+ # Copied from resource_support/aws/aws_resource_mixin.rb
33
+ def catch_aws_errors
34
+ yield
35
+ rescue Aws::Errors::MissingCredentialsError
36
+ # The AWS error here is unhelpful:
37
+ # "unable to sign request without credentials set"
38
+ Inspec::Log.error "It appears that you have not set your AWS credentials. You may set them using environment variables, or using the 'aws://region/aws_credentials_profile' target. See https://www.inspec.io/docs/reference/platforms for details."
39
+ fail_resource("No AWS credentials available")
40
+ rescue Aws::Errors::ServiceError => e
41
+ fail_resource e.message
42
+ end
43
+
44
+ # TODO: DRY up, see https://github.com/chef/inspec/issues/2633
45
+ # Copied from resource_support/aws/aws_singular_resource_mixin.rb
46
+ def inspec_runner
47
+ # When running under inspec-cli, we have an 'inspec' method that
48
+ # returns the runner. When running under unit tests, we don't
49
+ # have that, but we still have to call this to pass something
50
+ # (nil is OK) to the backend.
51
+ # TODO: remove with https://github.com/chef/inspec-aws/issues/216
52
+ # TODO: remove after rewrite to include AwsSingularResource
53
+ inspec if respond_to?(:inspec)
54
+ end
55
+
56
+ def id
57
+ return @instance_id if defined?(@instance_id)
58
+
59
+ catch_aws_errors do
60
+ if @opts.is_a?(Hash)
61
+ first = @ec2_resource.instances(
62
+ {
63
+ filters: [{
64
+ name: "tag:Name",
65
+ values: [@opts[:name]],
66
+ }],
67
+ }
68
+ ).first
69
+ # catch case where the instance is not known
70
+ @instance_id = first.id unless first.nil?
71
+ else
72
+ @instance_id = @opts
73
+ end
74
+ end
75
+ end
76
+ alias instance_id id
77
+
78
+ def exists?
79
+ return false if instance.nil?
80
+
81
+ instance.exists?
82
+ end
83
+
84
+ # returns the instance state
85
+ def state
86
+ catch_aws_errors do
87
+ instance&.state&.name
88
+ end
89
+ end
90
+
91
+ # helper methods for each state
92
+ %w{
93
+ pending running shutting-down
94
+ terminated stopping stopped unknown
95
+ }.each do |state_name|
96
+ define_method state_name.tr("-", "_") + "?" do
97
+ state == state_name
98
+ end
99
+ end
100
+
101
+ # attributes that we want to expose
102
+ %w{
103
+ public_ip_address private_ip_address key_name private_dns_name
104
+ public_dns_name subnet_id architecture root_device_type
105
+ root_device_name virtualization_type client_token launch_time
106
+ instance_type image_id vpc_id
107
+ }.each do |attribute|
108
+ define_method attribute do
109
+ catch_aws_errors do
110
+ instance.send(attribute) if instance
111
+ end
112
+ end
113
+ end
114
+
115
+ # Don't document this - it's a bit hard to use. Our current doctrine
116
+ # is to use dumb things, like arrays of strings - use security_group_ids instead.
117
+ def security_groups
118
+ catch_aws_errors do
119
+ @security_groups ||= instance.security_groups.map do |sg|
120
+ { id: sg.group_id, name: sg.group_name }
121
+ end
122
+ end
123
+ end
124
+
125
+ def security_group_ids
126
+ catch_aws_errors do
127
+ @security_group_ids ||= instance.security_groups.map(&:group_id)
128
+ end
129
+ end
130
+
131
+ def tags
132
+ catch_aws_errors do
133
+ @tags ||= instance.tags.map { |tag| { key: tag.key, value: tag.value } }
134
+ end
135
+ end
136
+
137
+ def to_s
138
+ "EC2 Instance #{@display_name}"
139
+ end
140
+
141
+ def has_roles?
142
+ catch_aws_errors do
143
+ instance_profile = instance.iam_instance_profile
144
+
145
+ if instance_profile
146
+ roles = @iam_resource.instance_profile(
147
+ instance_profile.arn.gsub(%r{^.*\/}, "")
148
+ ).roles
149
+ else
150
+ roles = nil
151
+ end
152
+
153
+ roles && !roles.empty?
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def instance
160
+ catch_aws_errors { @instance ||= @ec2_resource.instance(id) }
161
+ end
162
+ end