inspec 1.31.0 → 1.45.9

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.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +2599 -1
  4. data/Gemfile +0 -1
  5. data/README.md +24 -34
  6. data/Rakefile +0 -31
  7. data/docs/dsl_inspec.md +10 -0
  8. data/docs/matchers.md +29 -0
  9. data/docs/plugin_kitchen_inspec.md +18 -24
  10. data/docs/profiles.md +101 -81
  11. data/docs/resources/aide_conf.md.erb +71 -0
  12. data/docs/resources/apache_conf.md.erb +19 -33
  13. data/docs/resources/apt.md.erb +22 -36
  14. data/docs/resources/audit_policy.md.erb +9 -24
  15. data/docs/resources/auditd.md.erb +78 -0
  16. data/docs/resources/auditd_conf.md.erb +20 -34
  17. data/docs/resources/auditd_rules.md.erb +8 -24
  18. data/docs/resources/bash.md.erb +4 -26
  19. data/docs/resources/bond.md.erb +25 -40
  20. data/docs/resources/bridge.md.erb +5 -25
  21. data/docs/resources/bsd_service.md.erb +5 -25
  22. data/docs/resources/command.md.erb +35 -50
  23. data/docs/resources/cpan.md.erb +62 -0
  24. data/docs/resources/cran.md.erb +54 -0
  25. data/docs/resources/crontab.md.erb +9 -23
  26. data/docs/resources/csv.md.erb +12 -27
  27. data/docs/resources/dh_params.md +1 -0
  28. data/docs/resources/directory.md.erb +5 -25
  29. data/docs/resources/docker.md.erb +61 -58
  30. data/docs/resources/docker_container.md.erb +23 -19
  31. data/docs/resources/docker_image.md.erb +20 -16
  32. data/docs/resources/elasticsearch.md.erb +245 -0
  33. data/docs/resources/etc_fstab.md.erb +120 -0
  34. data/docs/resources/etc_group.md.erb +29 -45
  35. data/docs/resources/etc_hosts.md.erb +68 -0
  36. data/docs/resources/etc_hosts_allow.md.erb +68 -0
  37. data/docs/resources/etc_hosts_deny.md.erb +68 -0
  38. data/docs/resources/file.md.erb +227 -216
  39. data/docs/resources/firewalld.md.erb +104 -0
  40. data/docs/resources/gem.md.erb +21 -35
  41. data/docs/resources/group.md.erb +16 -30
  42. data/docs/resources/grub_conf.md.erb +9 -24
  43. data/docs/resources/host.md.erb +32 -49
  44. data/docs/resources/http.md.erb +38 -44
  45. data/docs/resources/iis_app.md.erb +25 -35
  46. data/docs/resources/iis_site.md.erb +26 -40
  47. data/docs/resources/inetd_conf.md.erb +27 -42
  48. data/docs/resources/ini.md.erb +9 -23
  49. data/docs/resources/interface.md.erb +5 -25
  50. data/docs/resources/iptables.md.erb +15 -29
  51. data/docs/resources/json.md.erb +12 -27
  52. data/docs/resources/kernel_module.md.erb +47 -61
  53. data/docs/resources/kernel_parameter.md.erb +15 -29
  54. data/docs/resources/key_rsa.md.erb +3 -0
  55. data/docs/resources/launchd_service.md.erb +5 -25
  56. data/docs/resources/limits_conf.md.erb +15 -29
  57. data/docs/resources/login_def.md.erb +15 -30
  58. data/docs/resources/mount.md.erb +18 -33
  59. data/docs/resources/mssql_session.md.erb +9 -12
  60. data/docs/resources/mysql_conf.md.erb +28 -32
  61. data/docs/resources/mysql_session.md.erb +15 -29
  62. data/docs/resources/nginx.md.erb +78 -0
  63. data/docs/resources/nginx_conf.md.erb +127 -0
  64. data/docs/resources/npm.md.erb +19 -35
  65. data/docs/resources/ntp_conf.md.erb +20 -37
  66. data/docs/resources/oneget.md.erb +15 -30
  67. data/docs/resources/oracledb_session.md.erb +9 -11
  68. data/docs/resources/os.md.erb +29 -43
  69. data/docs/resources/os_env.md.erb +23 -44
  70. data/docs/resources/package.md.erb +34 -36
  71. data/docs/resources/parse_config.md.erb +5 -25
  72. data/docs/resources/parse_config_file.md.erb +31 -43
  73. data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +24 -39
  74. data/docs/resources/pip.md.erb +20 -35
  75. data/docs/resources/port.md.erb +43 -57
  76. data/docs/resources/postgres_conf.md.erb +17 -31
  77. data/docs/resources/postgres_hba_conf.md.erb +26 -38
  78. data/docs/resources/postgres_ident_conf.md.erb +25 -37
  79. data/docs/resources/postgres_session.md.erb +15 -29
  80. data/docs/resources/powershell.md.erb +27 -42
  81. data/docs/resources/processes.md.erb +17 -33
  82. data/docs/resources/rabbitmq_config.md.erb +9 -24
  83. data/docs/resources/registry_key.md.erb +27 -42
  84. data/docs/resources/runit_service.md.erb +5 -25
  85. data/docs/resources/security_policy.md.erb +12 -27
  86. data/docs/resources/service.md.erb +27 -42
  87. data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +41 -47
  88. data/docs/resources/ssh_config.md.erb +19 -34
  89. data/docs/resources/sshd_config.md.erb +19 -34
  90. data/docs/resources/ssl.md.erb +39 -54
  91. data/docs/resources/sys_info.md.erb +12 -26
  92. data/docs/resources/systemd_service.md.erb +5 -25
  93. data/docs/resources/sysv_service.md.erb +5 -25
  94. data/docs/resources/upstart_service.md.erb +5 -25
  95. data/docs/resources/user.md.erb +29 -44
  96. data/docs/resources/users.md.erb +12 -26
  97. data/docs/resources/vbscript.md.erb +9 -24
  98. data/docs/resources/virtualization.md.erb +8 -23
  99. data/docs/resources/windows_feature.md.erb +15 -30
  100. data/docs/resources/windows_hotfix.md.erb +50 -0
  101. data/docs/resources/windows_task.md.erb +12 -26
  102. data/docs/resources/wmi.md.erb +9 -24
  103. data/docs/resources/x509_certificate.md.erb +4 -0
  104. data/docs/resources/xinetd_conf.md.erb +65 -80
  105. data/docs/resources/xml.md.erb +61 -0
  106. data/docs/resources/yaml.md.erb +12 -27
  107. data/docs/resources/yum.md.erb +37 -51
  108. data/docs/resources/zfs_dataset.md.erb +15 -26
  109. data/docs/resources/zfs_pool.md.erb +9 -20
  110. data/docs/ruby_usage.md +0 -1
  111. data/examples/meta-profile/README.md +1 -1
  112. data/examples/profile-sensitive/README.md +29 -0
  113. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  114. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  115. data/examples/profile-sensitive/inspec.yml +8 -0
  116. data/inspec.gemspec +3 -3
  117. data/lib/bundles/inspec-artifact/cli.rb +5 -5
  118. data/lib/bundles/inspec-compliance/README.md +24 -22
  119. data/lib/bundles/inspec-compliance/api/login.rb +152 -0
  120. data/lib/bundles/inspec-compliance/api.rb +46 -16
  121. data/lib/bundles/inspec-compliance/cli.rb +58 -165
  122. data/lib/bundles/inspec-compliance/target.rb +2 -3
  123. data/lib/bundles/inspec-habitat/cli.rb +7 -2
  124. data/lib/bundles/inspec-habitat/profile.rb +44 -20
  125. data/lib/bundles/inspec-init/templates/profile/README.md +1 -1
  126. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +1 -1
  127. data/lib/bundles/inspec-supermarket/api.rb +1 -1
  128. data/lib/inspec/backend.rb +8 -0
  129. data/lib/inspec/cli.rb +1 -1
  130. data/lib/inspec/control_eval_context.rb +16 -6
  131. data/lib/inspec/dependencies/requirement.rb +22 -8
  132. data/lib/inspec/dependencies/resolver.rb +1 -1
  133. data/lib/inspec/exceptions.rb +4 -0
  134. data/lib/inspec/file_provider.rb +38 -9
  135. data/lib/inspec/metadata.rb +17 -3
  136. data/lib/inspec/objects/attribute.rb +16 -7
  137. data/lib/inspec/objects/control.rb +10 -2
  138. data/lib/inspec/plugins/resource.rb +34 -5
  139. data/lib/inspec/profile.rb +10 -1
  140. data/lib/inspec/profile_context.rb +3 -1
  141. data/lib/inspec/resource.rb +17 -5
  142. data/lib/inspec/rspec_json_formatter.rb +48 -14
  143. data/lib/inspec/rule.rb +4 -0
  144. data/lib/inspec/runner.rb +72 -30
  145. data/lib/inspec/shell.rb +24 -19
  146. data/lib/inspec/version.rb +1 -1
  147. data/lib/matchers/matchers.rb +40 -0
  148. data/lib/resources/aide_conf.rb +162 -0
  149. data/lib/resources/auditd.rb +231 -0
  150. data/lib/resources/auditd_rules.rb +3 -1
  151. data/lib/resources/command.rb +1 -1
  152. data/lib/resources/cpan.rb +60 -0
  153. data/lib/resources/cran.rb +66 -0
  154. data/lib/resources/docker.rb +8 -1
  155. data/lib/resources/docker_container.rb +19 -2
  156. data/lib/resources/elasticsearch.rb +172 -0
  157. data/lib/resources/etc_fstab.rb +102 -0
  158. data/lib/resources/etc_group.rb +1 -1
  159. data/lib/resources/etc_hosts.rb +81 -0
  160. data/lib/resources/etc_hosts_allow_deny.rb +122 -0
  161. data/lib/resources/file.rb +87 -10
  162. data/lib/resources/firewalld.rb +144 -0
  163. data/lib/resources/groups.rb +0 -1
  164. data/lib/resources/host.rb +30 -15
  165. data/lib/resources/http.rb +178 -22
  166. data/lib/resources/mssql_session.rb +3 -3
  167. data/lib/resources/mysql_conf.rb +10 -0
  168. data/lib/resources/mysql_session.rb +1 -1
  169. data/lib/resources/nginx.rb +97 -0
  170. data/lib/resources/nginx_conf.rb +134 -1
  171. data/lib/resources/oracledb_session.rb +9 -8
  172. data/lib/resources/os_env.rb +7 -4
  173. data/lib/resources/package.rb +14 -2
  174. data/lib/resources/packages.rb +1 -1
  175. data/lib/resources/pip.rb +28 -20
  176. data/lib/resources/port.rb +251 -3
  177. data/lib/resources/postgres.rb +1 -1
  178. data/lib/resources/postgres_session.rb +1 -1
  179. data/lib/resources/processes.rb +106 -20
  180. data/lib/resources/registry_key.rb +11 -2
  181. data/lib/resources/service.rb +5 -24
  182. data/lib/resources/ssh_conf.rb +1 -1
  183. data/lib/resources/ssl.rb +4 -3
  184. data/lib/resources/toml.rb +2 -2
  185. data/lib/resources/windows_hotfix.rb +35 -0
  186. data/lib/resources/xinetd.rb +4 -6
  187. data/lib/resources/xml.rb +27 -0
  188. data/lib/source_readers/inspec.rb +3 -3
  189. data/lib/utils/find_files.rb +2 -1
  190. data/lib/utils/nginx_parser.rb +10 -1
  191. data/lib/utils/object_traversal.rb +35 -10
  192. data/lib/utils/parser.rb +21 -3
  193. metadata +48 -18
  194. data/docs/cli.md +0 -392
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+ # author: Markus Grobelin
5
+
6
+ # Usage:
7
+ # describe cpan('DBD::Pg') do
8
+ # it { should be_installed }
9
+ # end
10
+ #
11
+
12
+ module Inspec::Resources
13
+ class CpanPackage < Inspec.resource(1)
14
+ name 'cpan'
15
+ desc 'Use the `cpan` InSpec audit resource to test Perl modules that are installed by system packages or the CPAN installer.'
16
+ example "
17
+ describe cpan('DBD::Pg') do
18
+ it { should be_installed }
19
+ end
20
+ "
21
+
22
+ def initialize(package_name, perl_lib_path = nil)
23
+ @package_name = package_name
24
+ @perl_lib_path = perl_lib_path
25
+ @perl_cmd = 'perl'
26
+
27
+ # this resource is not supported on Windows
28
+ return skip_resource 'The `cpan` resource is not supported on your OS yet.' if inspec.os.windows?
29
+ return skip_resource 'perl not found' unless inspec.command(@perl_cmd).exist?
30
+ end
31
+
32
+ def info
33
+ return @info if defined?(@info)
34
+
35
+ @info = {}
36
+ @info[:type] = 'cpan'
37
+ @info[:name] = @package_name
38
+ # set PERL5LIB environment variable if a custom lib path is given
39
+ lib_path = @perl_lib_path.nil? ? '' : "PERL5LIB=#{@perl_lib_path} "
40
+ cmd = inspec.command("#{lib_path+@perl_cmd} -le 'eval \"require $ARGV[0]\" and print $ARGV[0]->VERSION or exit 1' #{@package_name}")
41
+ @info[:installed] = cmd.exit_status.zero?
42
+ return @info unless cmd.exit_status.zero?
43
+
44
+ @info[:version] = cmd.stdout.strip
45
+ @info
46
+ end
47
+
48
+ def installed?
49
+ info[:installed] == true
50
+ end
51
+
52
+ def version
53
+ info[:version]
54
+ end
55
+
56
+ def to_s
57
+ "Perl Module #{@package_name}"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+ # author: Markus Grobelin
5
+
6
+ # Usage:
7
+ # describe cran('DBI') do
8
+ # it { should be_installed }
9
+ # end
10
+ #
11
+
12
+ module Inspec::Resources
13
+ class CranPackage < Inspec.resource(1)
14
+ name 'cran'
15
+ desc 'Use the `cran` InSpec audit resource to test R modules that are installed from CRAN package repository.'
16
+ example "
17
+ describe cran('DBI') do
18
+ it { should be_installed }
19
+ end
20
+ "
21
+
22
+ def initialize(package_name)
23
+ @package_name = package_name
24
+ @r_cmd = 'Rscript'
25
+
26
+ # this resource is not supported on Windows
27
+ return skip_resource 'The `cran` resource is not supported on your OS yet.' if inspec.os.windows?
28
+ return skip_resource 'Rscript not found' unless inspec.command(@r_cmd).exist?
29
+ end
30
+
31
+ def info
32
+ return @info if defined?(@info)
33
+
34
+ @info = {}
35
+ @info[:type] = 'cran'
36
+ @info[:name] = @package_name
37
+ cmd = inspec.command("#{@r_cmd} -e 'packageVersion(\"#{@package_name}\")'")
38
+ return @info unless cmd.exit_status.zero?
39
+
40
+ # Extract package version from Rscript output
41
+ # Output includes unicode punctuation (backticks) characters like so:
42
+ # [1] '0.5.1'
43
+ #
44
+ # So make sure command output is converted to unicode, as it returns ASCII-8BIT by default
45
+ utf8_stdout = cmd.stdout.chomp.force_encoding(Encoding::UTF_8)
46
+ params = /^\[\d+\]\s+(?:\p{Initial_Punctuation})(.+)(?:\p{Final_Punctuation})$/.match(utf8_stdout)
47
+ @info[:installed] = !params.nil?
48
+ return @info unless @info[:installed]
49
+
50
+ @info[:version] = params[1]
51
+ @info
52
+ end
53
+
54
+ def installed?
55
+ info[:installed] == true
56
+ end
57
+
58
+ def version
59
+ info[:version]
60
+ end
61
+
62
+ def to_s
63
+ "R Module #{@package_name}"
64
+ end
65
+ end
66
+ end
@@ -63,7 +63,7 @@ module Inspec::Resources
63
63
  # For compatability with Serverspec we also offer the following resouses:
