inspec-core 4.56.19 → 5.12.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/etc/deprecations.json +12 -16
- data/inspec-core.gemspec +1 -1
- data/lib/inspec/base_cli.rb +14 -2
- data/lib/inspec/cli.rb +15 -0
- data/lib/inspec/dependency_installer.rb +74 -0
- data/lib/inspec/dependency_loader.rb +97 -0
- data/lib/inspec/dsl.rb +11 -2
- data/lib/inspec/errors.rb +7 -0
- data/lib/inspec/formatters/base.rb +23 -0
- data/lib/inspec/metadata.rb +36 -0
- data/lib/inspec/plugin/v2/installer.rb +9 -2
- data/lib/inspec/plugin/v2/loader.rb +13 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
- data/lib/inspec/plugin/v2/status.rb +2 -1
- data/lib/inspec/profile.rb +63 -0
- data/lib/inspec/reporters/automate.rb +1 -1
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters/json.rb +31 -11
- data/lib/inspec/resource.rb +6 -0
- data/lib/inspec/resources/apt.rb +12 -6
- data/lib/inspec/resources/cgroup.rb +101 -0
- data/lib/inspec/resources/cron.rb +49 -0
- data/lib/inspec/resources/docker_container.rb +21 -0
- data/lib/inspec/resources/docker_image.rb +53 -0
- data/lib/inspec/resources/ipfilter.rb +59 -0
- data/lib/inspec/resources/ipnat.rb +58 -0
- data/lib/inspec/resources/lxc.rb +57 -0
- data/lib/inspec/resources/mail_alias.rb +46 -0
- data/lib/inspec/resources/routing_table.rb +137 -0
- data/lib/inspec/resources/service.rb +14 -1
- data/lib/inspec/resources/user.rb +12 -0
- data/lib/inspec/resources/users.rb +79 -14
- data/lib/inspec/resources/virtualization.rb +9 -3
- data/lib/inspec/resources.rb +3 -16
- data/lib/inspec/runner.rb +18 -1
- data/lib/inspec/runner_rspec.rb +15 -0
- data/lib/inspec/schema/exec_json.rb +59 -58
- data/lib/inspec/schema/exec_json_min.rb +16 -16
- data/lib/inspec/schema/primitives.rb +68 -51
- data/lib/inspec/schema/profile_json.rb +27 -27
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/ui.rb +10 -0
- data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec.rb +3 -0
- data/lib/plugins/inspec-artifact/inspec-artifact.gemspec +9 -0
- data/lib/plugins/inspec-compliance/inspec-compliance.gemspec +9 -0
- data/lib/plugins/inspec-habitat/inspec-habitat.gemspec +9 -0
- data/lib/plugins/inspec-init/inspec-init.gemspec +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
- data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
- data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
- data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
- data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
- data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
- data/lib/plugins/inspec-plugin-manager-cli/inspec-plugin-manager-cli.gemspec +10 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +15 -11
- data/lib/plugins/inspec-reporter-html2/inspec-reporter-html2.gemspec +9 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +2 -0
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +3 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -0
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
- data/lib/plugins/inspec-reporter-json-min/inspec-reporter-json-min.gemspec +9 -0
- data/lib/plugins/inspec-reporter-junit/inspec-reporter-junit.gemspec +9 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/inspec-streaming-reporter-progress-bar.gemspec +9 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +123 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
- metadata +33 -3
data/lib/inspec/profile.rb
CHANGED
@@ -13,6 +13,8 @@ require "inspec/dependencies/cache"
|
|
13
13
|
require "inspec/dependencies/lockfile"
|
14
14
|
require "inspec/dependencies/dependency_set"
|
15
15
|
require "inspec/utils/json_profile_summary"
|
16
|
+
require "inspec/dependency_loader"
|
17
|
+
require "inspec/dependency_installer"
|
16
18
|
|
17
19
|
module Inspec
|
18
20
|
class Profile
|
@@ -378,6 +380,66 @@ module Inspec
|
|
378
380
|
@runner_context
|
379
381
|
end
|
380
382
|
|
383
|
+
def collect_gem_dependencies(profile_context)
|
384
|
+
gem_dependencies = []
|
385
|
+
all_profiles = []
|
386
|
+
profile_context.dependencies.list.values.each do |requirement|
|
387
|
+
all_profiles << requirement.profile
|
388
|
+
end
|
389
|
+
all_profiles << self
|
390
|
+
all_profiles.each do |profile|
|
391
|
+
gem_dependencies << profile.metadata.gem_dependencies unless profile.metadata.gem_dependencies.empty?
|
392
|
+
end
|
393
|
+
gem_dependencies.flatten.uniq
|
394
|
+
end
|
395
|
+
|
396
|
+
# Loads the required gems specified in the Profile's metadata file from default inspec gems path i.e. ~/.inspec/gems
|
397
|
+
# else installs and loads them.
|
398
|
+
def load_gem_dependencies
|
399
|
+
gem_dependencies = collect_gem_dependencies(load_libraries)
|
400
|
+
gem_dependencies.each do |gem_data|
|
401
|
+
dependency_loader = DependencyLoader.new
|
402
|
+
if dependency_loader.gem_version_installed?(gem_data[:name], gem_data[:version]) ||
|
403
|
+
dependency_loader.gem_installed?(gem_data[:name])
|
404
|
+
load_gem_dependency(gem_data)
|
405
|
+
else
|
406
|
+
if Inspec::Config.cached[:auto_install_gems]
|
407
|
+
install_gem_dependency(gem_data)
|
408
|
+
load_gem_dependency(gem_data)
|
409
|
+
else
|
410
|
+
ui = Inspec::UI.new
|
411
|
+
gem_dependencies.each { |gem_dependency| ui.list_item("#{gem_dependency[:name]} #{gem_dependency[:version]}") }
|
412
|
+
choice = ui.prompt.select("Would you like to install profile gem dependencies listed above?", %w{Yes No})
|
413
|
+
if choice == "Yes"
|
414
|
+
Inspec::Config.cached[:auto_install_gems] = true
|
415
|
+
load_gem_dependencies
|
416
|
+
else
|
417
|
+
ui.error "Unable to resolve above listed profile gem dependencies."
|
418
|
+
Inspec::UI.new.exit(:gem_dependency_load_error)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Requires gem_data as argument.
|
426
|
+
# gem_dta example: { name: "gem_name", version: "0.0.1"}
|
427
|
+
def load_gem_dependency(gem_data)
|
428
|
+
dependency_loader = DependencyLoader.new(nil, [gem_data])
|
429
|
+
dependency_loader.load
|
430
|
+
rescue Inspec::GemDependencyLoadError => e
|
431
|
+
raise e.message
|
432
|
+
end
|
433
|
+
|
434
|
+
# Requires gem_data as argument.
|
435
|
+
# gem_dta example: { name: "gem_name", version: "0.0.1"}
|
436
|
+
def install_gem_dependency(gem_data)
|
437
|
+
gem_dependency = DependencyInstaller.new(nil, [gem_data])
|
438
|
+
gem_dependency.install
|
439
|
+
rescue Inspec::GemDependencyInstallError => e
|
440
|
+
raise e.message
|
441
|
+
end
|
442
|
+
|
381
443
|
def to_s
|
382
444
|
"Inspec::Profile<#{name}>"
|
383
445
|
end
|
@@ -774,6 +836,7 @@ module Inspec
|
|
774
836
|
end
|
775
837
|
|
776
838
|
def load_checks_params(params)
|
839
|
+
load_gem_dependencies
|
777
840
|
load_libraries
|
778
841
|
tests = collect_tests
|
779
842
|
params[:controls] = controls = {}
|
@@ -21,7 +21,7 @@ module Inspec::Reporters
|
|
21
21
|
final_report[:type] = "inspec_report"
|
22
22
|
|
23
23
|
final_report[:end_time] = Time.now.utc.strftime("%FT%TZ")
|
24
|
-
final_report[:node_uuid] =
|
24
|
+
final_report[:node_uuid] = @config["node_uuid"] || report[:platform][:target_id]
|
25
25
|
raise Inspec::ReporterError, "Cannot find a UUID for your node. Please specify one via json-config." if final_report[:node_uuid].nil?
|
26
26
|
|
27
27
|
final_report[:report_uuid] = @config["report_uuid"] || uuid_from_string(final_report[:end_time] + final_report[:node_uuid])
|
data/lib/inspec/reporters/cli.rb
CHANGED
@@ -76,7 +76,7 @@ module Inspec::Reporters
|
|
76
76
|
}
|
77
77
|
header["Failure Message"] = profile[:status_message] if profile[:status] == "failed"
|
78
78
|
header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil?
|
79
|
-
header["Target ID"] =
|
79
|
+
header["Target ID"] = run_data[:platform][:target_id] || ""
|
80
80
|
|
81
81
|
pad = header.keys.max_by(&:length).length + 1
|
82
82
|
header.each do |title, value|
|
@@ -29,28 +29,48 @@ module Inspec::Reporters
|
|
29
29
|
{
|
30
30
|
name: run_data[:platform][:name],
|
31
31
|
release: run_data[:platform][:release],
|
32
|
-
target_id: run_data[:platform][:target_id] ||
|
32
|
+
target_id: run_data[:platform][:target_id] || "",
|
33
33
|
}.reject { |_k, v| v.nil? }
|
34
34
|
end
|
35
35
|
|
36
36
|
def profile_results(control)
|
37
37
|
(control[:results] || []).map { |r|
|
38
38
|
{
|
39
|
-
status:
|
40
|
-
code_desc:
|
41
|
-
run_time:
|
42
|
-
start_time:
|
43
|
-
resource:
|
44
|
-
skip_message:
|
45
|
-
message:
|
46
|
-
exception:
|
47
|
-
backtrace:
|
48
|
-
resource_class:
|
39
|
+
status: r[:status],
|
40
|
+
code_desc: r[:code_desc],
|
41
|
+
run_time: r[:run_time],
|
42
|
+
start_time: r[:start_time],
|
43
|
+
resource: r[:resource],
|
44
|
+
skip_message: r[:skip_message],
|
45
|
+
message: r[:message],
|
46
|
+
exception: r[:exception],
|
47
|
+
backtrace: r[:backtrace],
|
48
|
+
resource_class: r[:resource_class],
|
49
49
|
resource_params: r[:resource_params].to_s,
|
50
|
+
resource_id: extract_resource_id(r),
|
50
51
|
}.reject { |_k, v| v.nil? }
|
51
52
|
}
|
52
53
|
end
|
53
54
|
|
55
|
+
def extract_resource_id(r)
|
56
|
+
# According to the RunData API, this is supposed to be an anonymous
|
57
|
+
# class that represents a resource, with embedded instance methods....
|
58
|
+
resource_obj = r[:resource_title]
|
59
|
+
return resource_obj.resource_id if resource_obj.respond_to?(:resource_id)
|
60
|
+
|
61
|
+
# But sometimes, it isn't, and has been collapsed into the to_s stringification of the resource.
|
62
|
+
if resource_obj.is_a?(String)
|
63
|
+
orig_str = resource_obj
|
64
|
+
# Try to trim off the resource class - eg "File /some/path" => "/some/path"
|
65
|
+
trimmed_str = orig_str.sub(/^#{r[:resource_class]}/i, "").strip
|
66
|
+
trimmed_str.empty? ? orig_str : trimmed_str
|
67
|
+
else
|
68
|
+
# Boo, InSpec is crazy, and we don't know what it possibly could be.
|
69
|
+
# Failsafe for resource_id is empty string.
|
70
|
+
""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
54
74
|
def profiles
|
55
75
|
run_data[:profiles].map do |p|
|
56
76
|
res = {
|
data/lib/inspec/resource.rb
CHANGED
@@ -34,6 +34,12 @@ module Inspec
|
|
34
34
|
Inspec::Resource.support_registry[key].push(criteria)
|
35
35
|
end
|
36
36
|
|
37
|
+
def resource_id(value = nil)
|
38
|
+
@resource_id = value if value
|
39
|
+
@resource_id = "" if @resource_id.nil?
|
40
|
+
@resource_id
|
41
|
+
end
|
42
|
+
|
37
43
|
# TODO: this is pretty terrible and is only here to work around
|
38
44
|
# the idea that we've trained resource authors to make initialize
|
39
45
|
# methods w/o calling super.
|
data/lib/inspec/resources/apt.rb
CHANGED
@@ -135,19 +135,25 @@ module Inspec::Resources
|
|
135
135
|
|
136
136
|
class PpaRepository < AptRepository
|
137
137
|
name "ppa"
|
138
|
+
desc "Use the ppa InSpec audit resource to verify PPA repositories on the Debian-based linux platforms."
|
139
|
+
example <<~EXAMPLE
|
140
|
+
describe ppa('ubuntu-wine/ppa') do
|
141
|
+
it { should exist }
|
142
|
+
it { should be_enabled }
|
143
|
+
end
|
144
|
+
|
145
|
+
describe ppa('ppa:ubuntu-wine/ppa') do
|
146
|
+
it { should exist }
|
147
|
+
it { should be_enabled }
|
148
|
+
end
|
149
|
+
EXAMPLE
|
138
150
|
|
139
151
|
def exists?
|
140
|
-
deprecated
|
141
152
|
super()
|
142
153
|
end
|
143
154
|
|
144
155
|
def enabled?
|
145
|
-
deprecated
|
146
156
|
super()
|
147
157
|
end
|
148
|
-
|
149
|
-
def deprecated
|
150
|
-
Inspec.deprecate(:resource_ppa, "The `ppa` resource is deprecated. Please use `apt`")
|
151
|
-
end
|
152
158
|
end
|
153
159
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class Cgroup < Inspec.resource(1)
|
4
|
+
name "cgroup"
|
5
|
+
# Restrict to only run on the below platform
|
6
|
+
supports platform: "linux"
|
7
|
+
desc "Use the cgroup InSpec audit resource to test cgroup subsytem's parameters."
|
8
|
+
|
9
|
+
example <<~EXAMPLE
|
10
|
+
describe cgroup("foo") do
|
11
|
+
its("cpuset.cpus") { should eq 0 }
|
12
|
+
its("memory.limit_in_bytes") { should eq 499712 }
|
13
|
+
its("memory.limit_in_bytes") { should be <= 500000 }
|
14
|
+
its("memory.numa_stat") { should match /total=0/ }
|
15
|
+
end
|
16
|
+
EXAMPLE
|
17
|
+
|
18
|
+
# Resource initialization.
|
19
|
+
def initialize(cgroup_name)
|
20
|
+
raise Inspec::Exceptions::ResourceSkipped, "The `cgroup` resource is not supported on your OS yet." unless inspec.os.linux?
|
21
|
+
|
22
|
+
@cgroup_name = cgroup_name
|
23
|
+
@valid_queries, @valid_queries_split = [], []
|
24
|
+
find_valid_queries
|
25
|
+
# Used to track the method calls in an "its" query
|
26
|
+
@cgroup_info_query = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def resource_id
|
30
|
+
@cgroup_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"cgroup #{resource_id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(param)
|
38
|
+
# Add the latest param we've seen to the list and form the query with all the params we've seen so far.
|
39
|
+
@cgroup_info_query << param.to_s
|
40
|
+
query = @cgroup_info_query.join(".")
|
41
|
+
|
42
|
+
# The ith level param must match with atleast one row's ith column of @valid_queries_split
|
43
|
+
# Else there is no way, we would find any valid query in further iteration, so raise exception.
|
44
|
+
if @valid_queries_split.map { |e| e[@cgroup_info_query.length - 1] }.include?(param.to_s)
|
45
|
+
# If the query form so far is part of @valid_queries, we are good to trigger find_cgroup_info
|
46
|
+
# else go for next level of param
|
47
|
+
if @valid_queries.include?(query)
|
48
|
+
@cgroup_info_query = []
|
49
|
+
find_cgroup_info(query)
|
50
|
+
else
|
51
|
+
self
|
52
|
+
end
|
53
|
+
else
|
54
|
+
@cgroup_info_query = []
|
55
|
+
|
56
|
+
raise Inspec::Exceptions::ResourceFailed, "The query #{query} does not appear to be valid."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Method to find cgget tool
|
63
|
+
def find_cgget_or_error
|
64
|
+
%w{/usr/sbin/cgget /sbin/cgget cgget}.each do |cmd|
|
65
|
+
return cmd if inspec.command(cmd).exist?
|
66
|
+
end
|
67
|
+
|
68
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `cgget`"
|
69
|
+
end
|
70
|
+
|
71
|
+
# find the cgroup info of the query which is given as input by the user
|
72
|
+
def find_cgroup_info(query)
|
73
|
+
bin = find_cgget_or_error
|
74
|
+
cgget_cmd = "#{bin} -n -r #{query} #{@cgroup_name}"
|
75
|
+
cmd = inspec.command(cgget_cmd)
|
76
|
+
|
77
|
+
raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
|
78
|
+
|
79
|
+
# For complex returns the user must use match /the_regex/
|
80
|
+
param_value = cmd.stdout.split(":")
|
81
|
+
return nil if param_value.nil? || param_value.empty?
|
82
|
+
|
83
|
+
param_value = param_value[1].strip.split("\t").join
|
84
|
+
param_value.match(/^\d+$/) ? param_value.to_i : param_value
|
85
|
+
end
|
86
|
+
|
87
|
+
# find all the information about all relevant controllers for the current cgroup
|
88
|
+
def find_valid_queries
|
89
|
+
bin = find_cgget_or_error
|
90
|
+
cgget_all_cmd = "#{bin} -n -a #{@cgroup_name}"
|
91
|
+
cmd = inspec.command(cgget_all_cmd)
|
92
|
+
|
93
|
+
raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
|
94
|
+
|
95
|
+
queries = cmd.stdout.to_s.gsub(/:.*/, "").gsub(/^\s+.*/, "").split("\n")
|
96
|
+
# store the relevant controller parameters in @valid_queries and the dot splitted paramters into @valid_queries_split
|
97
|
+
@valid_queries = queries.map { |q| q if q.length > 0 }.compact
|
98
|
+
@valid_queries_split = @valid_queries.map { |q| q.split(".") }.compact
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "inspec/resources/crontab"
|
2
|
+
|
3
|
+
module Inspec::Resources
|
4
|
+
class Cron < Crontab
|
5
|
+
name "cron"
|
6
|
+
supports platform: "unix"
|
7
|
+
desc "Use the cron InSpec audit resource to test entires in the crontab file for a given user. This also can be used as alias to crontab resource."
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe cron do
|
10
|
+
it { should have_entry '* * * * * /usr/local/bin/foo' }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe cron(user: "username") do
|
14
|
+
its(:table) { should match /you can use regexp/ }
|
15
|
+
end
|
16
|
+
EXAMPLE
|
17
|
+
|
18
|
+
def initialize(opts = nil)
|
19
|
+
super
|
20
|
+
@params = read_cron_contents
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_cron_contents
|
24
|
+
result = inspec.command(crontab_cmd)
|
25
|
+
if result.exit_status == 0
|
26
|
+
result.stdout.lines.map { |l| parse_comment_line(l, comment_char: "#", standalone_comments: false)[0].strip }
|
27
|
+
else
|
28
|
+
error = result.stdout + "\n" + result.stderr
|
29
|
+
raise Inspec::Exceptions::ResourceFailed, "Error while executing #{crontab_cmd} command: #{error}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def table
|
34
|
+
@params.reject(&:empty?).join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_entry?(rule)
|
38
|
+
@params.include?(rule)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
if is_user_crontab?
|
43
|
+
"cron for user #{@user}"
|
44
|
+
else
|
45
|
+
"cron for current user"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -43,6 +43,19 @@ module Inspec::Resources
|
|
43
43
|
status.downcase.start_with?("up") if object_info.entries.length == 1
|
44
44
|
end
|
45
45
|
|
46
|
+
# has_volume? matcher checks if the volume specified in source path of host is mounted in destination path of docker
|
47
|
+
def has_volume?(destination, source)
|
48
|
+
# volume_info is the hash which contains the low-level information about the container
|
49
|
+
# if Mounts key is not present or is nil; raise exception
|
50
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find any mounted volumes for your container" unless volume_info.Mounts[0]
|
51
|
+
|
52
|
+
# Iterate through the list of mounted volumes and check if it matches with the given destination and source
|
53
|
+
# is_mounted flag is used to handle to return explict boolean values of true or false
|
54
|
+
is_mounted = false
|
55
|
+
volume_info.Mounts.detect { |mount| is_mounted = mount.Destination == destination && mount.Source == source }
|
56
|
+
is_mounted
|
57
|
+
end
|
58
|
+
|
46
59
|
def status
|
47
60
|
object_info.status[0] if object_info.entries.length == 1
|
48
61
|
end
|
@@ -87,5 +100,13 @@ module Inspec::Resources
|
|
87
100
|
opts = @opts
|
88
101
|
@info = inspec.docker.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
|
89
102
|
end
|
103
|
+
|
104
|
+
# volume_info returns the low-level information obtained on docker inspect [container_name/id]
|
105
|
+
def volume_info
|
106
|
+
return @mount_info if defined?(@mount_info)
|
107
|
+
|
108
|
+
# Check for either docker inspect [container_name] or docker inspect [container_id]
|
109
|
+
@mount_info = inspec.docker.object(@opts[:name] || @opts[:id])
|
110
|
+
end
|
90
111
|
end
|
91
112
|
end
|
@@ -48,6 +48,25 @@ module Inspec::Resources
|
|
48
48
|
object_info.tags[0] if object_info.entries.size == 1
|
49
49
|
end
|
50
50
|
|
51
|
+
# method_missing handles when hash_keys are invoked to check information obtained on docker inspect [image_name]
|
52
|
+
def method_missing(*hash_keys)
|
53
|
+
# User can test the low-level inspect information in three ways:
|
54
|
+
# Way 1: Serverspec style: its(['Config.Cmd']) { should include some_value }
|
55
|
+
# here, the value for hash_keys recieved is [:[], "Config.Cmd"]
|
56
|
+
# Way 2: InSpec style: its(['Config','Cmd']) { should include some_value }
|
57
|
+
# here, the value for hash_keys recieved is [:[], "Config", "Cmd"]
|
58
|
+
# Way 3: Mix of both: its(['GraphDriver.Data','MergedDir']) { should include some_value }
|
59
|
+
# here, the value for hash_keys recieved is [:[], "GraphDriver.Data", "MergedDir"]
|
60
|
+
|
61
|
+
# hash_keys are passed to this method to evaluate the value
|
62
|
+
image_hash_inspection(hash_keys)
|
63
|
+
end
|
64
|
+
|
65
|
+
# inspection property allows to test any of the hash key-value pairs as part of the image_inspect_info
|
66
|
+
def inspection
|
67
|
+
image_inspect_info
|
68
|
+
end
|
69
|
+
|
51
70
|
def to_s
|
52
71
|
img = @opts[:image] || @opts[:id]
|
53
72
|
"Docker Image #{img}"
|
@@ -80,5 +99,39 @@ module Inspec::Resources
|
|
80
99
|
(repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
|
81
100
|
end
|
82
101
|
end
|
102
|
+
|
103
|
+
# image_inspect_info returns the complete inspect hash_values of the image
|
104
|
+
def image_inspect_info
|
105
|
+
return @inspect_info if defined?(@inspect_info)
|
106
|
+
|
107
|
+
@inspect_info = inspec.docker.object(@opts[:image] || (!@opts[:id].nil? && @opts[:id]))
|
108
|
+
end
|
109
|
+
|
110
|
+
# image_hash_inspection formats the input hash_keys and checks if any value exists for such keys in @inspect_info(image_inspect_info)
|
111
|
+
def image_hash_inspection(hash_keys)
|
112
|
+
# The hash_keys recieved are in three formats as mentioned in method_missing
|
113
|
+
# The hash_keys recieved must be in array format [] and the zeroth index must be :[]
|
114
|
+
# Check for the conditions and remove the zeroth element from the hash_keys
|
115
|
+
|
116
|
+
hash_keys.shift if hash_keys.is_a?(Array) && hash_keys[0] == :[]
|
117
|
+
|
118
|
+
# When received hash_keys in Serverspec style or mix of both
|
119
|
+
# The hash_keys are to be splitted at '.' (dot) and flatten it so that it doesn't become array of arrays
|
120
|
+
# After splitting and flattening is done, hash_keys is now an array with individual keys
|
121
|
+
hash_keys = hash_keys.map { |key| key.split(".") }.flatten
|
122
|
+
|
123
|
+
# image_inspect_info returns the complete inspect hash_values of the image
|
124
|
+
# dig() finds the nested value specified by the sequence of the key object by calling dig at each step.
|
125
|
+
# hash_keys is the key object. If one of the key is bad, value will be nil.
|
126
|
+
hash_value = image_inspect_info.dig(*hash_keys)
|
127
|
+
|
128
|
+
# If one of the key is bad, hash_value will be nil, so raise exception which throws it in rescue block
|
129
|
+
# else return hash_value
|
130
|
+
raise Inspec::Exceptions::ResourceFailed if hash_value.nil?
|
131
|
+
|
132
|
+
hash_value
|
133
|
+
rescue
|
134
|
+
raise Inspec::Exceptions::ResourceFailed, "#{hash_keys.join(".")} is not a valid key for your image or has nil value."
|
135
|
+
end
|
83
136
|
end
|
84
137
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class IpFilter < Inspec.resource(1)
|
4
|
+
name "ipfilter"
|
5
|
+
supports platform: "bsd"
|
6
|
+
supports platform: "solaris"
|
7
|
+
desc "Use the ipfilter InSpec audit resource to test rules that are defined for ipfilter, which maintains the IP rule set"
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe ipfilter do
|
10
|
+
it { should have_rule("pass in quick on lo0 all") }
|
11
|
+
end
|
12
|
+
EXAMPLE
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
# checks if the instance is either bsd or solaris
|
16
|
+
return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
|
17
|
+
|
18
|
+
# ensures, all calls are aborted for non-supported os
|
19
|
+
@ipfilter_cache = []
|
20
|
+
skip_resource "The `ipfilter` resource is not supported on your OS yet."
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_rule?(rule = nil)
|
24
|
+
# checks if the rule is part of the ruleset
|
25
|
+
retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
|
26
|
+
end
|
27
|
+
|
28
|
+
def retrieve_rules
|
29
|
+
# this would be true if the OS family was not bsd/solaris when checked in initliaze
|
30
|
+
return @ipfilter_cache if defined?(@ipfilter_cache)
|
31
|
+
|
32
|
+
# construct ipfstat command to read all rules
|
33
|
+
bin = find_ipfstat_or_error
|
34
|
+
ipfstat_cmd = "#{bin} -io"
|
35
|
+
cmd = inspec.command(ipfstat_cmd)
|
36
|
+
|
37
|
+
# Return empty array when command is not executed successfully
|
38
|
+
# or there is no output since no rules are active
|
39
|
+
return [] if cmd.exit_status.to_i != 0 || cmd.stdout == ""
|
40
|
+
|
41
|
+
# split rules, returns array or rules
|
42
|
+
@ipfilter_cache = cmd.stdout.split("\n").map(&:strip)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"Ipfilter"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def find_ipfstat_or_error
|
52
|
+
%w{/usr/sbin/ipfstat /sbin/ipfstat ipfstat}.each do |cmd|
|
53
|
+
return cmd if inspec.command(cmd).exist?
|
54
|
+
end
|
55
|
+
|
56
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `ipfstat`"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class IpNat < Inspec.resource(1)
|
4
|
+
name "ipnat"
|
5
|
+
supports platform: "bsd"
|
6
|
+
supports platform: "solaris"
|
7
|
+
desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT"
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe ipnat do
|
10
|
+
it { should have_rule("map net1 192.168.0.0/24 -> 0/32") }
|
11
|
+
end
|
12
|
+
EXAMPLE
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
# checks if the instance is either bsd or solaris
|
16
|
+
return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
|
17
|
+
|
18
|
+
# ensures, all calls are aborted for non-supported os
|
19
|
+
@ipnat_cache = []
|
20
|
+
skip_resource "The `ipnat` resource is not supported on your OS yet."
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_rule?(rule = nil)
|
24
|
+
# checks if the rule is part of the ruleset
|
25
|
+
retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
|
26
|
+
end
|
27
|
+
|
28
|
+
def retrieve_rules
|
29
|
+
# this would be true if the OS family was not bsd/solaris when checked in initliaze
|
30
|
+
return @ipnat_cache if defined?(@ipnat_cache)
|
31
|
+
|
32
|
+
# construct ipnat command to show the list of current IP NAT table entry mappings
|
33
|
+
bin = find_ipnat_or_error
|
34
|
+
ipnat_cmd = "#{bin} -l"
|
35
|
+
cmd = inspec.command(ipnat_cmd)
|
36
|
+
|
37
|
+
# Return empty array when command is not executed successfully
|
38
|
+
return [] if cmd.exit_status.to_i != 0
|
39
|
+
|
40
|
+
# split rules, returns array or rules
|
41
|
+
@ipnat_cache = cmd.stdout.split("\n").map(&:strip)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"Ipnat"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def find_ipnat_or_error
|
51
|
+
%w{/usr/sbin/ipnat /sbin/ipnat ipnat}.each do |cmd|
|
52
|
+
return cmd if inspec.command(cmd).exist?
|
53
|
+
end
|
54
|
+
|
55
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `ipnat`"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class Lxc < Inspec.resource(1)
|
4
|
+
name "lxc"
|
5
|
+
# Restrict to only run on the below platforms
|
6
|
+
supports platform: "linux"
|
7
|
+
desc "Use the lxc InSpec audit resource to test if container exists and/or is running for linux container"
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe lxc("ubuntu-container") do
|
10
|
+
it { should exist }
|
11
|
+
it { should be_running }
|
12
|
+
end
|
13
|
+
EXAMPLE
|
14
|
+
|
15
|
+
# Resource initialization.
|
16
|
+
def initialize(container_name)
|
17
|
+
@container_name = container_name
|
18
|
+
|
19
|
+
raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux?
|
20
|
+
end
|
21
|
+
|
22
|
+
def resource_id
|
23
|
+
@container_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"lxc #{resource_id}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def exists?
|
31
|
+
lxc_info_cmd.exit_status.to_i == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def running?
|
35
|
+
container_info = lxc_info_cmd.stdout.split(":").map(&:strip)
|
36
|
+
container_info[0] == "Status" && container_info[1] == "Running"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Method to find lxc
|
42
|
+
def find_lxc_or_error
|
43
|
+
%w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd|
|
44
|
+
return cmd if inspec.command(cmd).exist?
|
45
|
+
end
|
46
|
+
|
47
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`"
|
48
|
+
end
|
49
|
+
|
50
|
+
def lxc_info_cmd
|
51
|
+
bin = find_lxc_or_error
|
52
|
+
info_cmd = "info #{@container_name} | grep -i Status"
|
53
|
+
lxc_cmd = format("%s %s", bin, info_cmd).strip
|
54
|
+
inspec.command(lxc_cmd)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class Mailalias < Inspec.resource(1)
|
4
|
+
# resource internal name.
|
5
|
+
name "mail_alias"
|
6
|
+
|
7
|
+
# Restrict to only run on the below platforms (if none were given,
|
8
|
+
# all OS's and cloud API's supported)
|
9
|
+
supports platform: "unix"
|
10
|
+
|
11
|
+
desc "Use the mail_alias InSpec audit resource to test mail alias present in the aliases file"
|
12
|
+
|
13
|
+
example <<~EXAMPLE
|
14
|
+
describe mail_alias("toor") do
|
15
|
+
it { should be_aliased_to "root" }
|
16
|
+
end
|
17
|
+
EXAMPLE
|
18
|
+
|
19
|
+
def initialize(alias_key)
|
20
|
+
skip_resource "The `mail_alias` resource is not yet available on your OS." unless inspec.os.unix?
|
21
|
+
@alias_key = alias_key
|
22
|
+
end
|
23
|
+
|
24
|
+
# resource_id is used in reporting engines to uniquely identify the individual resource.
|
25
|
+
def resource_id
|
26
|
+
"#{@alias_key}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# resource appearance in test reports.
|
30
|
+
def to_s
|
31
|
+
"mail_alias #{resource_id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# aliased_to matcher checks if the given alias_value is set to the initialized alias_key
|
35
|
+
def aliased_to?(alias_value)
|
36
|
+
# /etc/aliases if the file where the alias and its value(s) are stored
|
37
|
+
cmd = inspec.command("cat /etc/aliases | grep '^#{@alias_key}:'")
|
38
|
+
raise Inspec::Exceptions::ResourceFailed, "#{@alias_key} is not a valid key in the aliases" if cmd.exit_status.to_i != 0
|
39
|
+
|
40
|
+
# in general aliases file contains : separated values like alias_key : alias_value1, alias_value2
|
41
|
+
alias_values_combined = cmd.stdout.split(":").map(&:strip)[1]
|
42
|
+
alias_values_splitted = alias_values_combined.split(",").map(&:strip)
|
43
|
+
alias_values_splitted.include?(alias_value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|