inspec 0.14.8 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -2
- data/bin/inspec +3 -4
- data/examples/inheritance/README.md +19 -0
- data/examples/inheritance/controls/example.rb +11 -0
- data/examples/inheritance/inspec.yml +10 -0
- data/lib/bundles/inspec-compliance/cli.rb +1 -4
- data/lib/bundles/inspec-supermarket/cli.rb +1 -4
- data/lib/inspec/dsl.rb +48 -55
- data/lib/inspec/profile.rb +6 -2
- data/lib/inspec/profile_context.rb +21 -8
- data/lib/inspec/runner.rb +17 -12
- data/lib/inspec/runner_rspec.rb +1 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache.rb +20 -18
- data/lib/resources/apache_conf.rb +92 -90
- data/lib/resources/apt.rb +92 -90
- data/lib/resources/audit_policy.rb +35 -33
- data/lib/resources/auditd_conf.rb +41 -39
- data/lib/resources/auditd_rules.rb +155 -153
- data/lib/resources/bond.rb +1 -1
- data/lib/resources/bridge.rb +97 -95
- data/lib/resources/command.rb +47 -45
- data/lib/resources/csv.rb +23 -21
- data/lib/resources/directory.rb +1 -1
- data/lib/resources/etc_group.rb +116 -114
- data/lib/resources/file.rb +1 -1
- data/lib/resources/gem.rb +39 -37
- data/lib/resources/group.rb +100 -98
- data/lib/resources/host.rb +103 -101
- data/lib/resources/inetd_conf.rb +42 -40
- data/lib/resources/ini.rb +15 -13
- data/lib/resources/interface.rb +106 -104
- data/lib/resources/iptables.rb +36 -34
- data/lib/resources/json.rb +64 -62
- data/lib/resources/kernel_module.rb +30 -28
- data/lib/resources/kernel_parameter.rb +44 -42
- data/lib/resources/limits_conf.rb +41 -39
- data/lib/resources/login_def.rb +38 -36
- data/lib/resources/mount.rb +43 -41
- data/lib/resources/mysql.rb +67 -65
- data/lib/resources/mysql_conf.rb +89 -87
- data/lib/resources/mysql_session.rb +46 -44
- data/lib/resources/npm.rb +35 -33
- data/lib/resources/ntp_conf.rb +44 -42
- data/lib/resources/oneget.rb +46 -44
- data/lib/resources/os.rb +22 -20
- data/lib/resources/os_env.rb +47 -45
- data/lib/resources/package.rb +213 -211
- data/lib/resources/parse_config.rb +59 -57
- data/lib/resources/passwd.rb +89 -87
- data/lib/resources/pip.rb +60 -58
- data/lib/resources/port.rb +352 -350
- data/lib/resources/postgres.rb +26 -24
- data/lib/resources/postgres_conf.rb +66 -64
- data/lib/resources/postgres_session.rb +47 -45
- data/lib/resources/processes.rb +56 -54
- data/lib/resources/registry_key.rb +150 -148
- data/lib/resources/script.rb +30 -28
- data/lib/resources/security_policy.rb +56 -54
- data/lib/resources/service.rb +638 -636
- data/lib/resources/shadow.rb +98 -96
- data/lib/resources/ssh_conf.rb +58 -56
- data/lib/resources/user.rb +363 -361
- data/lib/resources/windows_feature.rb +46 -44
- data/lib/resources/xinetd.rb +111 -109
- data/lib/resources/yaml.rb +16 -14
- data/lib/resources/yum.rb +107 -105
- data/lib/utils/base_cli.rb +18 -0
- data/test/helper.rb +2 -2
- data/test/unit/profile_context_test.rb +1 -1
- data/test/unit/resources/file_test.rb +1 -1
- data/test/unit/resources/mount_test.rb +1 -1
- metadata +5 -2
@@ -24,40 +24,42 @@
|
|
24
24
|
#
|
25
25
|
# Further information is available at: https://msdn.microsoft.com/en-us/library/dd973859.aspx
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def method_missing(method)
|
37
|
-
key = method.to_s
|
38
|
-
|
39
|
-
# expected result:
|
40
|
-
# Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
|
41
|
-
# WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
|
42
|
-
result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
|
43
|
-
|
44
|
-
# find line
|
45
|
-
target = nil
|
46
|
-
result.each_line {|s|
|
47
|
-
target = s.strip if s =~ /\b.*#{key}.*\b/
|
48
|
-
}
|
49
|
-
|
50
|
-
# extract value
|
51
|
-
values = nil
|
52
|
-
unless target.nil?
|
53
|
-
# split csv values and return value
|
54
|
-
values = target.split(',')[4]
|
55
|
-
end
|
27
|
+
module Inspec::Resources
|
28
|
+
class AuditPolicy < Inspec.resource(1)
|
29
|
+
name 'audit_policy'
|
30
|
+
desc 'Use the audit_policy InSpec audit resource to test auditing policies on the Microsoft Windows platform. An auditing policy is a category of security-related events to be audited. Auditing is disabled by default and may be enabled for categories like account management, logon events, policy changes, process tracking, privilege use, system events, or object access. For each auditing category property that is enabled, the auditing level may be set to No Auditing, Not Specified, Success, Success and Failure, or Failure.'
|
31
|
+
example "
|
32
|
+
describe audit_policy do
|
33
|
+
its('parameter') { should eq 'value' }
|
34
|
+
end
|
35
|
+
"
|
56
36
|
|
57
|
-
|
58
|
-
|
37
|
+
def method_missing(method)
|
38
|
+
key = method.to_s
|
39
|
+
|
40
|
+
# expected result:
|
41
|
+
# Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
|
42
|
+
# WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
|
43
|
+
result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
|
44
|
+
|
45
|
+
# find line
|
46
|
+
target = nil
|
47
|
+
result.each_line {|s|
|
48
|
+
target = s.strip if s =~ /\b.*#{key}.*\b/
|
49
|
+
}
|
59
50
|
|
60
|
-
|
61
|
-
|
51
|
+
# extract value
|
52
|
+
values = nil
|
53
|
+
unless target.nil?
|
54
|
+
# split csv values and return value
|
55
|
+
values = target.split(',')[4]
|
56
|
+
end
|
57
|
+
|
58
|
+
values
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
'Audit Policy'
|
63
|
+
end
|
62
64
|
end
|
63
65
|
end
|
@@ -6,50 +6,52 @@
|
|
6
6
|
|
7
7
|
require 'utils/simpleconfig'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
module Inspec::Resources
|
10
|
+
class AuditDaemonConf < Inspec.resource(1)
|
11
|
+
name 'auditd_conf'
|
12
|
+
desc "Use the auditd_conf InSpec audit resource to test the configuration settings for the audit daemon. This file is typically located under /etc/audit/auditd.conf' on UNIX and Linux platforms."
|
13
|
+
example "
|
14
|
+
describe auditd_conf do
|
15
|
+
its('space_left_action') { should eq 'email' }
|
16
|
+
end
|
17
|
+
"
|
18
|
+
|
19
|
+
def initialize(path = nil)
|
20
|
+
@conf_path = path || '/etc/audit/auditd.conf'
|
15
21
|
end
|
16
|
-
"
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def method_missing(name)
|
23
|
-
read_params[name.to_s]
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s
|
27
|
-
'Audit Daemon Config'
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def read_params
|
33
|
-
return @params if defined?(@params)
|
34
|
-
|
35
|
-
# read the file
|
36
|
-
file = inspec.file(@conf_path)
|
37
|
-
if !file.file?
|
38
|
-
skip_resource "Can't find file '#{@conf_path}'"
|
39
|
-
return @params = {}
|
23
|
+
def method_missing(name)
|
24
|
+
read_params[name.to_s]
|
40
25
|
end
|
41
26
|
|
42
|
-
|
43
|
-
|
44
|
-
skip_resource "Can't read file '#{@conf_path}'"
|
45
|
-
return @params = {}
|
27
|
+
def to_s
|
28
|
+
'Audit Daemon Config'
|
46
29
|
end
|
47
30
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
31
|
+
private
|
32
|
+
|
33
|
+
def read_params
|
34
|
+
return @params if defined?(@params)
|
35
|
+
|
36
|
+
# read the file
|
37
|
+
file = inspec.file(@conf_path)
|
38
|
+
if !file.file?
|
39
|
+
skip_resource "Can't find file '#{@conf_path}'"
|
40
|
+
return @params = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
content = file.content
|
44
|
+
if content.empty? && file.size > 0
|
45
|
+
skip_resource "Can't read file '#{@conf_path}'"
|
46
|
+
return @params = {}
|
47
|
+
end
|
48
|
+
|
49
|
+
# parse the file
|
50
|
+
conf = SimpleConfig.new(
|
51
|
+
content,
|
52
|
+
multiple_values: false,
|
53
|
+
)
|
54
|
+
@params = conf.params
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
@@ -7,197 +7,199 @@
|
|
7
7
|
require 'forwardable'
|
8
8
|
require 'utils/filter_array'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
module Inspec::Resources
|
11
|
+
class AuditdRulesLegacy
|
12
|
+
def initialize(content)
|
13
|
+
@content = content
|
14
|
+
@opts = {
|
15
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
16
|
+
multiple_values: true,
|
17
|
+
}
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def params
|
21
|
+
@params ||= SimpleConfig.new(@content, @opts).params
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
def method_missing(name)
|
25
|
+
params[name.to_s]
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
def status(name)
|
29
|
+
@status_opts = {
|
30
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
31
|
+
multiple_values: false,
|
32
|
+
}
|
33
|
+
@status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
|
34
|
+
@status_params = SimpleConfig.new(@status_content, @status_opts).params
|
34
35
|
|
35
|
-
|
36
|
-
|
36
|
+
status = @status_params['AUDIT_STATUS']
|
37
|
+
return nil if status.nil?
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
items = Hash[status.scan(/([^=]+)=(\w*)\s*/)]
|
40
|
+
items[name]
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
43
|
+
def to_s
|
44
|
+
'Audit Daemon Rules (for auditd version < 2.3)'
|
45
|
+
end
|
44
46
|
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# rubocop:disable Metrics/ClassLength
|
48
|
-
class AuditDaemonRules < Inspec.resource(1)
|
49
|
-
extend Forwardable
|
50
|
-
attr_accessor :rules, :lines
|
51
47
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
# rubocop:disable Metrics/ClassLength
|
49
|
+
class AuditDaemonRules < Inspec.resource(1)
|
50
|
+
extend Forwardable
|
51
|
+
attr_accessor :rules, :lines
|
52
|
+
|
53
|
+
name 'auditd_rules'
|
54
|
+
desc 'Use the auditd_rules InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files.'
|
55
|
+
example "
|
56
|
+
# syntax for auditd < 2.3
|
57
|
+
describe auditd_rules do
|
58
|
+
its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=adjtimex,settimeofday/) }
|
59
|
+
its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=stime,settimeofday,adjtimex/) }
|
60
|
+
its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=clock_settime/)}
|
61
|
+
its('LIST_RULES') {should contain_match(/^exit,always watch=\/etc\/localtime perm=wa key=time-change/)}
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
# syntax for auditd >= 2.3
|
65
|
+
describe auditd_rules.syscall('open').action do
|
66
|
+
it { should eq(['always']) }
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
describe auditd_rules.key('sshd_config') do
|
70
|
+
its(:permissions) { should contain_match(/x/) }
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
describe auditd_rules do
|
74
|
+
its(:lines) { should contain_match(%r{-w /etc/ssh/sshd_config/}) }
|
75
|
+
end
|
76
|
+
"
|
76
77
|
|
77
|
-
|
78
|
-
|
78
|
+
def initialize
|
79
|
+
@content = inspec.command('/sbin/auditctl -l').stdout.chomp
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
if @content =~ /^LIST_RULES:/
|
82
|
+
# do not warn on centos 5
|
83
|
+
unless inspec.os[:family] == 'centos' && inspec.os[:release].to_i == 5
|
84
|
+
warn '[WARN] this version of auditd is outdated. Updating it allows for using more precise matchers.'
|
85
|
+
end
|
86
|
+
@legacy = AuditdRulesLegacy.new(@content)
|
87
|
+
else
|
88
|
+
parse_content
|
84
89
|
end
|
85
|
-
@legacy = AuditdRulesLegacy.new(@content)
|
86
|
-
else
|
87
|
-
parse_content
|
88
90
|
end
|
89
|
-
end
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
92
|
+
# non-legacy instances are not asked for `its('LIST_RULES')`
|
93
|
+
# rubocop:disable Style/MethodName
|
94
|
+
def LIST_RULES
|
95
|
+
return @legacy.LIST_RULES if @legacy
|
96
|
+
fail 'Using legacy auditd_rules LIST_RULES interface with non-legacy audit package. Please use the new syntax.'
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
-
|
99
|
+
def status(name = nil)
|
100
|
+
return @legacy.status(name) if @legacy
|
100
101
|
|
101
|
-
|
102
|
-
|
102
|
+
@status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
|
103
|
+
@status_params ||= Hash[@status_content.scan(/^([^ ]+) (.*)$/)]
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
return @status_params[name] if name
|
106
|
+
@status_params
|
107
|
+
end
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
109
|
+
def parse_content
|
110
|
+
@rules = {
|
111
|
+
syscalls: [],
|
112
|
+
files: [],
|
113
|
+
}
|
114
|
+
@lines = @content.lines.map(&:chomp)
|
115
|
+
|
116
|
+
lines.each do |line|
|
117
|
+
if is_syscall?(line)
|
118
|
+
syscalls = get_syscalls line
|
119
|
+
action, list = get_action_list line
|
120
|
+
fields, opts = get_fields line
|
121
|
+
|
122
|
+
# create a 'flatter' structure because sanity
|
123
|
+
syscalls.each do |s|
|
124
|
+
@rules[:syscalls] << { syscall: s, list: list, action: action, fields: fields }.merge(opts)
|
125
|
+
end
|
126
|
+
elsif is_file?(line)
|
127
|
+
file = get_file line
|
128
|
+
perms = get_permissions line
|
129
|
+
key = get_key line
|
130
|
+
|
131
|
+
@rules[:files] << { file: file, key: key, permissions: perms }
|
124
132
|
end
|
125
|
-
elsif is_file?(line)
|
126
|
-
file = get_file line
|
127
|
-
perms = get_permissions line
|
128
|
-
key = get_key line
|
129
|
-
|
130
|
-
@rules[:files] << { file: file, key: key, permissions: perms }
|
131
133
|
end
|
132
134
|
end
|
133
|
-
end
|
134
135
|
|
135
|
-
|
136
|
-
|
137
|
-
|
136
|
+
def syscall(name)
|
137
|
+
select_name(:syscall, name)
|
138
|
+
end
|
138
139
|
|
139
|
-
|
140
|
-
|
141
|
-
|
140
|
+
def file(name)
|
141
|
+
select_name(:file, name)
|
142
|
+
end
|
142
143
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
# both files and syscalls have `key` identifiers
|
145
|
+
def key(name)
|
146
|
+
res = rules.values.flatten.find_all { |rule| rule[:key] == name }
|
147
|
+
FilterArray.new(res)
|
148
|
+
end
|
148
149
|
|
149
|
-
|
150
|
-
|
151
|
-
|
150
|
+
def to_s
|
151
|
+
'Audit Daemon Rules'
|
152
|
+
end
|
152
153
|
|
153
|
-
|
154
|
+
private
|
154
155
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
156
|
+
def select_name(key, name)
|
157
|
+
plural = "#{key}s".to_sym
|
158
|
+
res = rules[plural].find_all { |rule| rule[key] == name }
|
159
|
+
FilterArray.new(res)
|
160
|
+
end
|
160
161
|
|
161
|
-
|
162
|
-
|
163
|
-
|
162
|
+
def is_syscall?(line)
|
163
|
+
line.match(/\ -S /)
|
164
|
+
end
|
164
165
|
|
165
|
-
|
166
|
-
|
167
|
-
|
166
|
+
def is_file?(line)
|
167
|
+
line.match(/-w /)
|
168
|
+
end
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
|
170
|
+
def get_syscalls(line)
|
171
|
+
line.scan(/-S ([^ ]+) /).flatten.first.split(',')
|
172
|
+
end
|
172
173
|
|
173
|
-
|
174
|
-
|
175
|
-
|
174
|
+
def get_action_list(line)
|
175
|
+
line.scan(/-a ([^,]+),([^ ]+)/).flatten
|
176
|
+
end
|
176
177
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
178
|
+
# NB only in file lines
|
179
|
+
def get_key(line)
|
180
|
+
line.match(/-k ([^ ]+)/)[1]
|
181
|
+
end
|
181
182
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
183
|
+
# NOTE there are NO precautions wrt. filenames containing spaces in auditctl
|
184
|
+
# `auditctl -w /foo\ bar` gives the following line: `-w /foo bar -p rwxa`
|
185
|
+
def get_file(line)
|
186
|
+
line.match(/-w (.+) -p/)[1]
|
187
|
+
end
|
187
188
|
|
188
|
-
|
189
|
-
|
190
|
-
|
189
|
+
def get_permissions(line)
|
190
|
+
line.match(/-p ([^ ]+)/)[1]
|
191
|
+
end
|
191
192
|
|
192
|
-
|
193
|
-
|
193
|
+
def get_fields(line)
|
194
|
+
fields = line.gsub(/-[aS] [^ ]+ /, '').split('-F ').map { |l| l.split(' ') }.flatten
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
196
|
+
opts = {}
|
197
|
+
fields.find_all { |x| x.match(/[a-z]+=.*/) }.each do |kv|
|
198
|
+
k, v = kv.split('=')
|
199
|
+
opts[k.to_sym] = v
|
200
|
+
end
|
200
201
|
|
201
|
-
|
202
|
+
[fields, opts]
|
203
|
+
end
|
202
204
|
end
|
203
205
|
end
|