inspec 1.42.3 → 1.43.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -22
- data/docs/profiles.md +1 -1
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/shadow.md.erb +20 -6
- data/lib/bundles/inspec-compliance/README.md +13 -22
- data/lib/bundles/inspec-compliance/api.rb +13 -2
- data/lib/bundles/inspec-compliance/api/login.rb +150 -0
- data/lib/bundles/inspec-compliance/cli.rb +43 -157
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/profile.rb +2 -1
- data/lib/inspec/resource.rb +3 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/elasticsearch.rb +172 -0
- metadata +9 -2
@@ -32,7 +32,7 @@ module Compliance
|
|
32
32
|
if config['token'].nil? && config['refresh_token'].nil?
|
33
33
|
if config['server_type'] == 'automate'
|
34
34
|
server = 'automate'
|
35
|
-
msg = 'inspec compliance
|
35
|
+
msg = 'inspec compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN'
|
36
36
|
else
|
37
37
|
server = 'compliance'
|
38
38
|
msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
|
@@ -90,8 +90,7 @@ EOF
|
|
90
90
|
|
91
91
|
raise 'Unable to determine compliance profile name. This can be caused by ' \
|
92
92
|
'an incorrect server in your configuration. Try to login to compliance ' \
|
93
|
-
'via the `inspec compliance login`
|
94
|
-
'commands.' if m.nil?
|
93
|
+
'via the `inspec compliance login` command.' if m.nil?
|
95
94
|
|
96
95
|
"#{m[:owner]}/#{m[:id]}"
|
97
96
|
end
|
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
module Inspec
|
4
4
|
class Control
|
5
|
-
attr_accessor :id, :title, :desc, :impact, :tests, :tags
|
5
|
+
attr_accessor :id, :title, :desc, :impact, :tests, :tags, :refs
|
6
6
|
def initialize
|
7
7
|
@tests = []
|
8
8
|
@tags = []
|
9
|
+
@refs = []
|
9
10
|
end
|
10
11
|
|
11
12
|
def add_test(t)
|
@@ -20,12 +21,13 @@ module Inspec
|
|
20
21
|
{ id: id, title: title, desc: desc, impact: impact, tests: tests.map(&:to_hash), tags: tags.map(&:to_hash) }
|
21
22
|
end
|
22
23
|
|
23
|
-
def to_ruby
|
24
|
+
def to_ruby # rubocop:disable Metrics/AbcSize
|
24
25
|
res = ["control #{id.inspect} do"]
|
25
26
|
res.push " title #{title.inspect}" unless title.to_s.empty?
|
26
27
|
res.push " desc #{prettyprint_text(desc, 2)}" unless desc.to_s.empty?
|
27
28
|
res.push " impact #{impact}" unless impact.nil?
|
28
29
|
tags.each { |t| res.push(indent(t.to_ruby, 2)) }
|
30
|
+
refs.each { |t| res.push(" ref #{print_ref(t)}") }
|
29
31
|
tests.each { |t| res.push(indent(t.to_ruby, 2)) }
|
30
32
|
res.push 'end'
|
31
33
|
res.join("\n")
|
@@ -33,6 +35,12 @@ module Inspec
|
|
33
35
|
|
34
36
|
private
|
35
37
|
|
38
|
+
def print_ref(x)
|
39
|
+
return x.inspect if x.is_a?(String)
|
40
|
+
raise "Cannot process the ref: #{x}" unless x.is_a?(Hash)
|
41
|
+
'('+x.inspect+')'
|
42
|
+
end
|
43
|
+
|
36
44
|
# Pretty-print a text block of InSpec code
|
37
45
|
#
|
38
46
|
# @param s [String] should not be empty
|
data/lib/inspec/profile.rb
CHANGED
@@ -106,7 +106,8 @@ module Inspec
|
|
106
106
|
# we share the backend between profiles.
|
107
107
|
#
|
108
108
|
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
109
|
-
|
109
|
+
train_options = options.select { |k, _| k != 'target' } # See https://github.com/chef/inspec/pull/1646
|
110
|
+
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
110
111
|
@runtime_profile = RuntimeProfile.new(self)
|
111
112
|
@backend.profile = @runtime_profile
|
112
113
|
|
data/lib/inspec/resource.rb
CHANGED
@@ -84,12 +84,15 @@ require 'resources/bash'
|
|
84
84
|
require 'resources/bond'
|
85
85
|
require 'resources/bridge'
|
86
86
|
require 'resources/command'
|
87
|
+
require 'resources/cran'
|
88
|
+
require 'resources/cpan'
|
87
89
|
require 'resources/crontab'
|
88
90
|
require 'resources/dh_params'
|
89
91
|
require 'resources/directory'
|
90
92
|
require 'resources/docker'
|
91
93
|
require 'resources/docker_container'
|
92
94
|
require 'resources/docker_image'
|
95
|
+
require 'resources/elasticsearch'
|
93
96
|
require 'resources/etc_fstab'
|
94
97
|
require 'resources/etc_group'
|
95
98
|
require 'resources/etc_hosts_allow_deny'
|
data/lib/inspec/version.rb
CHANGED
@@ -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
|
@@ -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
|
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: 1.
|
4
|
+
version: 1.43.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train
|
@@ -324,6 +324,8 @@ files:
|
|
324
324
|
- docs/resources/bridge.md.erb
|
325
325
|
- docs/resources/bsd_service.md.erb
|
326
326
|
- docs/resources/command.md.erb
|
327
|
+
- docs/resources/cpan.md.erb
|
328
|
+
- docs/resources/cran.md.erb
|
327
329
|
- docs/resources/crontab.md.erb
|
328
330
|
- docs/resources/csv.md.erb
|
329
331
|
- docs/resources/dh_params.md
|
@@ -331,6 +333,7 @@ files:
|
|
331
333
|
- docs/resources/docker.md.erb
|
332
334
|
- docs/resources/docker_container.md.erb
|
333
335
|
- docs/resources/docker_image.md.erb
|
336
|
+
- docs/resources/elasticsearch.md.erb
|
334
337
|
- docs/resources/etc_fstab.md.erb
|
335
338
|
- docs/resources/etc_group.md.erb
|
336
339
|
- docs/resources/etc_hosts.md.erb
|
@@ -467,6 +470,7 @@ files:
|
|
467
470
|
- lib/bundles/inspec-compliance/.kitchen.yml
|
468
471
|
- lib/bundles/inspec-compliance/README.md
|
469
472
|
- lib/bundles/inspec-compliance/api.rb
|
473
|
+
- lib/bundles/inspec-compliance/api/login.rb
|
470
474
|
- lib/bundles/inspec-compliance/bootstrap.sh
|
471
475
|
- lib/bundles/inspec-compliance/cli.rb
|
472
476
|
- lib/bundles/inspec-compliance/configuration.rb
|
@@ -572,6 +576,8 @@ files:
|
|
572
576
|
- lib/resources/bond.rb
|
573
577
|
- lib/resources/bridge.rb
|
574
578
|
- lib/resources/command.rb
|
579
|
+
- lib/resources/cpan.rb
|
580
|
+
- lib/resources/cran.rb
|
575
581
|
- lib/resources/crontab.rb
|
576
582
|
- lib/resources/csv.rb
|
577
583
|
- lib/resources/dh_params.rb
|
@@ -579,6 +585,7 @@ files:
|
|
579
585
|
- lib/resources/docker.rb
|
580
586
|
- lib/resources/docker_container.rb
|
581
587
|
- lib/resources/docker_image.rb
|
588
|
+
- lib/resources/elasticsearch.rb
|
582
589
|
- lib/resources/etc_fstab.rb
|
583
590
|
- lib/resources/etc_group.rb
|
584
591
|
- lib/resources/etc_hosts.rb
|