inspec 4.10.4 → 4.12.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/README.md +7 -7
- data/inspec.gemspec +2 -1
- data/lib/inspec/resources/command.rb +2 -0
- data/lib/inspec/resources/dh_params.rb +63 -61
- data/lib/inspec/resources/etc_hosts.rb +46 -44
- data/lib/inspec/resources/groups.rb +26 -8
- data/lib/inspec/resources/grub_conf.rb +180 -178
- data/lib/inspec/resources/iis_app_pool.rb +106 -104
- data/lib/inspec/resources/service.rb +12 -3
- data/lib/inspec/resources/ssl.rb +74 -72
- data/lib/inspec/runner.rb +1 -0
- data/lib/inspec/version.rb +1 -1
- metadata +18 -4
@@ -5,125 +5,127 @@ require "inspec/resources/powershell"
|
|
5
5
|
# check for web applications in IIS
|
6
6
|
# Note: this is only supported in windows 2012 and later
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def pool_name
|
31
|
-
iis_app_pool[:pool_name]
|
32
|
-
end
|
33
|
-
|
34
|
-
def runtime_version
|
35
|
-
iis_app_pool[:version]
|
36
|
-
end
|
8
|
+
module Inspec::Resources
|
9
|
+
class IisAppPool < Inspec.resource(1)
|
10
|
+
name "iis_app_pool"
|
11
|
+
desc "Tests IIS application pool configuration on windows."
|
12
|
+
supports platform: "windows"
|
13
|
+
example <<~EXAMPLE
|
14
|
+
describe iis_app_pool('DefaultAppPool') do
|
15
|
+
it { should exist }
|
16
|
+
its('enable32bit') { should cmp 'True' }
|
17
|
+
its('runtime_version') { should eq 'v4.0' }
|
18
|
+
its('pipeline_mode') { should eq 'Integrated' }
|
19
|
+
end
|
20
|
+
EXAMPLE
|
21
|
+
|
22
|
+
def initialize(pool_name)
|
23
|
+
@pool_name = pool_name
|
24
|
+
@pool_path = "IIS:\\AppPools\\#{@pool_name}"
|
25
|
+
@cache = nil
|
26
|
+
|
27
|
+
# verify that this resource is only supported on Windows
|
28
|
+
return skip_resource "The `iis_app_pool` resource is not supported on your OS." unless inspec.os.windows?
|
29
|
+
end
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
def pool_name
|
32
|
+
iis_app_pool[:pool_name]
|
33
|
+
end
|
41
34
|
|
42
|
-
|
43
|
-
|
44
|
-
|
35
|
+
def runtime_version
|
36
|
+
iis_app_pool[:version]
|
37
|
+
end
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
def enable32bit
|
40
|
+
iis_app_pool[:e32b]
|
41
|
+
end
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
|
43
|
+
def pipeline_mode
|
44
|
+
iis_app_pool[:mode]
|
45
|
+
end
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
47
|
+
def max_processes
|
48
|
+
iis_app_pool[:processes]
|
49
|
+
end
|
57
50
|
|
58
|
-
|
59
|
-
|
60
|
-
|
51
|
+
def timeout
|
52
|
+
iis_app_pool[:timeout]
|
53
|
+
end
|
61
54
|
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
def timeout_days
|
56
|
+
iis_app_pool[:timeout_days]
|
57
|
+
end
|
65
58
|
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
def timeout_hours
|
60
|
+
iis_app_pool[:timeout_hours]
|
61
|
+
end
|
69
62
|
|
70
|
-
|
71
|
-
|
72
|
-
|
63
|
+
def timeout_minutes
|
64
|
+
iis_app_pool[:timeout_minutes]
|
65
|
+
end
|
73
66
|
|
74
|
-
|
75
|
-
|
76
|
-
|
67
|
+
def timeout_seconds
|
68
|
+
iis_app_pool[:timeout_seconds]
|
69
|
+
end
|
77
70
|
|
78
|
-
|
79
|
-
|
80
|
-
|
71
|
+
def user_identity_type
|
72
|
+
iis_app_pool[:user_identity_type]
|
73
|
+
end
|
81
74
|
|
82
|
-
|
83
|
-
|
84
|
-
|
75
|
+
def username
|
76
|
+
iis_app_pool[:username]
|
77
|
+
end
|
85
78
|
|
86
|
-
|
79
|
+
def exists?
|
80
|
+
!iis_app_pool[:pool_name].empty?
|
81
|
+
end
|
87
82
|
|
88
|
-
|
89
|
-
|
83
|
+
def to_s
|
84
|
+
"IIS App Pool '#{@pool_name}'"
|
85
|
+
end
|
90
86
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
87
|
+
private
|
88
|
+
|
89
|
+
def iis_app_pool
|
90
|
+
return @cache unless @cache.nil?
|
91
|
+
|
92
|
+
# We use `-Compress` here to avoid a bug in PowerShell
|
93
|
+
# It does not affect validity of the output, only the representation
|
94
|
+
# See: https://github.com/inspec/inspec/pull/3842
|
95
|
+
script = <<~EOH
|
96
|
+
Import-Module WebAdministration
|
97
|
+
If (Test-Path '#{@pool_path}') {
|
98
|
+
Get-Item '#{@pool_path}' | Select-Object * | ConvertTo-Json -Compress
|
99
|
+
} Else {
|
100
|
+
Write-Host '{}'
|
101
|
+
}
|
102
|
+
EOH
|
103
|
+
cmd = inspec.powershell(script)
|
104
|
+
|
105
|
+
begin
|
106
|
+
pool = JSON.parse(cmd.stdout)
|
107
|
+
rescue JSON::ParserError => _e
|
108
|
+
raise Inspec::Exceptions::ResourceFailed, "Unable to parse app pool JSON"
|
109
|
+
end
|
110
|
+
|
111
|
+
process_model = pool.fetch("processModel", {})
|
112
|
+
idle_timeout = process_model.fetch("idleTimeout", {})
|
113
|
+
|
114
|
+
# map our values to a hash table
|
115
|
+
@cache = {
|
116
|
+
pool_name: pool["name"],
|
117
|
+
version: pool["managedRuntimeVersion"],
|
118
|
+
e32b: pool["enable32BitAppOnWin64"],
|
119
|
+
mode: pool["managedPipelineMode"],
|
120
|
+
processes: process_model["maxProcesses"],
|
121
|
+
timeout: "#{idle_timeout["Hours"]}:#{idle_timeout["Minutes"]}:#{idle_timeout["Seconds"]}",
|
122
|
+
timeout_days: idle_timeout["Days"],
|
123
|
+
timeout_hours: idle_timeout["Hours"],
|
124
|
+
timeout_minutes: idle_timeout["Minutes"],
|
125
|
+
timeout_seconds: idle_timeout["Seconds"],
|
126
|
+
user_identity_type: process_model["identityType"],
|
127
|
+
username: process_model["userName"],
|
100
128
|
}
|
101
|
-
|
102
|
-
cmd = inspec.powershell(script)
|
103
|
-
|
104
|
-
begin
|
105
|
-
pool = JSON.parse(cmd.stdout)
|
106
|
-
rescue JSON::ParserError => _e
|
107
|
-
raise Inspec::Exceptions::ResourceFailed, "Unable to parse app pool JSON"
|
108
|
-
end
|
109
|
-
|
110
|
-
process_model = pool.fetch("processModel", {})
|
111
|
-
idle_timeout = process_model.fetch("idleTimeout", {})
|
112
|
-
|
113
|
-
# map our values to a hash table
|
114
|
-
@cache = {
|
115
|
-
pool_name: pool["name"],
|
116
|
-
version: pool["managedRuntimeVersion"],
|
117
|
-
e32b: pool["enable32BitAppOnWin64"],
|
118
|
-
mode: pool["managedPipelineMode"],
|
119
|
-
processes: process_model["maxProcesses"],
|
120
|
-
timeout: "#{idle_timeout["Hours"]}:#{idle_timeout["Minutes"]}:#{idle_timeout["Seconds"]}",
|
121
|
-
timeout_days: idle_timeout["Days"],
|
122
|
-
timeout_hours: idle_timeout["Hours"],
|
123
|
-
timeout_minutes: idle_timeout["Minutes"],
|
124
|
-
timeout_seconds: idle_timeout["Seconds"],
|
125
|
-
user_identity_type: process_model["identityType"],
|
126
|
-
username: process_model["userName"],
|
127
|
-
}
|
129
|
+
end
|
128
130
|
end
|
129
131
|
end
|
@@ -243,6 +243,13 @@ module Inspec::Resources
|
|
243
243
|
info[:startmode]
|
244
244
|
end
|
245
245
|
|
246
|
+
# returns the service's user from info
|
247
|
+
def startname
|
248
|
+
return nil if info.nil?
|
249
|
+
|
250
|
+
info[:startname]
|
251
|
+
end
|
252
|
+
|
246
253
|
def to_s
|
247
254
|
"Service #{@service_name}"
|
248
255
|
end
|
@@ -575,12 +582,13 @@ module Inspec::Resources
|
|
575
582
|
# Also see: https://msdn.microsoft.com/en-us/library/aa384896(v=vs.85).aspx
|
576
583
|
# Use the following powershell to determine the start mode
|
577
584
|
# PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
|
578
|
-
# erty Name, StartMode, State, Status | ConvertTo-Json
|
585
|
+
# erty Name, StartMode, State, Status, StartName | ConvertTo-Json
|
579
586
|
# {
|
580
587
|
# "Name": "Dhcp",
|
581
588
|
# "StartMode": "Auto",
|
582
589
|
# "State": "Running",
|
583
|
-
# "Status": "OK"
|
590
|
+
# "Status": "OK",
|
591
|
+
# "StartName": "LocalSystem"
|
584
592
|
# }
|
585
593
|
#
|
586
594
|
# Windows Services have the following status code:
|
@@ -593,7 +601,7 @@ module Inspec::Resources
|
|
593
601
|
# - 6: Pause Pending
|
594
602
|
# - 7: Paused
|
595
603
|
def info(service_name)
|
596
|
-
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")
|
604
|
+
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, StartName) -PassThru | ConvertTo-Json")
|
597
605
|
|
598
606
|
# cannot rely on exit code for now, successful command returns exit code 1
|
599
607
|
# return nil if cmd.exit_status != 0
|
@@ -614,6 +622,7 @@ module Inspec::Resources
|
|
614
622
|
running: service_running?(service),
|
615
623
|
enabled: service_enabled?(service),
|
616
624
|
startmode: service["WMI"]["StartMode"],
|
625
|
+
startname: service["WMI"]["StartName"],
|
617
626
|
type: "windows",
|
618
627
|
}
|
619
628
|
end
|
data/lib/inspec/resources/ssl.rb
CHANGED
@@ -6,92 +6,94 @@ require "uri"
|
|
6
6
|
require "parallel"
|
7
7
|
|
8
8
|
# Custom resource based on the InSpec resource DSL
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
module Inspec::Resources
|
10
|
+
class SSL < Inspec.resource(1)
|
11
|
+
name "ssl"
|
12
|
+
supports platform: "unix"
|
13
|
+
supports platform: "windows"
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
desc "
|
16
|
+
SSL test resource
|
17
|
+
"
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
example <<~EXAMPLE
|
20
|
+
describe ssl(port: 443) do
|
21
|
+
it { should be_enabled }
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
# protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2
|
25
|
+
describe ssl(port: 443).protocols('ssl2') do
|
26
|
+
it { should_not be_enabled }
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
# any ciphers, filter by name or regex
|
30
|
+
describe ssl(port: 443).ciphers(/rc4/i) do
|
31
|
+
it { should_not be_enabled }
|
32
|
+
end
|
33
|
+
EXAMPLE
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
VERSIONS = [
|
36
|
+
"ssl2",
|
37
|
+
"ssl3",
|
38
|
+
"tls1.0",
|
39
|
+
"tls1.1",
|
40
|
+
"tls1.2",
|
41
|
+
].freeze
|
41
42
|
|
42
|
-
|
43
|
+
attr_reader :host, :port, :timeout, :retries
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
def initialize(opts = {})
|
46
|
+
@host = opts[:host]
|
47
|
+
if @host.nil?
|
48
|
+
# Transports like SSH and WinRM will provide a hostname
|
49
|
+
if inspec.backend.respond_to?("hostname")
|
50
|
+
@host = inspec.backend.hostname
|
51
|
+
elsif inspec.backend.class.to_s == "Train::Transports::Local::Connection"
|
52
|
+
@host = "localhost"
|
53
|
+
end
|
52
54
|
end
|
55
|
+
@port = opts[:port] || 443
|
56
|
+
@timeout = opts[:timeout]
|
57
|
+
@retries = opts[:retries]
|
53
58
|
end
|
54
|
-
@port = opts[:port] || 443
|
55
|
-
@timeout = opts[:timeout]
|
56
|
-
@retries = opts[:retries]
|
57
|
-
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
filter = FilterTable.create
|
61
|
+
filter.register_custom_matcher(:enabled?) do |x|
|
62
|
+
raise "Cannot determine host for SSL test. Please specify it or use a different target." if x.resource.host.nil?
|
62
63
|
|
63
|
-
|
64
|
-
end
|
65
|
-
filter.register_column(:ciphers, field: "cipher")
|
66
|
-
.register_column(:protocols, field: "protocol")
|
67
|
-
.register_custom_property(:handshake) do |x|
|
68
|
-
groups = x.entries.group_by(&:protocol)
|
69
|
-
res = Parallel.map(groups, in_threads: 8) do |proto, e|
|
70
|
-
[proto, SSLShake.hello(x.resource.host, port: x.resource.port,
|
71
|
-
protocol: proto, ciphers: e.map(&:cipher),
|
72
|
-
timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
|
73
|
-
end
|
74
|
-
Hash[res]
|
64
|
+
x.handshake.values.any? { |i| i["success"] }
|
75
65
|
end
|
76
|
-
.
|
66
|
+
filter.register_column(:ciphers, field: "cipher")
|
67
|
+
.register_column(:protocols, field: "protocol")
|
68
|
+
.register_custom_property(:handshake) do |x|
|
69
|
+
groups = x.entries.group_by(&:protocol)
|
70
|
+
res = Parallel.map(groups, in_threads: 8) do |proto, e|
|
71
|
+
[proto, SSLShake.hello(x.resource.host, port: x.resource.port,
|
72
|
+
protocol: proto, ciphers: e.map(&:cipher),
|
73
|
+
timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
|
74
|
+
end
|
75
|
+
Hash[res]
|
76
|
+
end
|
77
|
+
.install_filter_methods_on_resource(self, :scan_config)
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
def to_s
|
80
|
+
"SSL/TLS on #{@host}:#{@port}"
|
81
|
+
end
|
81
82
|
|
82
|
-
|
83
|
+
private
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
85
|
+
def scan_config
|
86
|
+
[
|
87
|
+
{ "protocol" => "ssl2", "ciphers" => SSLShake::SSLv2::CIPHERS.keys },
|
88
|
+
{ "protocol" => "ssl3", "ciphers" => SSLShake::TLS::SSL3_CIPHERS.keys },
|
89
|
+
{ "protocol" => "tls1.0", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
|
90
|
+
{ "protocol" => "tls1.1", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
|
91
|
+
{ "protocol" => "tls1.2", "ciphers" => SSLShake::TLS::TLS_CIPHERS.keys },
|
92
|
+
].map do |line|
|
93
|
+
line["ciphers"].map do |cipher|
|
94
|
+
{ "protocol" => line["protocol"], "cipher" => cipher }
|
95
|
+
end
|
96
|
+
end.flatten
|
97
|
+
end
|
96
98
|
end
|
97
99
|
end
|