64
64
  # - docker_container
65
65
  # - docker_image
66
- class Docker < Inspec.resource(1)
66
+ class Docker < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
67
67
  name 'docker'
68
68
 
69
69
  desc "
@@ -167,6 +167,13 @@ module Inspec::Resources
167
167
 
168
168
  # ensure all keys are there
169
169
  j = ensure_container_keys(j)
170
+
171
+ # strip off any linked container names
172
+ # Depending on how it was linked, the actual container name may come before
173
+ # or after the link information, so we'll just look for the first name that
174
+ # does not include a slash since that is not a valid character in a container name
175
+ j['names'] = j['names'].split(',').find { |c| !c.include?('/') }
176
+
170
177
  ps.push(j)
171
178
  }
172
179
  ps
@@ -74,11 +74,18 @@ module Inspec::Resources
74
74
  end
75
75
 
76
76
  def repo
77
- image.split(':')[0] unless image.nil?
77
+ return if image.nil? || image_name_from_image.nil?
78
+ if image.include?('/') # host:port/ubuntu:latest
79
+ repo_part, image_part = image.split('/') # host:port, ubuntu:latest
80
+ repo_part + '/' + image_part.split(':')[0] # host:port + / + ubuntu
81
+ else
82
+ image_name_from_image.split(':')[0]
83
+ end
78
84
  end
