inspec 0.14.8 → 0.15.0
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/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
data/lib/resources/script.rb
CHANGED
@@ -4,38 +4,40 @@
|
|
4
4
|
# author: Dominik Richter
|
5
5
|
# license: All rights reserved
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
module Inspec::Resources
|
8
|
+
class Script < Cmd
|
9
|
+
name 'script'
|
10
|
+
desc 'Use the script InSpec audit resource to test a Windows PowerShell script on the Microsoft Windows platform.'
|
11
|
+
example "
|
12
|
+
script = <<-EOH
|
13
|
+
# you powershell script
|
14
|
+
EOH
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
describe script(script) do
|
17
|
+
its('matcher') { should eq 'output' }
|
18
|
+
end
|
19
|
+
"
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def initialize(script)
|
22
|
+
unless inspec.os.windows?
|
23
|
+
return skip_resource 'The `script` resource is not supported on your OS yet.'
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
# encodes a script as base64 to run as powershell encodedCommand
|
27
|
+
# this comes with performance issues: @see https://gist.github.com/fnichol/7b20596b950e65fb96f9
|
28
|
+
require 'winrm'
|
29
|
+
script = WinRM::PowershellScript.new(script)
|
30
|
+
cmd = "powershell -encodedCommand #{script.encoded}"
|
31
|
+
super(cmd)
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
# we cannot determine if a command exists, because that does not work for scripts
|
35
|
+
def exist?
|
36
|
+
nil
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
39
|
+
def to_s
|
40
|
+
'Script'
|
41
|
+
end
|
40
42
|
end
|
41
43
|
end
|
@@ -13,70 +13,72 @@
|
|
13
13
|
# All local GPO parameters can be examined via Registry, but not all security
|
14
14
|
# parameters. Therefore we need a combination of Registry and secedit output
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
module Inspec::Resources
|
17
|
+
class SecurityPolicy < Inspec.resource(1)
|
18
|
+
name 'security_policy'
|
19
|
+
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
20
|
+
example "
|
21
|
+
describe security_policy do
|
22
|
+
its('SeNetworkLogonRight') { should eq '*S-1-5-11' }
|
23
|
+
end
|
24
|
+
"
|
25
|
+
def initialize
|
26
|
+
@loaded = false
|
27
|
+
@policy = nil
|
28
|
+
@exit_status = nil
|
22
29
|
end
|
23
|
-
"
|
24
|
-
def initialize
|
25
|
-
@loaded = false
|
26
|
-
@policy = nil
|
27
|
-
@exit_status = nil
|
28
|
-
end
|
29
|
-
|
30
|
-
# load security content
|
31
|
-
def load
|
32
|
-
# export the security policy
|
33
|
-
cmd = inspec.command('secedit /export /cfg win_secpol.cfg')
|
34
|
-
return nil if cmd.exit_status.to_i != 0
|
35
30
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@loaded = true
|
31
|
+
# load security content
|
32
|
+
def load
|
33
|
+
# export the security policy
|
34
|
+
cmd = inspec.command('secedit /export /cfg win_secpol.cfg')
|
35
|
+
return nil if cmd.exit_status.to_i != 0
|
42
36
|
|
43
|
-
|
44
|
-
|
37
|
+
# store file content
|
38
|
+
cmd = inspec.command('Get-Content win_secpol.cfg')
|
39
|
+
@exit_status = cmd.exit_status.to_i
|
40
|
+
return nil if @exit_status != 0
|
41
|
+
@policy = cmd.stdout
|
42
|
+
@loaded = true
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
inspec.command('Remove-Item win_secpol.cfg').exit_status.to_i
|
49
|
-
end
|
44
|
+
# returns self
|
45
|
+
self
|
50
46
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
load
|
47
|
+
ensure
|
48
|
+
# delete temp file
|
49
|
+
inspec.command('Remove-Item win_secpol.cfg').exit_status.to_i
|
55
50
|
end
|
56
51
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
}
|
52
|
+
def method_missing(method)
|
53
|
+
# load data if needed
|
54
|
+
if @loaded == false
|
55
|
+
load
|
56
|
+
end
|
63
57
|
|
64
|
-
|
65
|
-
|
58
|
+
# find line with key
|
59
|
+
key = Regexp.escape(method.to_s)
|
60
|
+
target = ''
|
61
|
+
@policy.each_line {|s|
|
62
|
+
target = s.strip if s =~ /^\s*#{key}\s*=\s*(.*)\b/
|
63
|
+
}
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
val = val.to_i if val =~ /^\d+$/
|
70
|
-
else
|
71
|
-
# TODO: we may need to return skip or failure if the
|
72
|
-
# requested value is not available
|
73
|
-
val = nil
|
74
|
-
end
|
65
|
+
# extract variable value
|
66
|
+
result = target.match(/[=]{1}\s*(?<value>.*)/)
|
75
67
|
|
76
|
-
|
77
|
-
|
68
|
+
if !result.nil?
|
69
|
+
val = result[:value]
|
70
|
+
val = val.to_i if val =~ /^\d+$/
|
71
|
+
else
|
72
|
+
# TODO: we may need to return skip or failure if the
|
73
|
+
# requested value is not available
|
74
|
+
val = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
val
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
-
|
80
|
+
def to_s
|
81
|
+
'Security Policy'
|
82
|
+
end
|
81
83
|
end
|
82
84
|
end
|
data/lib/resources/service.rb
CHANGED
@@ -4,728 +4,730 @@
|
|
4
4
|
# author: Stephan Renatus
|
5
5
|
# license: All rights reserved
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def initialize(owner, default = false)
|
33
|
-
@owner = owner
|
34
|
-
super(default)
|
35
|
-
end
|
7
|
+
module Inspec::Resources
|
8
|
+
class Runlevels < Hash
|
9
|
+
attr_accessor :owner
|
10
|
+
|
11
|
+
def self.from_hash(owner, hash = {}, filter = nil)
|
12
|
+
res = Runlevels.new(owner)
|
13
|
+
filter = filter.first if filter.is_a?(Array) && filter.length <= 1
|
14
|
+
|
15
|
+
ks = case filter
|
16
|
+
when nil
|
17
|
+
hash.keys
|
18
|
+
when Regexp
|
19
|
+
hash.keys.find_all { |x| x.to_s =~ filter }
|
20
|
+
when Array
|
21
|
+
f = filter.map(&:to_s)
|
22
|
+
hash.keys.find_all { |x| f.include?(x.to_s) }
|
23
|
+
when Numeric
|
24
|
+
hash.keys.include?(filter) ? [filter] : []
|
25
|
+
else
|
26
|
+
hash.keys.find_all { |x| x == filter }
|
27
|
+
end
|
28
|
+
|
29
|
+
ks.each { |k| res[k] = hash[k] }
|
30
|
+
res
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
def initialize(owner, default = false)
|
34
|
+
@owner = owner
|
35
|
+
super(default)
|
36
|
+
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def enabled?
|
45
|
-
values.all?
|
46
|
-
end
|
38
|
+
def filter(f)
|
39
|
+
Runlevels.from_hash(owner, self, f)
|
40
|
+
end
|
47
41
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
# Check if all runlevels are enabled
|
43
|
+
#
|
44
|
+
# @return [boolean] true if all runlevels are enabled
|
45
|
+
def enabled?
|
46
|
+
values.all?
|
47
|
+
end
|
54
48
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
# Check if all runlevels are disabled
|
50
|
+
#
|
51
|
+
# @return [boolean] true if all runlevels are disabled
|
52
|
+
def disabled?
|
53
|
+
!values.any?
|
54
|
+
end
|
59
55
|
|
60
|
-
|
61
|
-
#
|
62
|
-
|
63
|
-
# Fedora 15 : systemd
|
64
|
-
# RedHat 7 : systemd
|
65
|
-
# Ubuntu 15.04 : systemd
|
66
|
-
# Ubuntu < 15.04 : upstart
|
67
|
-
#
|
68
|
-
# TODO: extend the logic to detect the running init system, independently of OS
|
69
|
-
class Service < Inspec.resource(1)
|
70
|
-
name 'service'
|
71
|
-
desc 'Use the service InSpec audit resource to test if the named service is installed, running and/or enabled.'
|
72
|
-
example "
|
73
|
-
describe service('service_name') do
|
74
|
-
it { should be_installed }
|
75
|
-
it { should be_enabled }
|
76
|
-
it { should be_running }
|
77
|
-
end
|
78
|
-
|
79
|
-
describe service('service_name').runlevels(3, 5) do
|
80
|
-
it { should be_enabled }
|
81
|
-
end
|
82
|
-
"
|
83
|
-
|
84
|
-
attr_reader :service_ctl
|
85
|
-
|
86
|
-
def initialize(service_name, service_ctl = nil)
|
87
|
-
@service_name = service_name
|
88
|
-
@service_mgmt = nil
|
89
|
-
@service_ctl ||= service_ctl
|
90
|
-
@cache = nil
|
91
|
-
@service_mgmt = select_service_mgmt
|
92
|
-
|
93
|
-
return skip_resource 'The `service` resource is not supported on your OS yet.' if @service_mgmt.nil?
|
56
|
+
def to_s
|
57
|
+
"#{owner} runlevels #{keys.join(', ')}"
|
58
|
+
end
|
94
59
|
end
|
95
60
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
Systemd.new(inspec, service_ctl)
|
61
|
+
# We detect the init system for each operating system, based on the operating
|
62
|
+
# system.
|
63
|
+
#
|
64
|
+
# Fedora 15 : systemd
|
65
|
+
# RedHat 7 : systemd
|
66
|
+
# Ubuntu 15.04 : systemd
|
67
|
+
# Ubuntu < 15.04 : upstart
|
68
|
+
#
|
69
|
+
# TODO: extend the logic to detect the running init system, independently of OS
|
70
|
+
class Service < Inspec.resource(1)
|
71
|
+
name 'service'
|
72
|
+
desc 'Use the service InSpec audit resource to test if the named service is installed, running and/or enabled.'
|
73
|
+
example "
|
74
|
+
describe service('service_name') do
|
75
|
+
it { should be_installed }
|
76
|
+
it { should be_enabled }
|
77
|
+
it { should be_running }
|
114
78
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
Systemd.new(inspec, service_ctl)
|
119
|
-
else
|
120
|
-
SysV.new(inspec, service_ctl || '/usr/sbin/service')
|
79
|
+
|
80
|
+
describe service('service_name').runlevels(3, 5) do
|
81
|
+
it { should be_enabled }
|
121
82
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
83
|
+
"
|
84
|
+
|
85
|
+
attr_reader :service_ctl
|
86
|
+
|
87
|
+
def initialize(service_name, service_ctl = nil)
|
88
|
+
@service_name = service_name
|
89
|
+
@service_mgmt = nil
|
90
|
+
@service_ctl ||= service_ctl
|
91
|
+
@cache = nil
|
92
|
+
@service_mgmt = select_service_mgmt
|
93
|
+
|
94
|
+
return skip_resource 'The `service` resource is not supported on your OS yet.' if @service_mgmt.nil?
|
95
|
+
end
|
96
|
+
|
97
|
+
def select_service_mgmt # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
98
|
+
os = inspec.os
|
99
|
+
family = os[:family]
|
100
|
+
|
101
|
+
# Ubuntu
|
102
|
+
# @see: https://wiki.ubuntu.com/SystemdForUpstartUsers
|
103
|
+
# Ubuntu 15.04 : Systemd
|
104
|
+
# Systemd runs with PID 1 as /sbin/init.
|
105
|
+
# Upstart runs with PID 1 as /sbin/upstart.
|
106
|
+
# Ubuntu < 15.04 : Upstart
|
107
|
+
# Upstart runs with PID 1 as /sbin/init.
|
108
|
+
# Systemd runs with PID 1 as /lib/systemd/systemd.
|
109
|
+
if %w{ubuntu}.include?(family)
|
110
|
+
version = inspec.os[:release].to_f
|
111
|
+
if version < 15.04
|
112
|
+
Upstart.new(inspec, service_ctl)
|
113
|
+
else
|
114
|
+
Systemd.new(inspec, service_ctl)
|
115
|
+
end
|
116
|
+
elsif %w{debian}.include?(family)
|
117
|
+
version = inspec.os[:release].to_i
|
118
|
+
if version > 7
|
119
|
+
Systemd.new(inspec, service_ctl)
|
120
|
+
else
|
121
|
+
SysV.new(inspec, service_ctl || '/usr/sbin/service')
|
122
|
+
end
|
123
|
+
elsif %w{redhat fedora centos}.include?(family)
|
124
|
+
version = inspec.os[:release].to_i
|
125
|
+
if (%w{ redhat centos }.include?(family) && version >= 7) || (family == 'fedora' && version >= 15)
|
126
|
+
Systemd.new(inspec, service_ctl)
|
127
|
+
else
|
128
|
+
SysV.new(inspec, service_ctl || '/sbin/service')
|
129
|
+
end
|
130
|
+
elsif %w{wrlinux}.include?(family)
|
131
|
+
SysV.new(inspec, service_ctl)
|
132
|
+
elsif %w{darwin}.include?(family)
|
133
|
+
LaunchCtl.new(inspec, service_ctl)
|
134
|
+
elsif os.windows?
|
135
|
+
WindowsSrv.new(inspec)
|
136
|
+
elsif %w{freebsd}.include?(family)
|
137
|
+
BSDInit.new(inspec, service_ctl)
|
138
|
+
elsif %w{arch opensuse}.include?(family)
|
125
139
|
Systemd.new(inspec, service_ctl)
|
126
|
-
|
127
|
-
|
140
|
+
elsif %w{aix}.include?(family)
|
141
|
+
SrcMstr.new(inspec)
|
142
|
+
elsif os.solaris?
|
143
|
+
Svcs.new(inspec)
|
128
144
|
end
|
129
|
-
elsif %w{wrlinux}.include?(family)
|
130
|
-
SysV.new(inspec, service_ctl)
|
131
|
-
elsif %w{darwin}.include?(family)
|
132
|
-
LaunchCtl.new(inspec, service_ctl)
|
133
|
-
elsif os.windows?
|
134
|
-
WindowsSrv.new(inspec)
|
135
|
-
elsif %w{freebsd}.include?(family)
|
136
|
-
BSDInit.new(inspec, service_ctl)
|
137
|
-
elsif %w{arch opensuse}.include?(family)
|
138
|
-
Systemd.new(inspec, service_ctl)
|
139
|
-
elsif %w{aix}.include?(family)
|
140
|
-
SrcMstr.new(inspec)
|
141
|
-
elsif os.solaris?
|
142
|
-
Svcs.new(inspec)
|
143
145
|
end
|
144
|
-
end
|
145
146
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
147
|
+
def info
|
148
|
+
return nil if @service_mgmt.nil?
|
149
|
+
@cache ||= @service_mgmt.info(@service_name)
|
150
|
+
end
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
152
|
+
# verifies if the service is enabled
|
153
|
+
def enabled?(_level = nil)
|
154
|
+
return false if info.nil?
|
155
|
+
info[:enabled]
|
156
|
+
end
|
156
157
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
158
|
+
# verifies the service is registered
|
159
|
+
def installed?(_name = nil, _version = nil)
|
160
|
+
return false if info.nil?
|
161
|
+
info[:installed]
|
162
|
+
end
|
162
163
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
# verifies the service is currently running
|
165
|
+
def running?(_under = nil)
|
166
|
+
return false if info.nil?
|
167
|
+
info[:running]
|
168
|
+
end
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
170
|
+
# get all runlevels that are available and their configuration
|
171
|
+
def runlevels(*args)
|
172
|
+
return Runlevels.new(self) if info.nil? or info[:runlevels].nil?
|
173
|
+
Runlevels.from_hash(self, info[:runlevels], args)
|
174
|
+
end
|
174
175
|
|
175
|
-
|
176
|
-
|
176
|
+
def to_s
|
177
|
+
"Service #{@service_name}"
|
178
|
+
end
|
177
179
|
end
|
178
|
-
end
|
179
180
|
|
180
|
-
class ServiceManager
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
181
|
+
class ServiceManager
|
182
|
+
attr_reader :inspec, :service_ctl
|
183
|
+
def initialize(inspec, service_ctl = nil)
|
184
|
+
@inspec = inspec
|
185
|
+
@service_ctl ||= service_ctl
|
186
|
+
end
|
185
187
|
end
|
186
|
-
end
|
187
188
|
|
188
|
-
# @see: http://www.freedesktop.org/software/systemd/man/systemctl.html
|
189
|
-
# @see: http://www.freedesktop.org/software/systemd/man/systemd-system.conf.html
|
190
|
-
class Systemd < ServiceManager
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
189
|
+
# @see: http://www.freedesktop.org/software/systemd/man/systemctl.html
|
190
|
+
# @see: http://www.freedesktop.org/software/systemd/man/systemd-system.conf.html
|
191
|
+
class Systemd < ServiceManager
|
192
|
+
def initialize(inspec, service_ctl = nil)
|
193
|
+
@service_ctl = service_ctl || 'systemctl'
|
194
|
+
super
|
195
|
+
end
|
195
196
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
197
|
+
def info(service_name)
|
198
|
+
cmd = inspec.command("#{service_ctl} show --all #{service_name}")
|
199
|
+
return nil if cmd.exit_status.to_i != 0
|
200
|
+
|
201
|
+
# parse data
|
202
|
+
params = SimpleConfig.new(
|
203
|
+
cmd.stdout.chomp,
|
204
|
+
assignment_re: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
|
205
|
+
multiple_values: false,
|
206
|
+
).params
|
207
|
+
|
208
|
+
# LoadState values eg. loaded, not-found
|
209
|
+
installed = params['LoadState'] == 'loaded'
|
210
|
+
# test via 'systemctl is-active service'
|
211
|
+
# SubState values running
|
212
|
+
running = params['SubState'] == 'running'
|
213
|
+
# test via systemctl --quiet is-enabled
|
214
|
+
# ActiveState values eg.g inactive, active
|
215
|
+
enabled = params['UnitFileState'] == 'enabled'
|
216
|
+
|
217
|
+
{
|
218
|
+
name: params['Id'],
|
219
|
+
description: params['Description'],
|
220
|
+
installed: installed,
|
221
|
+
running: running,
|
222
|
+
enabled: enabled,
|
223
|
+
type: 'systemd',
|
224
|
+
}
|
225
|
+
end
|
224
226
|
end
|
225
|
-
end
|
226
227
|
|
227
|
-
# AIX services
|
228
|
-
class SrcMstr < ServiceManager
|
229
|
-
|
230
|
-
|
231
|
-
def info(service_name)
|
232
|
-
@name = service_name
|
233
|
-
running = status?
|
234
|
-
return nil if running.nil?
|
235
|
-
|
236
|
-
{
|
237
|
-
name: service_name,
|
238
|
-
description: nil,
|
239
|
-
installed: true,
|
240
|
-
running: running,
|
241
|
-
enabled: enabled?,
|
242
|
-
type: 'srcmstr',
|
243
|
-
}
|
244
|
-
end
|
228
|
+
# AIX services
|
229
|
+
class SrcMstr < ServiceManager
|
230
|
+
attr_reader :name
|
245
231
|
|
246
|
-
|
232
|
+
def info(service_name)
|
233
|
+
@name = service_name
|
234
|
+
running = status?
|
235
|
+
return nil if running.nil?
|
247
236
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
237
|
+
{
|
238
|
+
name: service_name,
|
239
|
+
description: nil,
|
240
|
+
installed: true,
|
241
|
+
running: running,
|
242
|
+
enabled: enabled?,
|
243
|
+
type: 'srcmstr',
|
244
|
+
}
|
245
|
+
end
|
253
246
|
|
254
|
-
|
255
|
-
enabled_rc_tcpip? || enabled_inittab?
|
256
|
-
end
|
247
|
+
private
|
257
248
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
249
|
+
def status?
|
250
|
+
status_cmd = inspec.command("lssrc -s #{@name}")
|
251
|
+
return nil if status_cmd.exit_status.to_i != 0
|
252
|
+
status_cmd.stdout.split(/\n/).last.chomp =~ /active$/ ? true : false
|
253
|
+
end
|
264
254
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
255
|
+
def enabled?
|
256
|
+
enabled_rc_tcpip? || enabled_inittab?
|
257
|
+
end
|
269
258
|
|
270
|
-
#
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
259
|
+
# #rubocop:disable Style/TrailingComma
|
260
|
+
def enabled_rc_tcpip?
|
261
|
+
inspec.command(
|
262
|
+
"grep -v ^# /etc/rc.tcpip | grep 'start ' | grep -Eq '(/{0,1}| )#{name} '",
|
263
|
+
).exit_status == 0
|
264
|
+
end
|
276
265
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
# fallback for systemv services, those are not handled via `initctl`
|
282
|
-
return SysV.new(inspec).info(service_name) if status.exit_status.to_i != 0
|
283
|
-
|
284
|
-
# @see: http://upstart.ubuntu.com/cookbook/#job-states
|
285
|
-
# grep for running to indicate the service is there
|
286
|
-
running = !status.stdout[%r{start/running}].nil?
|
287
|
-
|
288
|
-
{
|
289
|
-
name: service_name,
|
290
|
-
description: nil,
|
291
|
-
installed: true,
|
292
|
-
running: running,
|
293
|
-
enabled: info_enabled(status, service_name),
|
294
|
-
type: 'upstart',
|
295
|
-
}
|
266
|
+
def enabled_inittab?
|
267
|
+
inspec.command("lsitab #{name}").exit_status == 0
|
268
|
+
end
|
296
269
|
end
|
297
270
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
# $ initctl show-config $job | grep -q "^ start on" && echo enabled || echo disabled
|
304
|
-
# Ubuntu 10.04 show-config is not supported
|
305
|
-
# @see http://manpages.ubuntu.com/manpages/maverick/man8/initctl.8.html
|
306
|
-
support_for_show_config = Gem::Version.new('1.3')
|
307
|
-
|
308
|
-
if version >= support_for_show_config
|
309
|
-
config = inspec.command("#{service_ctl} show-config #{service_name}").stdout
|
310
|
-
else # use config file as fallback
|
311
|
-
config = inspec.file("/etc/init/#{service_name}.conf").content
|
271
|
+
# @see: http://upstart.ubuntu.com
|
272
|
+
class Upstart < ServiceManager
|
273
|
+
def initialize(service_name, service_ctl = nil)
|
274
|
+
@service_ctl = service_ctl || 'initctl'
|
275
|
+
super
|
312
276
|
end
|
313
277
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
278
|
+
def info(service_name)
|
279
|
+
# get the status of upstart service
|
280
|
+
status = inspec.command("#{service_ctl} status #{service_name}")
|
281
|
+
|
282
|
+
# fallback for systemv services, those are not handled via `initctl`
|
283
|
+
return SysV.new(inspec).info(service_name) if status.exit_status.to_i != 0
|
284
|
+
|
285
|
+
# @see: http://upstart.ubuntu.com/cookbook/#job-states
|
286
|
+
# grep for running to indicate the service is there
|
287
|
+
running = !status.stdout[%r{start/running}].nil?
|
288
|
+
|
289
|
+
{
|
290
|
+
name: service_name,
|
291
|
+
description: nil,
|
292
|
+
installed: true,
|
293
|
+
running: running,
|
294
|
+
enabled: info_enabled(status, service_name),
|
295
|
+
type: 'upstart',
|
296
|
+
}
|
324
297
|
end
|
325
298
|
|
326
|
-
|
327
|
-
end
|
299
|
+
private
|
328
300
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
301
|
+
def info_enabled(status, service_name)
|
302
|
+
# check if a service is enabled
|
303
|
+
# http://upstart.ubuntu.com/cookbook/#determine-if-a-job-is-disabled
|
304
|
+
# $ initctl show-config $job | grep -q "^ start on" && echo enabled || echo disabled
|
305
|
+
# Ubuntu 10.04 show-config is not supported
|
306
|
+
# @see http://manpages.ubuntu.com/manpages/maverick/man8/initctl.8.html
|
307
|
+
support_for_show_config = Gem::Version.new('1.3')
|
336
308
|
|
337
|
-
|
338
|
-
|
309
|
+
if version >= support_for_show_config
|
310
|
+
config = inspec.command("#{service_ctl} show-config #{service_name}").stdout
|
311
|
+
else # use config file as fallback
|
312
|
+
config = inspec.file("/etc/init/#{service_name}.conf").content
|
313
|
+
end
|
339
314
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
end
|
315
|
+
# disregard if the config does not exist
|
316
|
+
return nil if config.nil?
|
317
|
+
enabled = !config[/^\s*start on/].nil?
|
344
318
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
service = srvlist.stdout.split("\n").select { |srv| srv == service_name }
|
353
|
-
|
354
|
-
# abort if we could not find any service
|
355
|
-
return nil if service.empty?
|
356
|
-
|
357
|
-
# read all enabled services from runlevel
|
358
|
-
# on rhel via: 'chkconfig --list', is not installed by default
|
359
|
-
# bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
|
360
|
-
enabled_services_cmd = inspec.command('find /etc/rc*.d -name S*')
|
361
|
-
service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
|
362
|
-
all_services = enabled_services_cmd.stdout.split("\n").map { |line|
|
363
|
-
service_line.match(line)
|
364
|
-
}.compact
|
365
|
-
enabled = !all_services.empty?
|
366
|
-
|
367
|
-
# Determine a list of runlevels which this service is activated for
|
368
|
-
runlevels = RUNLEVELS.dup
|
369
|
-
all_services.each { |x| runlevels[x[:runlevel].to_i] = true }
|
370
|
-
|
371
|
-
# check if service is really running
|
372
|
-
# service throws an exit code if the service is not installed or
|
373
|
-
# not enabled
|
374
|
-
|
375
|
-
cmd = inspec.command("#{service_ctl} #{service_name} status")
|
376
|
-
running = cmd.exit_status == 0
|
377
|
-
{
|
378
|
-
name: service_name,
|
379
|
-
description: nil,
|
380
|
-
installed: true,
|
381
|
-
running: running,
|
382
|
-
enabled: enabled,
|
383
|
-
runlevels: runlevels,
|
384
|
-
type: 'sysv',
|
385
|
-
}
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# @see: https://www.freebsd.org/doc/en/articles/linux-users/startup.html
|
390
|
-
# @see: https://www.freebsd.org/cgi/man.cgi?query=rc.conf&sektion=5
|
391
|
-
class BSDInit < ServiceManager
|
392
|
-
def initialize(service_name, service_ctl = nil)
|
393
|
-
@service_ctl = service_ctl || 'service'
|
394
|
-
super
|
395
|
-
end
|
319
|
+
# implement fallback for Ubuntu 10.04
|
320
|
+
if inspec.os[:family] == 'ubuntu' &&
|
321
|
+
inspec.os[:release].to_f >= 10.04 &&
|
322
|
+
inspec.os[:release].to_f < 12.04 &&
|
323
|
+
status.exit_status == 0
|
324
|
+
enabled = true
|
325
|
+
end
|
396
326
|
|
397
|
-
|
398
|
-
|
399
|
-
# services are enabled in /etc/rc.conf and /etc/defaults/rc.conf
|
400
|
-
# via #{service_name}_enable="YES"
|
401
|
-
# service SERVICE status returns the following result if not activated:
|
402
|
-
# Cannot 'status' sshd. Set sshd_enable to YES in /etc/rc.conf or use 'onestatus' instead of 'status'.
|
403
|
-
# gather all enabled services
|
404
|
-
cmd = inspec.command("#{service_ctl} -e")
|
405
|
-
return nil if cmd.exit_status != 0
|
406
|
-
|
407
|
-
# search for the service
|
408
|
-
srv = /(^.*#{service_name}$)/.match(cmd.stdout)
|
409
|
-
return nil if srv.nil? || srv[0].nil?
|
410
|
-
enabled = true
|
411
|
-
|
412
|
-
# check if the service is running
|
413
|
-
# if the service is not available or not running, we always get an error code
|
414
|
-
cmd = inspec.command("#{service_ctl} #{service_name} onestatus")
|
415
|
-
running = cmd.exit_status == 0
|
416
|
-
|
417
|
-
{
|
418
|
-
name: service_name,
|
419
|
-
description: nil,
|
420
|
-
installed: true,
|
421
|
-
running: running,
|
422
|
-
enabled: enabled,
|
423
|
-
type: 'bsd-init',
|
424
|
-
}
|
425
|
-
end
|
426
|
-
end
|
327
|
+
enabled
|
328
|
+
end
|
427
329
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
330
|
+
def version
|
331
|
+
@version ||= (
|
332
|
+
out = inspec.command("#{service_ctl} --version").stdout
|
333
|
+
Gem::Version.new(out[/\(upstart ([^\)]+)\)/, 1])
|
334
|
+
)
|
335
|
+
end
|
432
336
|
end
|
433
337
|
|
434
|
-
|
435
|
-
|
436
|
-
# get the status of runit service
|
437
|
-
cmd = inspec.command("#{service_ctl} status #{service_name}")
|
438
|
-
# return nil unless cmd.exit_status == 0 # NOTE(sr) why do we do this?
|
439
|
-
|
440
|
-
installed = cmd.exit_status == 0
|
441
|
-
running = installed && !!(cmd.stdout =~ /^run:/)
|
442
|
-
enabled = installed && (running || !!(cmd.stdout =~ /normally up/) || !!(cmd.stdout =~ /want up/))
|
443
|
-
|
444
|
-
{
|
445
|
-
name: service_name,
|
446
|
-
description: nil,
|
447
|
-
installed: installed,
|
448
|
-
running: running,
|
449
|
-
enabled: enabled,
|
450
|
-
type: 'runit',
|
451
|
-
}
|
452
|
-
end
|
453
|
-
end
|
338
|
+
class SysV < ServiceManager
|
339
|
+
RUNLEVELS = { 0=>false, 1=>false, 2=>false, 3=>false, 4=>false, 5=>false, 6=>false }.freeze
|
454
340
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
@service_ctl = service_ctl || 'launchctl'
|
460
|
-
super
|
461
|
-
end
|
341
|
+
def initialize(service_name, service_ctl = nil)
|
342
|
+
@service_ctl = service_ctl || 'service'
|
343
|
+
super
|
344
|
+
end
|
462
345
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
346
|
+
def info(service_name)
|
347
|
+
# check if service is installed
|
348
|
+
# read all available services via ls /etc/init.d/
|
349
|
+
srvlist = inspec.command('ls -1 /etc/init.d/')
|
350
|
+
return nil if srvlist.exit_status != 0
|
351
|
+
|
352
|
+
# check if the service is in list
|
353
|
+
service = srvlist.stdout.split("\n").select { |srv| srv == service_name }
|
354
|
+
|
355
|
+
# abort if we could not find any service
|
356
|
+
return nil if service.empty?
|
357
|
+
|
358
|
+
# read all enabled services from runlevel
|
359
|
+
# on rhel via: 'chkconfig --list', is not installed by default
|
360
|
+
# bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
|
361
|
+
enabled_services_cmd = inspec.command('find /etc/rc*.d -name S*')
|
362
|
+
service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
|
363
|
+
all_services = enabled_services_cmd.stdout.split("\n").map { |line|
|
364
|
+
service_line.match(line)
|
365
|
+
}.compact
|
366
|
+
enabled = !all_services.empty?
|
367
|
+
|
368
|
+
# Determine a list of runlevels which this service is activated for
|
369
|
+
runlevels = RUNLEVELS.dup
|
370
|
+
all_services.each { |x| runlevels[x[:runlevel].to_i] = true }
|
371
|
+
|
372
|
+
# check if service is really running
|
373
|
+
# service throws an exit code if the service is not installed or
|
374
|
+
# not enabled
|
375
|
+
|
376
|
+
cmd = inspec.command("#{service_ctl} #{service_name} status")
|
377
|
+
running = cmd.exit_status == 0
|
378
|
+
{
|
379
|
+
name: service_name,
|
380
|
+
description: nil,
|
381
|
+
installed: true,
|
382
|
+
running: running,
|
383
|
+
enabled: enabled,
|
384
|
+
runlevels: runlevels,
|
385
|
+
type: 'sysv',
|
386
|
+
}
|
387
|
+
end
|
491
388
|
end
|
492
|
-
end
|
493
389
|
|
494
|
-
#
|
495
|
-
#
|
496
|
-
class
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
# "DisplayName": "DHCP Client",
|
502
|
-
# "Status": 4
|
503
|
-
# }
|
504
|
-
#
|
505
|
-
# Until StartMode is not added to Get-Service, we need to do a workaround
|
506
|
-
# @see: https://connect.microsoft.com/PowerShell/feedback/details/424948/i-would-like-to-see-the-property-starttype-added-to-get-services
|
507
|
-
# Use the following powershell to determine the start mode
|
508
|
-
# PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
|
509
|
-
# erty Name, StartMode, State, Status | ConvertTo-Json
|
510
|
-
# {
|
511
|
-
# "Name": "Dhcp",
|
512
|
-
# "StartMode": "Auto",
|
513
|
-
# "State": "Running",
|
514
|
-
# "Status": "OK"
|
515
|
-
# }
|
516
|
-
#
|
517
|
-
# Windows Services have the following status code:
|
518
|
-
# @see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
|
519
|
-
# - 1: Stopped
|
520
|
-
# - 2: Starting
|
521
|
-
# - 3: Stopping
|
522
|
-
# - 4: Running
|
523
|
-
# - 5: Continue Pending
|
524
|
-
# - 6: Pause Pending
|
525
|
-
# - 7: Paused
|
526
|
-
def info(service_name)
|
527
|
-
cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name #{service_name}| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json")
|
528
|
-
|
529
|
-
# cannot rely on exit code for now, successful command returns exit code 1
|
530
|
-
# return nil if cmd.exit_status != 0
|
531
|
-
# try to parse json
|
532
|
-
begin
|
533
|
-
service = JSON.parse(cmd.stdout)
|
534
|
-
rescue JSON::ParserError => _e
|
535
|
-
return nil
|
536
|
-
end
|
537
|
-
|
538
|
-
# check that we got a response
|
539
|
-
return nil if service.nil? || service['Service'].nil?
|
540
|
-
|
541
|
-
{
|
542
|
-
name: service['Service']['Name'],
|
543
|
-
description: service['Service']['DisplayName'],
|
544
|
-
installed: true,
|
545
|
-
running: service_running?(service),
|
546
|
-
enabled: service_enabled?(service),
|
547
|
-
type: 'windows',
|
548
|
-
}
|
549
|
-
end
|
390
|
+
# @see: https://www.freebsd.org/doc/en/articles/linux-users/startup.html
|
391
|
+
# @see: https://www.freebsd.org/cgi/man.cgi?query=rc.conf&sektion=5
|
392
|
+
class BSDInit < ServiceManager
|
393
|
+
def initialize(service_name, service_ctl = nil)
|
394
|
+
@service_ctl = service_ctl || 'service'
|
395
|
+
super
|
396
|
+
end
|
550
397
|
|
551
|
-
|
398
|
+
def info(service_name)
|
399
|
+
# check if service is enabled
|
400
|
+
# services are enabled in /etc/rc.conf and /etc/defaults/rc.conf
|
401
|
+
# via #{service_name}_enable="YES"
|
402
|
+
# service SERVICE status returns the following result if not activated:
|
403
|
+
# Cannot 'status' sshd. Set sshd_enable to YES in /etc/rc.conf or use 'onestatus' instead of 'status'.
|
404
|
+
# gather all enabled services
|
405
|
+
cmd = inspec.command("#{service_ctl} -e")
|
406
|
+
return nil if cmd.exit_status != 0
|
407
|
+
|
408
|
+
# search for the service
|
409
|
+
srv = /(^.*#{service_name}$)/.match(cmd.stdout)
|
410
|
+
return nil if srv.nil? || srv[0].nil?
|
411
|
+
enabled = true
|
552
412
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
413
|
+
# check if the service is running
|
414
|
+
# if the service is not available or not running, we always get an error code
|
415
|
+
cmd = inspec.command("#{service_ctl} #{service_name} onestatus")
|
416
|
+
running = cmd.exit_status == 0
|
417
|
+
|
418
|
+
{
|
419
|
+
name: service_name,
|
420
|
+
description: nil,
|
421
|
+
installed: true,
|
422
|
+
running: running,
|
423
|
+
enabled: enabled,
|
424
|
+
type: 'bsd-init',
|
425
|
+
}
|
426
|
+
end
|
558
427
|
end
|
559
428
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
end
|
429
|
+
class Runit < ServiceManager
|
430
|
+
def initialize(service_name, service_ctl = nil)
|
431
|
+
@service_ctl = service_ctl || 'sv'
|
432
|
+
super
|
433
|
+
end
|
565
434
|
|
566
|
-
#
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
435
|
+
# rubocop:disable Style/DoubleNegation
|
436
|
+
def info(service_name)
|
437
|
+
# get the status of runit service
|
438
|
+
cmd = inspec.command("#{service_ctl} status #{service_name}")
|
439
|
+
# return nil unless cmd.exit_status == 0 # NOTE(sr) why do we do this?
|
440
|
+
|
441
|
+
installed = cmd.exit_status == 0
|
442
|
+
running = installed && !!(cmd.stdout =~ /^run:/)
|
443
|
+
enabled = installed && (running || !!(cmd.stdout =~ /normally up/) || !!(cmd.stdout =~ /want up/))
|
444
|
+
|
445
|
+
{
|
446
|
+
name: service_name,
|
447
|
+
description: nil,
|
448
|
+
installed: installed,
|
449
|
+
running: running,
|
450
|
+
enabled: enabled,
|
451
|
+
type: 'runit',
|
452
|
+
}
|
453
|
+
end
|
571
454
|
end
|
572
455
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
456
|
+
# MacOS / Darwin
|
457
|
+
# new launctl on macos 10.10
|
458
|
+
class LaunchCtl < ServiceManager
|
459
|
+
def initialize(service_name, service_ctl = nil)
|
460
|
+
@service_ctl = service_ctl || 'launchctl'
|
461
|
+
super
|
462
|
+
end
|
463
|
+
|
464
|
+
def info(service_name)
|
465
|
+
# get the status of upstart service
|
466
|
+
cmd = inspec.command("#{service_ctl} list")
|
467
|
+
return nil if cmd.exit_status != 0
|
468
|
+
|
469
|
+
# search for the service
|
470
|
+
srv = /(^.*#{service_name}.*)/.match(cmd.stdout)
|
471
|
+
return nil if srv.nil? || srv[0].nil?
|
472
|
+
|
473
|
+
# extract values from service
|
474
|
+
parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[0-9]+)\t(?<name>\S*)$/.match(srv[0])
|
475
|
+
enabled = !parsed_srv['name'].nil? # it's in the list
|
476
|
+
|
477
|
+
# check if the service is running
|
478
|
+
pid = parsed_srv['pid']
|
479
|
+
running = pid != '-'
|
480
|
+
|
481
|
+
# extract service label
|
482
|
+
srv = parsed_srv['name'] || service_name
|
483
|
+
|
484
|
+
{
|
485
|
+
name: srv,
|
486
|
+
description: nil,
|
487
|
+
installed: true,
|
488
|
+
running: running,
|
489
|
+
enabled: enabled,
|
490
|
+
type: 'darwin',
|
491
|
+
}
|
492
|
+
end
|
596
493
|
end
|
597
|
-
end
|
598
494
|
|
599
|
-
#
|
495
|
+
# Determine the service state from Windows
|
496
|
+
# Uses Powershell to retrieve the information
|
497
|
+
class WindowsSrv < ServiceManager
|
498
|
+
# Determine service details
|
499
|
+
# PS: Get-Service -Name 'dhcp'| Select-Object -Property Name, DisplayName, Status | ConvertTo-Json
|
500
|
+
# {
|
501
|
+
# "Name": "dhcp",
|
502
|
+
# "DisplayName": "DHCP Client",
|
503
|
+
# "Status": 4
|
504
|
+
# }
|
505
|
+
#
|
506
|
+
# Until StartMode is not added to Get-Service, we need to do a workaround
|
507
|
+
# @see: https://connect.microsoft.com/PowerShell/feedback/details/424948/i-would-like-to-see-the-property-starttype-added-to-get-services
|
508
|
+
# Use the following powershell to determine the start mode
|
509
|
+
# PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
|
510
|
+
# erty Name, StartMode, State, Status | ConvertTo-Json
|
511
|
+
# {
|
512
|
+
# "Name": "Dhcp",
|
513
|
+
# "StartMode": "Auto",
|
514
|
+
# "State": "Running",
|
515
|
+
# "Status": "OK"
|
516
|
+
# }
|
517
|
+
#
|
518
|
+
# Windows Services have the following status code:
|
519
|
+
# @see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
|
520
|
+
# - 1: Stopped
|
521
|
+
# - 2: Starting
|
522
|
+
# - 3: Stopping
|
523
|
+
# - 4: Running
|
524
|
+
# - 5: Continue Pending
|
525
|
+
# - 6: Pause Pending
|
526
|
+
# - 7: Paused
|
527
|
+
def info(service_name)
|
528
|
+
cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name #{service_name}| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json")
|
529
|
+
|
530
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
531
|
+
# return nil if cmd.exit_status != 0
|
532
|
+
# try to parse json
|
533
|
+
begin
|
534
|
+
service = JSON.parse(cmd.stdout)
|
535
|
+
rescue JSON::ParserError => _e
|
536
|
+
return nil
|
537
|
+
end
|
600
538
|
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
539
|
+
# check that we got a response
|
540
|
+
return nil if service.nil? || service['Service'].nil?
|
541
|
+
|
542
|
+
{
|
543
|
+
name: service['Service']['Name'],
|
544
|
+
description: service['Service']['DisplayName'],
|
545
|
+
installed: true,
|
546
|
+
running: service_running?(service),
|
547
|
+
enabled: service_enabled?(service),
|
548
|
+
type: 'windows',
|
549
|
+
}
|
610
550
|
end
|
611
551
|
|
612
|
-
|
613
|
-
|
614
|
-
|
552
|
+
private
|
553
|
+
|
554
|
+
# detect if service is enabled
|
555
|
+
def service_enabled?(service)
|
556
|
+
!service['WMI'].nil? &&
|
557
|
+
!service['WMI']['StartMode'].nil? &&
|
558
|
+
service['WMI']['StartMode'] == 'Auto'
|
615
559
|
end
|
616
|
-
"
|
617
560
|
|
618
|
-
|
619
|
-
|
561
|
+
# detect if service is running
|
562
|
+
def service_running?(service)
|
563
|
+
!service['Service']['Status'].nil? && service['Service']['Status'] == 4
|
564
|
+
end
|
620
565
|
end
|
621
|
-
end
|
622
566
|
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
describe upstart_service('service_name') do
|
629
|
-
it { should be_installed }
|
630
|
-
it { should be_enabled }
|
631
|
-
it { should be_running }
|
567
|
+
# Solaris services
|
568
|
+
class Svcs < ServiceManager
|
569
|
+
def initialize(service_name, service_ctl = nil)
|
570
|
+
@service_ctl = service_ctl || 'svcs'
|
571
|
+
super
|
632
572
|
end
|
633
573
|
|
634
|
-
|
635
|
-
|
636
|
-
|
574
|
+
def info(service_name)
|
575
|
+
# get the status of runit service
|
576
|
+
cmd = inspec.command("#{service_ctl} -l #{service_name}")
|
577
|
+
return nil if cmd.exit_status != 0
|
578
|
+
|
579
|
+
params = SimpleConfig.new(
|
580
|
+
cmd.stdout.chomp,
|
581
|
+
assignment_re: /^(\w+)\s*(.*)$/,
|
582
|
+
multiple_values: false,
|
583
|
+
).params
|
584
|
+
|
585
|
+
installed = cmd.exit_status == 0
|
586
|
+
running = installed && (params['state'] == 'online')
|
587
|
+
enabled = installed && (params['enabled'] == 'true')
|
588
|
+
|
589
|
+
{
|
590
|
+
name: service_name,
|
591
|
+
description: params['name'],
|
592
|
+
installed: installed,
|
593
|
+
running: running,
|
594
|
+
enabled: enabled,
|
595
|
+
type: 'svcs',
|
596
|
+
}
|
637
597
|
end
|
638
|
-
"
|
639
|
-
|
640
|
-
def select_service_mgmt
|
641
|
-
Upstart.new(inspec, service_ctl)
|
642
598
|
end
|
643
|
-
end
|
644
599
|
|
645
|
-
|
646
|
-
name 'sysv_service'
|
647
|
-
desc 'Use the sysv_service InSpec audit resource to test if the named service (controlled by SysV) is installed, running and/or enabled.'
|
648
|
-
example "
|
649
|
-
# to override service mgmt auto-detection
|
650
|
-
describe sysv_service('service_name') do
|
651
|
-
it { should be_installed }
|
652
|
-
it { should be_enabled }
|
653
|
-
it { should be_running }
|
654
|
-
end
|
600
|
+
# specific resources for specific service managers
|
655
601
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
602
|
+
class SystemdService < Service
|
603
|
+
name 'systemd_service'
|
604
|
+
desc 'Use the systemd_service InSpec audit resource to test if the named service (controlled by systemd) is installed, running and/or enabled.'
|
605
|
+
example "
|
606
|
+
# to override service mgmt auto-detection
|
607
|
+
describe systemd_service('service_name') do
|
608
|
+
it { should be_installed }
|
609
|
+
it { should be_enabled }
|
610
|
+
it { should be_running }
|
611
|
+
end
|
661
612
|
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
end
|
613
|
+
# to set a non-standard systemctl path
|
614
|
+
describe systemd_service('service_name', '/path/to/systemctl') do
|
615
|
+
it { should be_running }
|
616
|
+
end
|
617
|
+
"
|
666
618
|
|
667
|
-
|
668
|
-
|
669
|
-
desc 'Use the bsd_service InSpec audit resource to test if the named service (controlled by BSD init) is installed, running and/or enabled.'
|
670
|
-
example "
|
671
|
-
# to override service mgmt auto-detection
|
672
|
-
describe bsd_service('service_name') do
|
673
|
-
it { should be_installed }
|
674
|
-
it { should be_enabled }
|
675
|
-
it { should be_running }
|
619
|
+
def select_service_mgmt
|
620
|
+
Systemd.new(inspec, service_ctl)
|
676
621
|
end
|
622
|
+
end
|
677
623
|
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
624
|
+
class UpstartService < Service
|
625
|
+
name 'upstart_service'
|
626
|
+
desc 'Use the upstart_service InSpec audit resource to test if the named service (controlled by upstart) is installed, running and/or enabled.'
|
627
|
+
example "
|
628
|
+
# to override service mgmt auto-detection
|
629
|
+
describe upstart_service('service_name') do
|
630
|
+
it { should be_installed }
|
631
|
+
it { should be_enabled }
|
632
|
+
it { should be_running }
|
633
|
+
end
|
683
634
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
end
|
635
|
+
# to set a non-standard initctl path
|
636
|
+
describe upstart_service('service_name', '/path/to/initctl') do
|
637
|
+
it { should be_running }
|
638
|
+
end
|
639
|
+
"
|
688
640
|
|
689
|
-
|
690
|
-
|
691
|
-
desc 'Use the launchd_service InSpec audit resource to test if the named service (controlled by launchd) is installed, running and/or enabled.'
|
692
|
-
example "
|
693
|
-
# to override service mgmt auto-detection
|
694
|
-
describe launchd_service('service_name') do
|
695
|
-
it { should be_installed }
|
696
|
-
it { should be_enabled }
|
697
|
-
it { should be_running }
|
641
|
+
def select_service_mgmt
|
642
|
+
Upstart.new(inspec, service_ctl)
|
698
643
|
end
|
644
|
+
end
|
699
645
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
646
|
+
class SysVService < Service
|
647
|
+
name 'sysv_service'
|
648
|
+
desc 'Use the sysv_service InSpec audit resource to test if the named service (controlled by SysV) is installed, running and/or enabled.'
|
649
|
+
example "
|
650
|
+
# to override service mgmt auto-detection
|
651
|
+
describe sysv_service('service_name') do
|
652
|
+
it { should be_installed }
|
653
|
+
it { should be_enabled }
|
654
|
+
it { should be_running }
|
655
|
+
end
|
705
656
|
|
706
|
-
|
707
|
-
|
657
|
+
# to set a non-standard service path
|
658
|
+
describe sysv_service('service_name', '/path/to/service') do
|
659
|
+
it { should be_running }
|
660
|
+
end
|
661
|
+
"
|
662
|
+
|
663
|
+
def select_service_mgmt
|
664
|
+
SysV.new(inspec, service_ctl)
|
665
|
+
end
|
708
666
|
end
|
709
|
-
end
|
710
667
|
|
711
|
-
class
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
668
|
+
class BSDService < Service
|
669
|
+
name 'bsd_service'
|
670
|
+
desc 'Use the bsd_service InSpec audit resource to test if the named service (controlled by BSD init) is installed, running and/or enabled.'
|
671
|
+
example "
|
672
|
+
# to override service mgmt auto-detection
|
673
|
+
describe bsd_service('service_name') do
|
674
|
+
it { should be_installed }
|
675
|
+
it { should be_enabled }
|
676
|
+
it { should be_running }
|
677
|
+
end
|
678
|
+
|
679
|
+
# to set a non-standard service path
|
680
|
+
describe bsd_service('service_name', '/path/to/service') do
|
681
|
+
it { should be_running }
|
682
|
+
end
|
683
|
+
"
|
684
|
+
|
685
|
+
def select_service_mgmt
|
686
|
+
BSDInit.new(inspec, service_ctl)
|
720
687
|
end
|
688
|
+
end
|
689
|
+
|
690
|
+
class LaunchdService < Service
|
691
|
+
name 'launchd_service'
|
692
|
+
desc 'Use the launchd_service InSpec audit resource to test if the named service (controlled by launchd) is installed, running and/or enabled.'
|
693
|
+
example "
|
694
|
+
# to override service mgmt auto-detection
|
695
|
+
describe launchd_service('service_name') do
|
696
|
+
it { should be_installed }
|
697
|
+
it { should be_enabled }
|
698
|
+
it { should be_running }
|
699
|
+
end
|
721
700
|
|
722
|
-
|
723
|
-
|
724
|
-
|
701
|
+
# to set a non-standard launchctl path
|
702
|
+
describe launchd_service('service_name', '/path/to/launchctl') do
|
703
|
+
it { should be_running }
|
704
|
+
end
|
705
|
+
"
|
706
|
+
|
707
|
+
def select_service_mgmt
|
708
|
+
LaunchCtl.new(inspec, service_ctl)
|
725
709
|
end
|
726
|
-
|
710
|
+
end
|
711
|
+
|
712
|
+
class RunitService < Service
|
713
|
+
name 'runit_service'
|
714
|
+
desc 'Use the runit_service InSpec audit resource to test if the named service (controlled by runit) is installed, running and/or enabled.'
|
715
|
+
example "
|
716
|
+
# to override service mgmt auto-detection
|
717
|
+
describe runit_service('service_name') do
|
718
|
+
it { should be_installed }
|
719
|
+
it { should be_enabled }
|
720
|
+
it { should be_running }
|
721
|
+
end
|
722
|
+
|
723
|
+
# to set a non-standard sv path
|
724
|
+
describe runit_service('service_name', '/path/to/sv') do
|
725
|
+
it { should be_running }
|
726
|
+
end
|
727
|
+
"
|
727
728
|
|
728
|
-
|
729
|
-
|
729
|
+
def select_service_mgmt
|
730
|
+
Runit.new(inspec, service_ctl)
|
731
|
+
end
|
730
732
|
end
|
731
733
|
end
|