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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +2599 -1
- data/Gemfile +0 -1
- data/README.md +24 -34
- data/Rakefile +0 -31
- data/docs/dsl_inspec.md +10 -0
- data/docs/matchers.md +29 -0
- data/docs/plugin_kitchen_inspec.md +18 -24
- data/docs/profiles.md +101 -81
- data/docs/resources/aide_conf.md.erb +71 -0
- data/docs/resources/apache_conf.md.erb +19 -33
- data/docs/resources/apt.md.erb +22 -36
- data/docs/resources/audit_policy.md.erb +9 -24
- data/docs/resources/auditd.md.erb +78 -0
- data/docs/resources/auditd_conf.md.erb +20 -34
- data/docs/resources/auditd_rules.md.erb +8 -24
- data/docs/resources/bash.md.erb +4 -26
- data/docs/resources/bond.md.erb +25 -40
- data/docs/resources/bridge.md.erb +5 -25
- data/docs/resources/bsd_service.md.erb +5 -25
- data/docs/resources/command.md.erb +35 -50
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/crontab.md.erb +9 -23
- data/docs/resources/csv.md.erb +12 -27
- data/docs/resources/dh_params.md +1 -0
- data/docs/resources/directory.md.erb +5 -25
- data/docs/resources/docker.md.erb +61 -58
- data/docs/resources/docker_container.md.erb +23 -19
- data/docs/resources/docker_image.md.erb +20 -16
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/etc_fstab.md.erb +120 -0
- data/docs/resources/etc_group.md.erb +29 -45
- data/docs/resources/etc_hosts.md.erb +68 -0
- data/docs/resources/etc_hosts_allow.md.erb +68 -0
- data/docs/resources/etc_hosts_deny.md.erb +68 -0
- data/docs/resources/file.md.erb +227 -216
- data/docs/resources/firewalld.md.erb +104 -0
- data/docs/resources/gem.md.erb +21 -35
- data/docs/resources/group.md.erb +16 -30
- data/docs/resources/grub_conf.md.erb +9 -24
- data/docs/resources/host.md.erb +32 -49
- data/docs/resources/http.md.erb +38 -44
- data/docs/resources/iis_app.md.erb +25 -35
- data/docs/resources/iis_site.md.erb +26 -40
- data/docs/resources/inetd_conf.md.erb +27 -42
- data/docs/resources/ini.md.erb +9 -23
- data/docs/resources/interface.md.erb +5 -25
- data/docs/resources/iptables.md.erb +15 -29
- data/docs/resources/json.md.erb +12 -27
- data/docs/resources/kernel_module.md.erb +47 -61
- data/docs/resources/kernel_parameter.md.erb +15 -29
- data/docs/resources/key_rsa.md.erb +3 -0
- data/docs/resources/launchd_service.md.erb +5 -25
- data/docs/resources/limits_conf.md.erb +15 -29
- data/docs/resources/login_def.md.erb +15 -30
- data/docs/resources/mount.md.erb +18 -33
- data/docs/resources/mssql_session.md.erb +9 -12
- data/docs/resources/mysql_conf.md.erb +28 -32
- data/docs/resources/mysql_session.md.erb +15 -29
- data/docs/resources/nginx.md.erb +78 -0
- data/docs/resources/nginx_conf.md.erb +127 -0
- data/docs/resources/npm.md.erb +19 -35
- data/docs/resources/ntp_conf.md.erb +20 -37
- data/docs/resources/oneget.md.erb +15 -30
- data/docs/resources/oracledb_session.md.erb +9 -11
- data/docs/resources/os.md.erb +29 -43
- data/docs/resources/os_env.md.erb +23 -44
- data/docs/resources/package.md.erb +34 -36
- data/docs/resources/parse_config.md.erb +5 -25
- data/docs/resources/parse_config_file.md.erb +31 -43
- data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +24 -39
- data/docs/resources/pip.md.erb +20 -35
- data/docs/resources/port.md.erb +43 -57
- data/docs/resources/postgres_conf.md.erb +17 -31
- data/docs/resources/postgres_hba_conf.md.erb +26 -38
- data/docs/resources/postgres_ident_conf.md.erb +25 -37
- data/docs/resources/postgres_session.md.erb +15 -29
- data/docs/resources/powershell.md.erb +27 -42
- data/docs/resources/processes.md.erb +17 -33
- data/docs/resources/rabbitmq_config.md.erb +9 -24
- data/docs/resources/registry_key.md.erb +27 -42
- data/docs/resources/runit_service.md.erb +5 -25
- data/docs/resources/security_policy.md.erb +12 -27
- data/docs/resources/service.md.erb +27 -42
- data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +41 -47
- data/docs/resources/ssh_config.md.erb +19 -34
- data/docs/resources/sshd_config.md.erb +19 -34
- data/docs/resources/ssl.md.erb +39 -54
- data/docs/resources/sys_info.md.erb +12 -26
- data/docs/resources/systemd_service.md.erb +5 -25
- data/docs/resources/sysv_service.md.erb +5 -25
- data/docs/resources/upstart_service.md.erb +5 -25
- data/docs/resources/user.md.erb +29 -44
- data/docs/resources/users.md.erb +12 -26
- data/docs/resources/vbscript.md.erb +9 -24
- data/docs/resources/virtualization.md.erb +8 -23
- data/docs/resources/windows_feature.md.erb +15 -30
- data/docs/resources/windows_hotfix.md.erb +50 -0
- data/docs/resources/windows_task.md.erb +12 -26
- data/docs/resources/wmi.md.erb +9 -24
- data/docs/resources/x509_certificate.md.erb +4 -0
- data/docs/resources/xinetd_conf.md.erb +65 -80
- data/docs/resources/xml.md.erb +61 -0
- data/docs/resources/yaml.md.erb +12 -27
- data/docs/resources/yum.md.erb +37 -51
- data/docs/resources/zfs_dataset.md.erb +15 -26
- data/docs/resources/zfs_pool.md.erb +9 -20
- data/docs/ruby_usage.md +0 -1
- data/examples/meta-profile/README.md +1 -1
- data/examples/profile-sensitive/README.md +29 -0
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
- data/examples/profile-sensitive/controls/sensitive.rb +9 -0
- data/examples/profile-sensitive/inspec.yml +8 -0
- data/inspec.gemspec +3 -3
- data/lib/bundles/inspec-artifact/cli.rb +5 -5
- data/lib/bundles/inspec-compliance/README.md +24 -22
- data/lib/bundles/inspec-compliance/api/login.rb +152 -0
- data/lib/bundles/inspec-compliance/api.rb +46 -16
- data/lib/bundles/inspec-compliance/cli.rb +58 -165
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/bundles/inspec-habitat/cli.rb +7 -2
- data/lib/bundles/inspec-habitat/profile.rb +44 -20
- data/lib/bundles/inspec-init/templates/profile/README.md +1 -1
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +1 -1
- data/lib/bundles/inspec-supermarket/api.rb +1 -1
- data/lib/inspec/backend.rb +8 -0
- data/lib/inspec/cli.rb +1 -1
- data/lib/inspec/control_eval_context.rb +16 -6
- data/lib/inspec/dependencies/requirement.rb +22 -8
- data/lib/inspec/dependencies/resolver.rb +1 -1
- data/lib/inspec/exceptions.rb +4 -0
- data/lib/inspec/file_provider.rb +38 -9
- data/lib/inspec/metadata.rb +17 -3
- data/lib/inspec/objects/attribute.rb +16 -7
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/plugins/resource.rb +34 -5
- data/lib/inspec/profile.rb +10 -1
- data/lib/inspec/profile_context.rb +3 -1
- data/lib/inspec/resource.rb +17 -5
- data/lib/inspec/rspec_json_formatter.rb +48 -14
- data/lib/inspec/rule.rb +4 -0
- data/lib/inspec/runner.rb +72 -30
- data/lib/inspec/shell.rb +24 -19
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +40 -0
- data/lib/resources/aide_conf.rb +162 -0
- data/lib/resources/auditd.rb +231 -0
- data/lib/resources/auditd_rules.rb +3 -1
- data/lib/resources/command.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/docker.rb +8 -1
- data/lib/resources/docker_container.rb +19 -2
- data/lib/resources/elasticsearch.rb +172 -0
- data/lib/resources/etc_fstab.rb +102 -0
- data/lib/resources/etc_group.rb +1 -1
- data/lib/resources/etc_hosts.rb +81 -0
- data/lib/resources/etc_hosts_allow_deny.rb +122 -0
- data/lib/resources/file.rb +87 -10
- data/lib/resources/firewalld.rb +144 -0
- data/lib/resources/groups.rb +0 -1
- data/lib/resources/host.rb +30 -15
- data/lib/resources/http.rb +178 -22
- data/lib/resources/mssql_session.rb +3 -3
- data/lib/resources/mysql_conf.rb +10 -0
- data/lib/resources/mysql_session.rb +1 -1
- data/lib/resources/nginx.rb +97 -0
- data/lib/resources/nginx_conf.rb +134 -1
- data/lib/resources/oracledb_session.rb +9 -8
- data/lib/resources/os_env.rb +7 -4
- data/lib/resources/package.rb +14 -2
- data/lib/resources/packages.rb +1 -1
- data/lib/resources/pip.rb +28 -20
- data/lib/resources/port.rb +251 -3
- data/lib/resources/postgres.rb +1 -1
- data/lib/resources/postgres_session.rb +1 -1
- data/lib/resources/processes.rb +106 -20
- data/lib/resources/registry_key.rb +11 -2
- data/lib/resources/service.rb +5 -24
- data/lib/resources/ssh_conf.rb +1 -1
- data/lib/resources/ssl.rb +4 -3
- data/lib/resources/toml.rb +2 -2
- data/lib/resources/windows_hotfix.rb +35 -0
- data/lib/resources/xinetd.rb +4 -6
- data/lib/resources/xml.rb +27 -0
- data/lib/source_readers/inspec.rb +3 -3
- data/lib/utils/find_files.rb +2 -1
- data/lib/utils/nginx_parser.rb +10 -1
- data/lib/utils/object_traversal.rb +35 -10
- data/lib/utils/parser.rb +21 -3
- metadata +48 -18
- 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
|
data/lib/resources/docker.rb
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
data/lib/resources/etc_group.rb
CHANGED
|
@@ -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
|