79
85
 
80
86
  def tag
81
- image.split(':')[1] unless image.nil?
87
+ return if image_name_from_image.nil?
88
+ image_name_from_image.split(':')[1]
82
89
  end
83
90
 
84
91
  def to_s
@@ -88,6 +95,16 @@ module Inspec::Resources
88
95
 
89
96
  private
90
97
 
98
+ def image_name_from_image
99
+ return if image.nil?
100
+ # possible image names include:
101
+ # alpine
102
+ # ubuntu:14.04
103
+ # repo.example.com:5000/ubuntu
104
+ # repo.example.com:5000/ubuntu:1404
105
+ image.include?('/') ? image.split('/')[1] : image
106
+ end
107
+
91
108
  def container_info
92
109
  return @info if defined?(@info)
93
110
  opts = @opts
@@ -0,0 +1,172 @@
1
+ # encoding: utf-8
2
+
3
+ require 'utils/filter'
4
+ require 'hashie/mash'
5
+ require 'resources/package'
6
+
7
+ module Inspec::Resources
8
+ class Elasticsearch < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
9
+ name 'elasticsearch'
10
+ desc "Use the Elasticsearch InSpec audit resource to test the status of nodes in
11
+ an Elasticsearch cluster."
12
+
13
+ example "
14
+ describe elasticsearch('http://eshost.mycompany.biz:9200/', username: 'elastic', password: 'changeme', ssl_verify: false) do
15
+ its('node_count') { should >= 3 }
16
+ end
17
+
18
+ describe elasticsearch do
19
+ its('node_name') { should include 'node1' }
20
+ its('os') { should_not include 'MacOS' }
21
+ its('version') { should cmp > 1.2.0 }
22
+ end
23
+ "
24
+
25
+ filter = FilterTable.create
26
+ filter.add_accessor(:where)
27
+ .add_accessor(:entries)
28
+ .add(:cluster_name, field: 'cluster_name')
29
+ .add(:node_name, field: 'name')
30
+ .add(:transport_address, field: 'transport_address')
31
+ .add(:host, field: 'host')
32
+ .add(:ip, field: 'ip')
33
+ .add(:version, field: 'version')
34
+ .add(:build_hash, field: 'build_hash')
35
+ .add(:total_indexing_buffer, field: 'total_indexing_buffer')
36
+ .add(:roles, field: 'roles')
37
+ .add(:settings, field: 'settings')
38
+ .add(:os, field: 'os')
39
+ .add(:process, field: 'process')
40
+ .add(:jvm, field: 'jvm')
41
+ .add(:transport, field: 'transport')
42
+ .add(:http, field: 'http')
43
+ .add(:plugins, field: 'plugins')
44
+ .add(:plugin_list, field: 'plugin_list')
45
+ .add(:modules, field: 'modules')
46
+ .add(:module_list, field: 'module_list')
47
+ .add(:node_id, field: 'node_id')
48
+ .add(:ingest, field: 'ingest')
49
+ .add(:exists?) { |x| !x.entries.empty? }
50
+ .add(:node_count) { |t, _|
51
+ t.entries.length
52
+ }
53
+
54
+ filter.connect(self, :nodes)
55
+
56
+ attr_reader :nodes, :url
57
+
58
+ def initialize(opts = {})
59
+ return skip_resource 'Package `curl` not avaiable on the host' unless inspec.command('curl').exist?
60
+
61
+ @url = opts.fetch(:url, 'http://localhost:9200')
62
+
63
+ username = opts.fetch(:username, nil)
64
+ password = opts.fetch(:password, nil)
65
+ ssl_verify = opts.fetch(:ssl_verify, true)
66
+
67
+ cmd = inspec.command(curl_command_string(username, password, ssl_verify))
68
+
69
+ # after implementation of PR #2235, this begin..rescue won't be necessary.
70
+ # The checks in verify_curl_success! can raise their own skip message exception.
71
+ begin
72
+ verify_curl_success!(cmd)
73
+ rescue => e
74
+ return skip_resource e.message
75
+ end
76
+
77
+ begin
78
+ content = JSON.parse(cmd.stdout)
79
+ # after implementation of PR #2235, this can be broken out of the begin..rescue
80
+ # clause. The checks in verify_json_payload! can raise their own skip message exception.
81
+ verify_json_payload!(content)
82
+ rescue JSON::ParserError => e
83
+ return skip_resource "Couldn't parse the Elasticsearch response: #{e.message}"
84
+ rescue => e
85
+ return skip_resource e.message
86
+ end
87
+
88
+ @nodes = parse_cluster(content)
89
+ end
90
+
91
+ def to_s
92
+ "Elasticsearch Cluster #{url}"
93
+ end
94
+
95
+ private
96
+
97
+ def parse_cluster(content)
98
+ return [] unless content['nodes']
99
+
100
+ nodes = []
101
+
102
+ content['nodes'].each do |node_id, node_data|
103
+ node_data = fix_mash_key_collision(node_data)
104
+
105
+ node = Hashie::Mash.new(node_data)
106
+ node.node_id = node_id
107
+ node.plugin_list = node.plugins.map(&:name)
108
+ node.module_list = node.modules.map(&:name)
109
+ node.cluster_name = node.settings.cluster.name
110
+ nodes << node
111
+ end
112
+
113
+ nodes
114
+ end
115
+
116
+ #
117
+ # Hashie::Mash will throw warnings if the Mash contains a key that is the same as a built-in
118
+ # method on a Hashie::Mash instance. This is a crude way of avoiding those warnings without
119
+ # hard-coding a bunch of key renames.
120
+ #
121
+ # Any key that is in conflict will be renamed "es_ORIGINALKEY"
122
+ #
123
+ def fix_mash_key_collision(data)
124
+ test_mash = Hashie::Mash.new
125
+
126
+ new_data = {}
127
+ data.each do |key, value|
128
+ new_key = test_mash.respond_to?(key.to_sym) ? "es_#{key}" : key
129
+ new_value = value.is_a?(Hash) ? fix_mash_key_collision(value) : value
130
+
131
+ new_data[new_key] = new_value
132
+ end
133
+
134
+ new_data
135
+ end
136
+
137
+ def curl_command_string(username, password, ssl_verify)
138
+ cmd_string = ['curl']
139
+ cmd_string << '-k' unless ssl_verify
140
+ cmd_string << "-H 'Content-Type: application/json'"
141
+ cmd_string << " -u #{username}:#{password}" unless username.nil? || password.nil?
142
+ cmd_string << URI.join(url, '_nodes')
143
+
144
+ cmd_string.join(' ')
145
+ end
146
+
147
+ def verify_curl_success!(cmd)
148
+ # the following lines captures known possible curl command errors and provides compact skip resource messeges
149
+ if cmd.stderr =~ /Failed to connect/
150
+ raise "Connection refused - please check the URL #{url} for accuracy"
151
+ end
152
+
153
+ if cmd.stderr =~ /Peer's Certificate issuer is not recognized/
154
+ raise 'Connection refused - peer certificate issuer is not recognized'
155
+ end
156
+
157
+ if !cmd.exit_status.zero?
158
+ raise "Error fetching Elastcsearch data from curl #{url}: #{cmd.stderr}"
159
+ end
160
+ end
161
+
162
+ def verify_json_payload!(content)
163
+ unless content['error'].nil?
164
+ raise "#{content['error']['type']}: #{content['error']['reason']}"
165
+ end
166
+
167
+ if content['_nodes']['successful'].zero?
168
+ raise 'No successful nodes available in cluster'
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+ # copyright:
3
+ # author: Matthew Dromazos
4
+
5
+ require 'utils/parser'
6
+
7
+ module Inspec::Resources
8
+ class EtcFstab < Inspec.resource(1)
9
+ name 'etc_fstab'
10
+ desc 'Use the etc_fstab InSpec audit resource to check the configuration of the etc/fstab file.'
11
+ example "
12
+ nfs_systems = etc_fstab.nfs_file_systems.entries
13
+ nfs_systems.each do |file_system|
14
+ describe file_system do
15
+ its ('mount_options') { should include 'nosuid' }
16
+ its ('mount_options') { should include 'noexec' }
17
+ its ('mount_options') { should include 'sec=krb5:krb5i:krb5p }
18
+ end
19
+ end
20
+
21
+ describe etc_fstab do
22
+ its ('home_mount_options') { should include 'nosuid' }
23
+ end
24
+ "
25
+
26
+ attr_reader :params
27
+
28
+ include CommentParser
29
+
30
+ def initialize(fstab_path = nil)
31
+ return skip_resource 'The `etc_fstab` resource is not supported on your OS.' unless inspec.os.linux?
32
+ @conf_path = fstab_path || '/etc/fstab'
33
+ @files_contents = {}
34
+ @content = nil
35
+ @params = nil
36
+ read_content
37
+ end
38
+
39
+ filter = FilterTable.create
40
+ filter.add_accessor(:where)
41
+ .add_accessor(:entries)
42
+ .add(:device_name, field: 'device_name')
43
+ .add(:mount_point, field: 'mount_point')
44
+ .add(:file_system_type, field: 'file_system_type')
45
+ .add(:mount_options, field: 'mount_options')
46
+ .add(:dump_options, field: 'dump_options')
47
+ .add(:file_system_options, field: 'file_system_options')
48
+ .add(:configured?) { |x| x.entries.any? }
49
+
50
+ filter.connect(self, :params)
51
+
52
+ def nfs_file_systems
53
+ where { file_system_type.match(/nfs/) }
54
+ end
55
+
56
+ def home_mount_options
57
+ return nil unless where { mount_point == '/home' }.configured?
58
+ where { mount_point == '/home' }.entries[0].mount_options
59
+ end
60
+
61
+ private
62
+
63
+ def read_content
64
+ @content = ''
65
+ @params = {}
66
+ @content = read_file(@conf_path)
67
+ @params = parse_conf(@content)
68
+ end
69
+
70
+ def parse_conf(content)
71
+ content.map do |line|
72
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
73
+ parse_line(data) unless data == ''
74
+ end.compact
75
+ end
76
+
77
+ def parse_line(line)
78
+ attributes = line.split
79
+ {
80
+ 'device_name' => attributes[0],
81
+ 'mount_point' => attributes[1],
82
+ 'file_system_type' => attributes[2],
83
+ 'mount_options' => attributes[3].split(','),
84
+ 'dump_options' => attributes[4].to_i,
85
+ 'file_system_options' => attributes[5].to_i,
86
+ }
87
+ end
88
+
89
+ def read_file(conf_path = @conf_path)
90
+ file = inspec.file(conf_path)
91
+ if !file.file?
92
+ return skip_resource "Can't find \"#{@conf_path}\""
93
+ end
94
+
95
+ raw_conf = file.content
96
+ if raw_conf.empty? && !file.empty?
97
+ return skip_resource("File is empty or unable to read file at path:\"#{@conf_path}\"")
98
+ end
99
+ raw_conf.lines
100
+ end
101
+ end
102
+ end
@@ -85,7 +85,7 @@ module Inspec::Resources
85
85
  conditions.each do |k, v|
86
86
  idx = fields[k.to_sym]
87
87
  next if idx.nil?
88
- res = res.select { |x| x[idx] == v.to_s }
88
+ res = res.select { |x| x[idx].to_s == v.to_s }
89
89
  end
90
90
 
91
91
  EtcGroupView.new(self, res)
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ # author: Matthew Dromazos
3
+
4
+ require 'utils/parser'
5
+
6
+ class EtcHosts < Inspec.resource(1)
7
+ name 'etc_hosts'
8
+ desc 'Use the etc_hosts InSpec audit resource to find an
9
+ ip_address and its associated hosts'
10
+ example "
11
+ describe etc_hosts.where { ip_address == '127.0.0.1' } do
12
+ its('ip_address') { should cmp '127.0.0.1' }
13
+ its('primary_name') { should cmp 'localhost' }
14
+ its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4']] }
15
+ end
16
+ "
17
+
18
+ attr_reader :params
19
+
20
+ include CommentParser
21
+
22
+ def initialize(hosts_path = nil)
23
+ return skip_resource 'The `etc_hosts` resource is not supported on your OS.' unless inspec.os.bsd? || inspec.os.linux? || inspec.os.windows?
24
+ @conf_path = hosts_path || default_hosts_file_path
25
+ @content = nil
26
+ @params = nil
27
+ read_content
28
+ end
29
+
30
+ filter = FilterTable.create
31
+ filter.add_accessor(:where)
32
+ .add_accessor(:entries)
33
+ .add(:ip_address, field: 'ip_address')
34
+ .add(:primary_name, field: 'primary_name')
35
+ .add(:all_host_names, field: 'all_host_names')
36
+
37
+ filter.connect(self, :params)
38
+
39
+ private
40
+
41
+ def default_hosts_file_path
42
+ inspec.os.windows? ? 'C:\windows\system32\drivers\etc\hosts' : '/etc/hosts'
43
+ end
44
+
45
+ def read_content
46
+ @content = ''
47
+ @params = {}
48
+ @content = read_file(@conf_path)
49
+ @params = parse_conf(@content)
50
+ end
51
+
52
+ def parse_conf(content)
53
+ content.map do |line|
54
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
55
+ parse_line(data) unless data == ''
56
+ end.compact
57
+ end
58
+
59
+ def parse_line(line)
60
+ line_parts = line.split
61
+ return nil unless line_parts.length >= 2
62
+ {
63
+ 'ip_address' => line_parts[0],
64
+ 'primary_name' => line_parts[1],
65
+ 'all_host_names' => line_parts[1..-1],
66
+ }
67
+ end
68
+
69
+ def read_file(conf_path = @conf_path)
70
+ file = inspec.file(conf_path)
71
+ if !file.file?
72
+ return skip_resource "Can't find file. \"#{@conf_path}\""
73
+ end
74
+
75
+ raw_conf = file.content
76
+ if raw_conf.empty? && !file.empty?
77
+ return skip_resource("Could not read file contents\" #{@conf_path}\"")
78
+ end
79
+ raw_conf.lines
80
+ end
81
+ end
@@ -0,0 +1,122 @@
1
+ # encoding: utf-8
2
+ # author: Matthew Dromazos
3
+
4
+ require 'utils/parser'
5
+
6
+ module Inspec::Resources
7
+ class EtcHostsAllow < Inspec.resource(1)
8
+ name 'etc_hosts_allow'
9
+ desc 'Use the etc_hosts_allow InSpec audit resource to test the connections
10
+ the client will allow. Controlled by the /etc/hosts.allow file.'
11
+ example "
12
+ describe etc_hosts_allow.where { daemon == 'ALL' } do
13
+ its('client_list') { should include ['127.0.0.1', '[::1]'] }
14
+ its('options') { should eq [[]] }
15
+ end
16
+ "
17
+
18
+ attr_reader :params
19
+
20
+ include CommentParser
21
+
22
+ def initialize(hosts_allow_path = nil)
23
+ return skip_resource 'The `etc_hosts_allow` resource is not supported on your OS.' unless inspec.os.linux?
24
+ @conf_path = hosts_allow_path || '/etc/hosts.allow'
25
+ @content = nil
26
+ @params = nil
27
+ read_content
28
+ end
29
+
30
+ filter = FilterTable.create
31
+ filter.add_accessor(:where)
32
+ .add_accessor(:entries)
33
+ .add(:daemon, field: 'daemon')
34
+ .add(:client_list, field: 'client_list')
35
+ .add(:options, field: 'options')
36
+
37
+ filter.connect(self, :params)
38
+
39
+ private
40
+
41
+ def read_content
42
+ @content = ''
43
+ @params = {}
44
+ @content = split_daemons(read_file(@conf_path))
45
+ @params = parse_conf(@content)
46
+ end
47
+
48
+ def split_daemons(content)
49
+ split_daemons_list = []
50
+ content.each do |line|
51
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
52
+ next unless data != ''
53
+ data.split(':')[0].split(',').each do |daemon|
54
+ split_daemons_list.push("#{daemon} : " + line.split(':', 2)[1])
55
+ end
56
+ end
57
+ split_daemons_list
58
+ end
59
+
60
+ def parse_conf(content)
61
+ content.map do |line|
62
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
63
+ parse_line(data) unless data == ''
64
+ end.compact
65
+ end
66
+
67
+ def parse_line(line)
68
+ daemon, clients_and_options = line.split(/:\s+/, 2)
69
+ daemon = daemon.strip
70
+
71
+ clients_and_options ||= ''
72
+ clients, options = clients_and_options.split(/\s+:\s+/, 2)
73
+ client_list = clients.split(/,/).map(&:strip)
74
+
75
+ options ||= ''
76
+ options_list = options.split(/:\s+/).map(&:strip)
77
+
78
+ {
79
+ 'daemon' => daemon,
80
+ 'client_list' => client_list,
81
+ 'options' => options_list,
82
+ }
83
+ end
84
+
85
+ def read_file(conf_path = @conf_path)
86
+ # Determine if the file exists and contains anything, if not
87
+ # then access control is turned off.
88
+ file = inspec.file(conf_path)
89
+ if !file.file?
90
+ return skip_resource "Can't find file at \"#{@conf_path}\""
91
+ end
92
+ raw_conf = file.content
93
+ if raw_conf.empty? && !file.empty?
94
+ return skip_resource("Unable to read file \"#{@conf_path}\"")
95
+ end
96
+
97
+ # If there is a file and it contains content, continue
98
+ raw_conf.lines
99
+ end
100
+ end
101
+
102
+ class EtcHostsDeny < EtcHostsAllow
103
+ name 'etc_hosts_deny'
104
+ desc 'Use the etc_hosts_deny InSpec audit resource to test the connections
105
+ the client will deny. Controlled by the /etc/hosts.deny file.'
106
+ example "
107
+ describe etc_hosts_deny.where { daemon_list == 'ALL' } do
108
+ its('client_list') { should eq [['127.0.0.1', '[::1]']] }
109
+ its('options') { should eq [] }
110
+ end
111
+ "
112
+
113
+ def initialize(path = nil)
114
+ return skip_resource '`etc_hosts_deny` is not supported on your OS' unless inspec.os.linux?
115
+ super(path || '/etc/hosts.deny')
116
+ end
117
+
118
+ def to_s
119
+ 'hosts.deny Configuration'
120
+ end
121
+ end
122
+ end