inspec 1.50.1 → 1.51.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 +35 -19
- data/docs/resources/docker_service.md.erb +107 -0
- data/docs/resources/filesystem.md.erb +39 -0
- data/docs/resources/ini.md.erb +5 -4
- data/docs/resources/pip.md.erb +7 -0
- data/lib/inspec/profile_context.rb +1 -0
- data/lib/inspec/resource.rb +2 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/docker.rb +57 -23
- data/lib/resources/docker_container.rb +14 -36
- data/lib/resources/docker_image.rb +8 -46
- data/lib/resources/docker_object.rb +57 -0
- data/lib/resources/docker_service.rb +94 -0
- data/lib/resources/filesystem.rb +31 -0
- data/lib/resources/grub_conf.rb +65 -25
- data/lib/resources/security_policy.rb +19 -4
- data/lib/resources/service.rb +11 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4952aa70e173665e538c1e9a462da104c533ad1
|
4
|
+
data.tar.gz: a2b79c2109ade3f8f96afbb95104a71ea9d8b16c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5444459f580693c6cce709ffb6cb99e47d817e1ddd35064a1636a299899165da235197640edb3cc2766d5b93d8fae7194996e731303c63df69f057527e02555
|
7
|
+
data.tar.gz: c866512ba5541fcb0b4dc2fb44c47f3ea22233b9d80590894e646e654f649160fbe0042dabb48941a7b500a709ee1c0d5f73c1725806083ce85713593d0b9939
|
data/CHANGELOG.md
CHANGED
@@ -1,33 +1,50 @@
|
|
1
1
|
# Change Log
|
2
2
|
<!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
|
3
|
-
<!-- latest_release
|
4
|
-
##
|
3
|
+
<!-- latest_release 1.51.0 -->
|
4
|
+
## [v1.51.0](https://github.com/chef/inspec/tree/v1.51.0) (2018-01-25)
|
5
5
|
|
6
|
-
####
|
7
|
-
-
|
6
|
+
#### New Resources
|
7
|
+
- filesystem resource: inspect linux filesystems [#2441](https://github.com/chef/inspec/pull/2441) ([tarcinil](https://github.com/tarcinil))
|
8
8
|
<!-- latest_release -->
|
9
9
|
|
10
|
-
<!-- release_rollup since=1.
|
11
|
-
### Changes since 1.
|
12
|
-
|
13
|
-
#### Bug Fixes
|
14
|
-
- http resource: make header keys case insensitive [#2457](https://github.com/chef/inspec/pull/2457) ([adamleff](https://github.com/adamleff)) <!-- 1.49.10 -->
|
15
|
-
- package resource: fix NilClass errors on arch linux [#2437](https://github.com/chef/inspec/pull/2437) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.49.8 -->
|
16
|
-
- firewalld resource: prepend rule string only when necessary [#2430](https://github.com/chef/inspec/pull/2430) ([tarcinil](https://github.com/tarcinil)) <!-- 1.49.6 -->
|
10
|
+
<!-- release_rollup since=1.50.1 -->
|
11
|
+
### Changes since 1.50.1 release
|
17
12
|
|
18
13
|
#### Enhancements
|
19
|
-
-
|
20
|
-
|
14
|
+
- Update security_policy resource to return Names, not SIDs [#2462](https://github.com/chef/inspec/pull/2462) ([ViolentOr](https://github.com/ViolentOr)) <!-- 1.50.5 -->
|
15
|
+
|
16
|
+
#### New Resources
|
17
|
+
- filesystem resource: inspect linux filesystems [#2441](https://github.com/chef/inspec/pull/2441) ([tarcinil](https://github.com/tarcinil)) <!-- 1.51.0 -->
|
18
|
+
- new docker_service resource to inspect Docker Swarm services [#2456](https://github.com/chef/inspec/pull/2456) ([mattlqx](https://github.com/mattlqx)) <!-- 1.50.4 -->
|
21
19
|
|
22
20
|
#### Merged Pull Requests
|
23
|
-
-
|
24
|
-
|
25
|
-
|
26
|
-
-
|
27
|
-
-
|
21
|
+
- Sort library files before loading them so load order is predictable [#2475](https://github.com/chef/inspec/pull/2475) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 1.50.3 -->
|
22
|
+
|
23
|
+
#### Bug Fixes
|
24
|
+
- service resource: attempt a SysV fallback if SystemD unit file is not found [#2473](https://github.com/chef/inspec/pull/2473) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.50.6 -->
|
25
|
+
- grub_conf resource: fix menuentry detection [#2408](https://github.com/chef/inspec/pull/2408) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.50.2 -->
|
28
26
|
<!-- release_rollup -->
|
29
27
|
|
30
28
|
<!-- latest_stable_release -->
|
29
|
+
## [v1.50.1](https://github.com/chef/inspec/tree/v1.50.1) (2018-01-17)
|
30
|
+
|
31
|
+
#### Enhancements
|
32
|
+
- mssql_session resource: add port parameter [#2429](https://github.com/chef/inspec/pull/2429) ([tarcinil](https://github.com/tarcinil))
|
33
|
+
- xml resource: support fetching attributes [#2423](https://github.com/chef/inspec/pull/2423) ([tarcinil](https://github.com/tarcinil))
|
34
|
+
|
35
|
+
#### Bug Fixes
|
36
|
+
- firewalld resource: prepend rule string only when necessary [#2430](https://github.com/chef/inspec/pull/2430) ([tarcinil](https://github.com/tarcinil))
|
37
|
+
- package resource: fix NilClass errors on arch linux [#2437](https://github.com/chef/inspec/pull/2437) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
38
|
+
- http resource: make header keys case insensitive [#2457](https://github.com/chef/inspec/pull/2457) ([adamleff](https://github.com/adamleff))
|
39
|
+
|
40
|
+
#### Merged Pull Requests
|
41
|
+
- Fix package manager detection on Arch Linux [#2436](https://github.com/chef/inspec/pull/2436) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
42
|
+
- Update the inspec support check to warn to stderr. [#2446](https://github.com/chef/inspec/pull/2446) ([jquick](https://github.com/jquick))
|
43
|
+
- Bump Omnibus Ruby (and Travis Rubies) to 2.4.3 [#2452](https://github.com/chef/inspec/pull/2452) ([adamleff](https://github.com/adamleff))
|
44
|
+
- Bump minor version [#2465](https://github.com/chef/inspec/pull/2465) ([adamleff](https://github.com/adamleff))
|
45
|
+
- Bump version manually to trigger Habitat build [#2466](https://github.com/chef/inspec/pull/2466) ([adamleff](https://github.com/adamleff))
|
46
|
+
<!-- latest_stable_release -->
|
47
|
+
|
31
48
|
## [v1.49.2](https://github.com/chef/inspec/tree/v1.49.2) (2018-01-04)
|
32
49
|
|
33
50
|
#### Enhancements
|
@@ -51,7 +68,6 @@
|
|
51
68
|
#### Merged Pull Requests
|
52
69
|
- Split unit tests from functional [#2391](https://github.com/chef/inspec/pull/2391) ([adamleff](https://github.com/adamleff))
|
53
70
|
- Bump minor version and cleanup changelog for release [#2440](https://github.com/chef/inspec/pull/2440) ([adamleff](https://github.com/adamleff))
|
54
|
-
<!-- latest_stable_release -->
|
55
71
|
|
56
72
|
## [v1.48.0](https://github.com/chef/inspec/tree/v1.48.0) (2017-12-07)
|
57
73
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
---
|
2
|
+
title: About the docker_service Resource
|
3
|
+
---
|
4
|
+
|
5
|
+
# docker_service
|
6
|
+
|
7
|
+
Use the `docker_service` InSpec audit resource to verify a docker swarm service.
|
8
|
+
|
9
|
+
<br>
|
10
|
+
|
11
|
+
## Syntax
|
12
|
+
|
13
|
+
A `docker_service` resource block declares the service by name:
|
14
|
+
|
15
|
+
describe docker_service('foo') do
|
16
|
+
it { should exist }
|
17
|
+
its('id') { should eq '2ghswegspre1' }
|
18
|
+
its('repo') { should eq 'alpine' }
|
19
|
+
its('tag') { should eq 'latest' }
|
20
|
+
end
|
21
|
+
|
22
|
+
The resource allows you to pass in a service id:
|
23
|
+
|
24
|
+
describe docker_service(id: '2ghswegspre1') do
|
25
|
+
...
|
26
|
+
end
|
27
|
+
|
28
|
+
You can also pass in the fully-qualified image:
|
29
|
+
|
30
|
+
describe docker_service(image: 'localhost:5000/alpine:latest') do
|
31
|
+
...
|
32
|
+
end
|
33
|
+
|
34
|
+
<br>
|
35
|
+
|
36
|
+
## Examples
|
37
|
+
|
38
|
+
The following examples show how to use this InSpec `docker_service` resource.
|
39
|
+
|
40
|
+
### Test a docker service
|
41
|
+
|
42
|
+
describe docker_service('foo') do
|
43
|
+
it { should exist }
|
44
|
+
its('id') { should eq '2ghswegspre1' }
|
45
|
+
its('repo') { should eq 'alpine' }
|
46
|
+
its('tag') { should eq 'latest' }
|
47
|
+
end
|
48
|
+
|
49
|
+
<br>
|
50
|
+
|
51
|
+
## Matchers
|
52
|
+
|
53
|
+
This InSpec audit resource has the following matchers. For a full list of available matchers please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
54
|
+
|
55
|
+
### exist
|
56
|
+
|
57
|
+
The `exist` matcher tests if the image is available on the node:
|
58
|
+
|
59
|
+
it { should exist }
|
60
|
+
|
61
|
+
### id
|
62
|
+
|
63
|
+
The `id` matcher returns the service id:
|
64
|
+
|
65
|
+
its('id') { should eq '2ghswegspre1' }
|
66
|
+
|
67
|
+
### image
|
68
|
+
|
69
|
+
The `image` matcher tests the value of the image. It is a combination of `repository:tag`:
|
70
|
+
|
71
|
+
its('image') { should eq 'alpine:latest' }
|
72
|
+
|
73
|
+
### mode
|
74
|
+
|
75
|
+
The `mode` matcher tests the value of the service mode:
|
76
|
+
|
77
|
+
its('mode') { should eq 'replicated' }
|
78
|
+
|
79
|
+
### name
|
80
|
+
|
81
|
+
The `name` matcher tests the value of the service name:
|
82
|
+
|
83
|
+
its('name') { should eq 'foo' }
|
84
|
+
|
85
|
+
### ports
|
86
|
+
|
87
|
+
The `ports` matcher tests the value of the service's published ports:
|
88
|
+
|
89
|
+
its('ports') { should include '*:8000->8000/tcp' }
|
90
|
+
|
91
|
+
### repo
|
92
|
+
|
93
|
+
The `repo` matcher tests the value of the repository name:
|
94
|
+
|
95
|
+
its('repo') { should eq 'alpine' }
|
96
|
+
|
97
|
+
### replicas
|
98
|
+
|
99
|
+
The `replicas` matcher tests the value of the service's replica count:
|
100
|
+
|
101
|
+
its('replicas') { should eq '3/3' }
|
102
|
+
|
103
|
+
### tag
|
104
|
+
|
105
|
+
The `tag` matcher tests the value of image tag:
|
106
|
+
|
107
|
+
its('tag') { should eq 'latest' }
|
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
title: About the filesystem Resource
|
3
|
+
---
|
4
|
+
|
5
|
+
# filesystem
|
6
|
+
|
7
|
+
Use the `filesystem` InSpec resource to audit filesystem disk space usage
|
8
|
+
<br>
|
9
|
+
|
10
|
+
## Syntax
|
11
|
+
|
12
|
+
A `filesystem` resource block declares tests for disk space in a partion:
|
13
|
+
|
14
|
+
describe filesystem('/') do
|
15
|
+
its('size') { should be >= 32000 }
|
16
|
+
end
|
17
|
+
|
18
|
+
where
|
19
|
+
|
20
|
+
* `filesystem('/')` states that it will be looking at the root (/) partition
|
21
|
+
* `size` is measured in megabytes (MB)
|
22
|
+
|
23
|
+
<br>
|
24
|
+
|
25
|
+
## Examples
|
26
|
+
|
27
|
+
The following examples show how to use this InSpec audit resource.
|
28
|
+
|
29
|
+
### Test if the root partition is greater thank 32000 MB
|
30
|
+
|
31
|
+
describe filesystem('/') do
|
32
|
+
its('size') { should be >= 32000 }
|
33
|
+
end
|
34
|
+
|
35
|
+
<br>
|
36
|
+
|
37
|
+
## Matchers
|
38
|
+
|
39
|
+
For a full list of available matchers please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
data/docs/resources/ini.md.erb
CHANGED
@@ -52,13 +52,14 @@ The following examples show how to use this InSpec audit resource.
|
|
52
52
|
|
53
53
|
For example, a PHP INI file located at contains the following settings:
|
54
54
|
|
55
|
-
|
56
|
-
|
55
|
+
[mail function]
|
56
|
+
SMTP = smtp.gmail.com
|
57
|
+
smtp_port = 465
|
57
58
|
|
58
59
|
and can be tested like this:
|
59
60
|
|
60
|
-
describe ini(/etc/php5/apache2/php.ini) do
|
61
|
-
its('smtp_port') { should eq('465') }
|
61
|
+
describe ini('/etc/php5/apache2/php.ini') do
|
62
|
+
its('mail function.smtp_port') { should eq('465') }
|
62
63
|
end
|
63
64
|
|
64
65
|
<br>
|
data/docs/resources/pip.md.erb
CHANGED
@@ -40,6 +40,13 @@ The following examples show how to use this InSpec audit resource.
|
|
40
40
|
its('version') { should eq '2.8' }
|
41
41
|
end
|
42
42
|
|
43
|
+
### Test packages installed into a non-default location (e.g. virtualenv) by passing a custom path to pip executable
|
44
|
+
|
45
|
+
describe pip('Jinja2', '/path/to/bin/pip') do
|
46
|
+
it { should be_installed }
|
47
|
+
its('version') { should eq '2.8' }
|
48
|
+
end
|
49
|
+
|
43
50
|
<br>
|
44
51
|
|
45
52
|
## Matchers
|
@@ -116,6 +116,7 @@ module Inspec
|
|
116
116
|
lib_prefix = 'libraries' + File::SEPARATOR
|
117
117
|
autoloads = []
|
118
118
|
|
119
|
+
libs.sort_by! { |l| l[1] } # Sort on source path so load order is deterministic
|
119
120
|
libs.each do |content, source, line|
|
120
121
|
path = source
|
121
122
|
if source.start_with?(lib_prefix)
|
data/lib/inspec/resource.rb
CHANGED
@@ -94,12 +94,14 @@ require 'resources/directory'
|
|
94
94
|
require 'resources/docker'
|
95
95
|
require 'resources/docker_container'
|
96
96
|
require 'resources/docker_image'
|
97
|
+
require 'resources/docker_service'
|
97
98
|
require 'resources/elasticsearch'
|
98
99
|
require 'resources/etc_fstab'
|
99
100
|
require 'resources/etc_group'
|
100
101
|
require 'resources/etc_hosts_allow_deny'
|
101
102
|
require 'resources/etc_hosts'
|
102
103
|
require 'resources/file'
|
104
|
+
require 'resources/filesystem'
|
103
105
|
require 'resources/firewalld'
|
104
106
|
require 'resources/gem'
|
105
107
|
require 'resources/groups'
|
data/lib/inspec/version.rb
CHANGED
data/lib/resources/docker.rb
CHANGED
@@ -59,6 +59,25 @@ module Inspec::Resources
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
class DockerServiceFilter
|
63
|
+
filter = FilterTable.create
|
64
|
+
filter.add_accessor(:where)
|
65
|
+
.add_accessor(:entries)
|
66
|
+
.add(:ids, field: 'id')
|
67
|
+
.add(:names, field: 'name')
|
68
|
+
.add(:modes, field: 'mode')
|
69
|
+
.add(:replicas, field: 'replicas')
|
70
|
+
.add(:images, field: 'image')
|
71
|
+
.add(:ports, field: 'ports')
|
72
|
+
.add(:exists?) { |x| !x.entries.empty? }
|
73
|
+
filter.connect(self, :services)
|
74
|
+
|
75
|
+
attr_reader :services
|
76
|
+
def initialize(services)
|
77
|
+
@services = services
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
62
81
|
# This resource helps to parse information from the docker host
|
63
82
|
# For compatability with Serverspec we also offer the following resouses:
|
64
83
|
# - docker_container
|
@@ -79,6 +98,10 @@ module Inspec::Resources
|
|
79
98
|
its('repositories') { should_not include 'inssecure_image' }
|
80
99
|
end
|
81
100
|
|
101
|
+
describe docker.services do
|
102
|
+
its('images') { should_not include 'inssecure_image' }
|
103
|
+
end
|
104
|
+
|
82
105
|
describe docker.version do
|
83
106
|
its('Server.Version') { should cmp >= '1.12'}
|
84
107
|
its('Client.Version') { should cmp >= '1.12'}
|
@@ -105,6 +128,10 @@ module Inspec::Resources
|
|
105
128
|
DockerImageFilter.new(parse_images)
|
106
129
|
end
|
107
130
|
|
131
|
+
def services
|
132
|
+
DockerServiceFilter.new(parse_services)
|
133
|
+
end
|
134
|
+
|
108
135
|
def version
|
109
136
|
return @version if defined?(@version)
|
110
137
|
data = {}
|
@@ -142,48 +169,55 @@ module Inspec::Resources
|
|
142
169
|
|
143
170
|
private
|
144
171
|
|
145
|
-
def
|
146
|
-
# @see https://github.com/moby/moby/issues/20625, works for docker 1.13+
|
147
|
-
# raw_containers = inspec.command('docker ps -a --no-trunc --format \'{{ json . }}\'').stdout
|
148
|
-
# therefore we stick with older approach
|
149
|
-
labels = %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status}
|
150
|
-
|
151
|
-
# Networks LocalVolumes work with 1.13+ only
|
152
|
-
if !version.empty? && Gem::Version.new(version['Client']['Version']) >= Gem::Version.new('1.13')
|
153
|
-
labels.push('Networks')
|
154
|
-
labels.push('LocalVolumes')
|
155
|
-
end
|
172
|
+
def parse_json_command(labels, subcommand)
|
156
173
|
# build command
|
157
174
|
format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" }
|
158
|
-
|
159
|
-
|
175
|
+
raw = inspec.command("docker #{subcommand} --format '{#{format.join(', ')}}'").stdout
|
176
|
+
output = []
|
160
177
|
# since docker is not outputting valid json, we need to parse each row
|
161
|
-
|
162
|
-
j = JSON.parse(entry)
|
178
|
+
raw.each_line { |entry|
|
163
179
|
# convert all keys to lower_case to work well with ruby and filter table
|
164
|
-
j =
|
180
|
+
j = JSON.parse(entry).map { |k, v|
|
165
181
|
[k.downcase, v]
|
166
182
|
}.to_h
|
167
183
|
|
168
184
|
# ensure all keys are there
|
169
|
-
j =
|
185
|
+
j = ensure_keys(j, labels)
|
170
186
|
|
171
187
|
# strip off any linked container names
|
172
188
|
# Depending on how it was linked, the actual container name may come before
|
173
189
|
# or after the link information, so we'll just look for the first name that
|
174
190
|
# does not include a slash since that is not a valid character in a container name
|
175
|
-
j['names'] = j['names'].split(',').find { |c| !c.include?('/') }
|
191
|
+
j['names'] = j['names'].split(',').find { |c| !c.include?('/') } if j.key?('names')
|
176
192
|
|
177
|
-
|
193
|
+
output.push(j)
|
178
194
|
}
|
179
|
-
|
195
|
+
output
|
180
196
|
rescue JSON::ParserError => _e
|
181
|
-
warn
|
197
|
+
warn "Could not parse `docker #{subcommand}` output"
|
182
198
|
[]
|
183
199
|
end
|
184
200
|
|
185
|
-
def
|
186
|
-
|
201
|
+
def parse_containers
|
202
|
+
# @see https://github.com/moby/moby/issues/20625, works for docker 1.13+
|
203
|
+
# raw_containers = inspec.command('docker ps -a --no-trunc --format \'{{ json . }}\'').stdout
|
204
|
+
# therefore we stick with older approach
|
205
|
+
labels = %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status}
|
206
|
+
|
207
|
+
# Networks LocalVolumes work with 1.13+ only
|
208
|
+
if !version.empty? && Gem::Version.new(version['Client']['Version']) >= Gem::Version.new('1.13')
|
209
|
+
labels.push('Networks')
|
210
|
+
labels.push('LocalVolumes')
|
211
|
+
end
|
212
|
+
parse_json_command(labels, 'ps -a --no-trunc')
|
213
|
+
end
|
214
|
+
|
215
|
+
def parse_services
|
216
|
+
parse_json_command(%w{ID Name Mode Replicas Image Ports}, 'service ls')
|
217
|
+
end
|
218
|
+
|
219
|
+
def ensure_keys(entry, labels)
|
220
|
+
labels.each { |key|
|
187
221
|
entry[key.downcase] = nil if !entry.key?(key.downcase)
|
188
222
|
}
|
189
223
|
entry
|
@@ -6,8 +6,12 @@
|
|
6
6
|
# author: Patrick Muench
|
7
7
|
# author: Dominik Richter
|
8
8
|
|
9
|
+
require_relative 'docker_object'
|
10
|
+
|
9
11
|
module Inspec::Resources
|
10
12
|
class DockerContainer < Inspec.resource(1)
|
13
|
+
include Inspec::Resources::DockerObject
|
14
|
+
|
11
15
|
name 'docker_container'
|
12
16
|
desc ''
|
13
17
|
example "
|
@@ -37,55 +41,39 @@ module Inspec::Resources
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def exist?
|
41
|
-
container_info.exists?
|
42
|
-
end
|
43
|
-
|
44
|
-
# is allways returning the full id
|
45
|
-
def id
|
46
|
-
container_info.ids[0] if container_info.entries.length == 1
|
47
|
-
end
|
48
|
-
|
49
44
|
def running?
|
50
|
-
status.downcase.start_with?('up') if
|
45
|
+
status.downcase.start_with?('up') if object_info.entries.length == 1
|
51
46
|
end
|
52
47
|
|
53
48
|
def status
|
54
|
-
|
49
|
+
object_info.status[0] if object_info.entries.length == 1
|
55
50
|
end
|
56
51
|
|
57
52
|
def labels
|
58
|
-
|
53
|
+
object_info.labels[0] if object_info.entries.length == 1
|
59
54
|
end
|
60
55
|
|
61
56
|
def ports
|
62
|
-
|
57
|
+
object_info.ports[0] if object_info.entries.length == 1
|
63
58
|
end
|
64
59
|
|
65
60
|
def command
|
66
|
-
return unless
|
61
|
+
return unless object_info.entries.length == 1
|
67
62
|
|
68
|
-
cmd =
|
63
|
+
cmd = object_info.commands[0]
|
69
64
|
cmd.slice(1, cmd.length - 2)
|
70
65
|
end
|
71
66
|
|
72
67
|
def image
|
73
|
-
|
68
|
+
object_info.images[0] if object_info.entries.length == 1
|
74
69
|
end
|
75
70
|
|
76
71
|
def repo
|
77
|
-
|
78
|
-
if image.include?('/') # host:port/ubuntu:latest
|
79
|
-
repo_part, image_part = image.split('/') # host:port, ubuntu:latest
|
80
|
-
repo_part + '/' + image_part.split(':')[0] # host:port + / + ubuntu
|
81
|
-
else
|
82
|
-
image_name_from_image.split(':')[0]
|
83
|
-
end
|
72
|
+
parse_components_from_image(image)[:repo] if object_info.entries.size == 1
|
84
73
|
end
|
85
74
|
|
86
75
|
def tag
|
87
|
-
|
88
|
-
image_name_from_image.split(':')[1]
|
76
|
+
parse_components_from_image(image)[:tag] if object_info.entries.size == 1
|
89
77
|
end
|
90
78
|
|
91
79
|
def to_s
|
@@ -95,17 +83,7 @@ module Inspec::Resources
|
|
95
83
|
|
96
84
|
private
|
97
85
|
|
98
|
-
def
|
99
|
-
return if image.nil?
|
100
|
-
# possible image names include:
|
101
|
-
# alpine
|
102
|
-
# ubuntu:14.04
|
103
|
-
# repo.example.com:5000/ubuntu
|
104
|
-
# repo.example.com:5000/ubuntu:1404
|
105
|
-
image.include?('/') ? image.split('/')[1] : image
|
106
|
-
end
|
107
|
-
|
108
|
-
def container_info
|
86
|
+
def object_info
|
109
87
|
return @info if defined?(@info)
|
110
88
|
opts = @opts
|
111
89
|
@info = inspec.docker.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
|
@@ -6,8 +6,12 @@
|
|
6
6
|
# author: Patrick Muench
|
7
7
|
# author: Dominik Richter
|
8
8
|
|
9
|
+
require_relative 'docker_object'
|
10
|
+
|
9
11
|
module Inspec::Resources
|
10
12
|
class DockerImage < Inspec.resource(1)
|
13
|
+
include Inspec::Resources::DockerObject
|
14
|
+
|
11
15
|
name 'docker_image'
|
12
16
|
desc ''
|
13
17
|
example "
|
@@ -35,24 +39,16 @@ module Inspec::Resources
|
|
35
39
|
@opts = sanitize_options(o)
|
36
40
|
end
|
37
41
|
|
38
|
-
def exist?
|
39
|
-
image_info.exists?
|
40
|
-
end
|
41
|
-
|
42
|
-
def id
|
43
|
-
image_info.ids[0] if image_info.entries.size == 1
|
44
|
-
end
|
45
|
-
|
46
42
|
def image
|
47
|
-
"#{repo}:#{tag}" if
|
43
|
+
"#{repo}:#{tag}" if object_info.entries.size == 1
|
48
44
|
end
|
49
45
|
|
50
46
|
def repo
|
51
|
-
|
47
|
+
object_info.repositories[0] if object_info.entries.size == 1
|
52
48
|
end
|
53
49
|
|
54
50
|
def tag
|
55
|
-
|
51
|
+
object_info.tags[0] if object_info.entries.size == 1
|
56
52
|
end
|
57
53
|
|
58
54
|
def to_s
|
@@ -79,46 +75,12 @@ module Inspec::Resources
|
|
79
75
|
opts
|
80
76
|
end
|
81
77
|
|
82
|
-
def
|
78
|
+
def object_info
|
83
79
|
return @info if defined?(@info)
|
84
80
|
opts = @opts
|
85
81
|
@info = inspec.docker.images.where {
|
86
82
|
(repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
|
87
83
|
}
|
88
84
|
end
|
89
|
-
|
90
|
-
def parse_components_from_image(image_string)
|
91
|
-
# if the user did not supply an image string, they likely supplied individual
|
92
|
-
# option parameters, such as repo and tag. Return empty data back to the caller.
|
93
|
-
return {} if image_string.nil?
|
94
|
-
|
95
|
-
first_colon = image_string.index(':') || -1
|
96
|
-
first_slash = image_string.index('/') || -1
|
97
|
-
|
98
|
-
if image_string.count(':') == 2
|
99
|
-
# If there are two colons in the image string, it contains a repo-with-port and a tag.
|
100
|
-
# example: localhost:5000/chef/inspec:1.46.3
|
101
|
-
partitioned_string = image_string.rpartition(':')
|
102
|
-
repo = partitioned_string.first
|
103
|
-
tag = partitioned_string.last
|
104
|
-
elsif image_string.count(':') == 1 && first_colon < first_slash
|
105
|
-
# If there's one colon in the image string, and it comes before a forward-slash,
|
106
|
-
# it contains a repo-with-port but no tag.
|
107
|
-
# example: localhost:5000/ubuntu
|
108
|
-
repo = image_string
|
109
|
-
tag = nil
|
110
|
-
else
|
111
|
-
# If there's one colon in the image string and it doesn't preceed a slash, or if
|
112
|
-
# there is no colon at all, then it separates the repo from the tag, if there is a tag.
|
113
|
-
# example: chef/inspec:1.46.3
|
114
|
-
# example: chef/inspec
|
115
|
-
# example: ubuntu:14.04
|
116
|
-
repo, tag = image_string.split(':')
|
117
|
-
end
|
118
|
-
|
119
|
-
# return the repo and tag parsed from the string, which can be merged into
|
120
|
-
# the rest of the user-supplied options
|
121
|
-
{ repo: repo, tag: tag }
|
122
|
-
end
|
123
85
|
end
|
124
86
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright 2017, Christoph Hartmann
|
4
|
+
#
|
5
|
+
# author: Christoph Hartmann
|
6
|
+
# author: Patrick Muench
|
7
|
+
# author: Dominik Richter
|
8
|
+
# author: Matt Kulka
|
9
|
+
|
10
|
+
module Inspec::Resources::DockerObject
|
11
|
+
def exist?
|
12
|
+
object_info.exists?
|
13
|
+
end
|
14
|
+
|
15
|
+
def id
|
16
|
+
object_info.ids[0] if object_info.entries.size == 1
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_components_from_image(image_string)
|
22
|
+
# if the user did not supply an image string, they likely supplied individual
|
23
|
+
# option parameters, such as repo and tag. Return empty data back to the caller.
|
24
|
+
return {} if image_string.nil?
|
25
|
+
|
26
|
+
first_colon = image_string.index(':') || -1
|
27
|
+
first_slash = image_string.index('/') || -1
|
28
|
+
|
29
|
+
if image_string.count(':') == 2
|
30
|
+
# If there are two colons in the image string, it contains a repo-with-port and a tag.
|
31
|
+
# example: localhost:5000/chef/inspec:1.46.3
|
32
|
+
partitioned_string = image_string.rpartition(':')
|
33
|
+
repo = partitioned_string.first
|
34
|
+
tag = partitioned_string.last
|
35
|
+
image_name = repo.split('/')[1..-1].join
|
36
|
+
elsif image_string.count(':') == 1 && first_colon < first_slash
|
37
|
+
# If there's one colon in the image string, and it comes before a forward-slash,
|
38
|
+
# it contains a repo-with-port but no tag.
|
39
|
+
# example: localhost:5000/ubuntu
|
40
|
+
repo = image_string
|
41
|
+
tag = nil
|
42
|
+
image_name = repo.split('/')[1..-1].join
|
43
|
+
else
|
44
|
+
# If there's one colon in the image string and it doesn't preceed a slash, or if
|
45
|
+
# there is no colon at all, then it separates the repo from the tag, if there is a tag.
|
46
|
+
# example: chef/inspec:1.46.3
|
47
|
+
# example: chef/inspec
|
48
|
+
# example: ubuntu:14.04
|
49
|
+
repo, tag = image_string.split(':')
|
50
|
+
image_name = repo
|
51
|
+
end
|
52
|
+
|
53
|
+
# return the repo, image_name and tag parsed from the string, which can be merged into
|
54
|
+
# the rest of the user-supplied options
|
55
|
+
{ repo: repo, image_name: image_name, tag: tag }
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright 2017, Christoph Hartmann
|
4
|
+
#
|
5
|
+
# author: Christoph Hartmann
|
6
|
+
# author: Patrick Muench
|
7
|
+
# author: Dominik Richter
|
8
|
+
# author: Matt Kulka
|
9
|
+
|
10
|
+
require_relative 'docker_object'
|
11
|
+
|
12
|
+
module Inspec::Resources
|
13
|
+
class DockerService < Inspec.resource(1)
|
14
|
+
include Inspec::Resources::DockerObject
|
15
|
+
|
16
|
+
name 'docker_service'
|
17
|
+
desc 'Swarm-mode service'
|
18
|
+
example "
|
19
|
+
describe docker_service('service1') do
|
20
|
+
it { should exist }
|
21
|
+
its('id') { should_not eq '' }
|
22
|
+
its('image') { should eq 'alpine:latest' }
|
23
|
+
its('repo') { should eq 'alpine' }
|
24
|
+
its('tag') { should eq 'latest' }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe docker_service(id: '4a415e366388') do
|
28
|
+
it { should exist }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe docker_service(image: 'alpine:latest') do
|
32
|
+
it { should exist }
|
33
|
+
end
|
34
|
+
"
|
35
|
+
|
36
|
+
def initialize(opts = {})
|
37
|
+
# do sanitizion of input values
|
38
|
+
o = opts.dup
|
39
|
+
o = { name: opts } if opts.is_a?(String)
|
40
|
+
@opts = sanitize_options(o)
|
41
|
+
end
|
42
|
+
|
43
|
+
def name
|
44
|
+
object_info.names[0] if object_info.entries.size == 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def image
|
48
|
+
object_info.images[0] if object_info.entries.size == 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def image_name
|
52
|
+
parse_components_from_image(image)[:image_name] if object_info.entries.size == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
def repo
|
56
|
+
parse_components_from_image(image)[:repo] if object_info.entries.size == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def tag
|
60
|
+
parse_components_from_image(image)[:tag] if object_info.entries.size == 1
|
61
|
+
end
|
62
|
+
|
63
|
+
def mode
|
64
|
+
object_info.modes[0] if object_info.entries.size == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def replicas
|
68
|
+
object_info.replicas[0] if object_info.entries.size == 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def ports
|
72
|
+
object_info.ports[0] if object_info.entries.size == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
service = @opts[:name] || @opts[:id]
|
77
|
+
"Docker Service #{service}"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def sanitize_options(opts)
|
83
|
+
opts.merge(parse_components_from_image(opts[:image]))
|
84
|
+
end
|
85
|
+
|
86
|
+
def object_info
|
87
|
+
return @info if defined?(@info)
|
88
|
+
opts = @opts
|
89
|
+
@info = inspec.docker.services.where {
|
90
|
+
name == opts[:name] || image == opts[:image] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class FileSystemResource < Inspec.resource(1)
|
3
|
+
name 'filesystem'
|
4
|
+
supports os_family: 'linux'
|
5
|
+
desc 'Use the filesystem InSpec resource to test file system'
|
6
|
+
example "
|
7
|
+
describe filesystem('/') do
|
8
|
+
its('size') { should be >= 32000 }
|
9
|
+
end
|
10
|
+
"
|
11
|
+
attr_reader :partition
|
12
|
+
|
13
|
+
def initialize(partition)
|
14
|
+
@partition = partition
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
@size ||= begin
|
19
|
+
cmd = inspec.command("df #{partition} --output=size")
|
20
|
+
raise Inspec::Exceptions::ResourceFailed, "Unable to get available space for partition #{partition}" if cmd.stdout.nil? || cmd.stdout.empty? || !cmd.exit_status.zero?
|
21
|
+
|
22
|
+
value = cmd.stdout.gsub(/\dK-blocks[\r\n]/, '').strip
|
23
|
+
value.to_i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"Filesystem #{partition}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/resources/grub_conf.rb
CHANGED
@@ -36,6 +36,7 @@ class GrubConfig < Inspec.resource(1)
|
|
36
36
|
elsif os.debian?
|
37
37
|
@conf_path = path || '/boot/grub/grub.cfg'
|
38
38
|
@defaults_path = '/etc/default/grub'
|
39
|
+
@grubenv_path = '/boot/grub2/grubenv'
|
39
40
|
@version = 'grub2'
|
40
41
|
elsif os[:name] == 'amazon'
|
41
42
|
@conf_path = path || '/etc/grub.conf'
|
@@ -52,6 +53,7 @@ class GrubConfig < Inspec.resource(1)
|
|
52
53
|
else
|
53
54
|
@conf_path = path || '/boot/grub2/grub.cfg'
|
54
55
|
@defaults_path = '/etc/default/grub'
|
56
|
+
@grubenv_path = '/boot/grub2/grubenv'
|
55
57
|
@version = 'grub2'
|
56
58
|
end
|
57
59
|
end
|
@@ -71,35 +73,73 @@ class GrubConfig < Inspec.resource(1)
|
|
71
73
|
######################################################################
|
72
74
|
|
73
75
|
def grub2_parse_kernel_lines(content, conf)
|
74
|
-
|
75
|
-
|
76
|
+
menu_entries = extract_menu_entries(content)
|
77
|
+
|
78
|
+
if @kernel == 'default'
|
79
|
+
default_menu_entry(menu_entries, conf['GRUB_DEFAULT'])
|
80
|
+
else
|
81
|
+
menu_entries.find { |entry| entry['name'] == @kernel }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def extract_menu_entries(content)
|
86
|
+
menu_entries = []
|
87
|
+
|
76
88
|
lines = content.split("\n")
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
89
|
+
lines.each_with_index do |line, index|
|
90
|
+
next unless line =~ /^menuentry\s+.*/
|
91
|
+
entry = {}
|
92
|
+
entry['insmod'] = []
|
93
|
+
|
94
|
+
# Extract name from menuentry line
|
95
|
+
capture_data = line.match(/(?:^|\s+).*menuentry\s*['|"](.*)['|"]\s*--/)
|
96
|
+
if capture_data.nil? || capture_data.captures[0].nil?
|
97
|
+
raise Inspec::Exceptions::ResourceFailed "Failed to extract menuentry name from #{line}"
|
98
|
+
end
|
99
|
+
|
100
|
+
entry['name'] = capture_data.captures[0]
|
101
|
+
|
102
|
+
# Begin processing from index forward until a `}` line is met
|
103
|
+
lines.drop(index+1).each do |mline|
|
104
|
+
break if mline =~ /^\s*}\s*$/
|
105
|
+
case mline
|
106
|
+
when /(?:^|\s*)initrd.*/
|
107
|
+
entry['initrd'] = mline.split(' ')[1]
|
108
|
+
when /(?:^|\s*)linux.*/
|
109
|
+
entry['kernel'] = mline.split
|
110
|
+
when /(?:^|\s*)set root=.*/
|
111
|
+
entry['root'] = mline.split('=')[1].tr('\'', '')
|
112
|
+
when /(?:^|\s*)insmod.*/
|
113
|
+
entry['insmod'] << mline.split(' ')[1]
|
99
114
|
end
|
100
115
|
end
|
116
|
+
|
117
|
+
menu_entries << entry
|
101
118
|
end
|
102
|
-
|
119
|
+
|
120
|
+
menu_entries
|
121
|
+
end
|
122
|
+
|
123
|
+
def default_menu_entry(menu_entries, default)
|
124
|
+
# If the default entry isn't `saved` then a number is used as an index.
|
125
|
+
# By default this is `0`, which would be the first item in the list.
|
126
|
+
return menu_entries[default.to_i] unless default == 'saved'
|
127
|
+
|
128
|
+
grubenv_contents = inspec.file(@grubenv_path).content
|
129
|
+
|
130
|
+
# The location of the grubenv file is not guaranteed. In the case that
|
131
|
+
# the file does not exist this will return the 0th entry. This will also
|
132
|
+
# return the 0th entry if InSpec lacks permission to read the file. Both
|
133
|
+
# of these reflect the default Grub2 behavior.
|
134
|
+
return menu_entries[0] if grubenv_contents.nil?
|
135
|
+
|
136
|
+
default_name = SimpleConfig.new(grubenv_contents).params['saved_entry']
|
137
|
+
default_entry = menu_entries.select { |k| k['name'] == default_name }[0]
|
138
|
+
return default_entry unless default_entry.nil?
|
139
|
+
|
140
|
+
# It is possible for the saved entry to not be valid . For example, grubenv
|
141
|
+
# not being up to date. If so, the 0th entry is the default.
|
142
|
+
menu_entries[0]
|
103
143
|
end
|
104
144
|
|
105
145
|
###################################################################
|
@@ -74,8 +74,16 @@ module Inspec::Resources
|
|
74
74
|
describe security_policy do
|
75
75
|
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
|
76
76
|
end
|
77
|
+
|
78
|
+
describe security_policy(translate_sid: true) do
|
79
|
+
its('SeNetworkLogonRight') { should include 'NT AUTHORITY\\Authenticated Users' }
|
80
|
+
end
|
77
81
|
"
|
78
82
|
|
83
|
+
def initialize(opts = {})
|
84
|
+
@translate_sid = opts[:translate_sid] || false
|
85
|
+
end
|
86
|
+
|
79
87
|
def content
|
80
88
|
read_content
|
81
89
|
end
|
@@ -142,10 +150,17 @@ module Inspec::Resources
|
|
142
150
|
if val =~ /^\d+$/
|
143
151
|
val.to_i
|
144
152
|
# special handling for SID array
|
145
|
-
elsif val =~
|
146
|
-
|
147
|
-
|
148
|
-
|
153
|
+
elsif val =~ /[,]{0,1}\*\S/
|
154
|
+
if @translate_sid
|
155
|
+
val.split(',').map { |v|
|
156
|
+
object_name = inspec.command("(New-Object System.Security.Principal.SecurityIdentifier(\"#{v.sub('*S', 'S')}\")).Translate( [System.Security.Principal.NTAccount]).Value").stdout.to_s.strip
|
157
|
+
object_name.empty? || object_name.nil? ? v.sub('*S', 'S') : object_name
|
158
|
+
}
|
159
|
+
else
|
160
|
+
val.split(',').map { |v|
|
161
|
+
v.sub('*S', 'S')
|
162
|
+
}
|
163
|
+
end
|
149
164
|
# special handling for string values with "
|
150
165
|
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
151
166
|
m[1]
|
data/lib/resources/service.rb
CHANGED
@@ -250,7 +250,17 @@ module Inspec::Resources
|
|
250
250
|
end
|
251
251
|
|
252
252
|
def is_enabled?(service_name)
|
253
|
-
inspec.command("#{service_ctl} is-enabled #{service_name} --quiet")
|
253
|
+
result = inspec.command("#{service_ctl} is-enabled #{service_name} --quiet")
|
254
|
+
return true if result.exit_status == 0
|
255
|
+
|
256
|
+
# Some systems may not have a `.service` file for a particular service
|
257
|
+
# which causes the `systemctl is-enabled` check to fail despite the
|
258
|
+
# service being enabled. In that event we fallback to `sysv_service`.
|
259
|
+
if result.stderr =~ /Failed to get.*No such file or directory/
|
260
|
+
return inspec.sysv_service(service_name).enabled?
|
261
|
+
end
|
262
|
+
|
263
|
+
false
|
254
264
|
end
|
255
265
|
|
256
266
|
def is_active?(service_name)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.51.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train
|
@@ -319,6 +319,7 @@ files:
|
|
319
319
|
- docs/resources/docker.md.erb
|
320
320
|
- docs/resources/docker_container.md.erb
|
321
321
|
- docs/resources/docker_image.md.erb
|
322
|
+
- docs/resources/docker_service.md.erb
|
322
323
|
- docs/resources/elasticsearch.md.erb
|
323
324
|
- docs/resources/etc_fstab.md.erb
|
324
325
|
- docs/resources/etc_group.md.erb
|
@@ -326,6 +327,7 @@ files:
|
|
326
327
|
- docs/resources/etc_hosts_allow.md.erb
|
327
328
|
- docs/resources/etc_hosts_deny.md.erb
|
328
329
|
- docs/resources/file.md.erb
|
330
|
+
- docs/resources/filesystem.md.erb
|
329
331
|
- docs/resources/firewalld.md.erb
|
330
332
|
- docs/resources/gem.md.erb
|
331
333
|
- docs/resources/group.md.erb
|
@@ -571,12 +573,15 @@ files:
|
|
571
573
|
- lib/resources/docker.rb
|
572
574
|
- lib/resources/docker_container.rb
|
573
575
|
- lib/resources/docker_image.rb
|
576
|
+
- lib/resources/docker_object.rb
|
577
|
+
- lib/resources/docker_service.rb
|
574
578
|
- lib/resources/elasticsearch.rb
|
575
579
|
- lib/resources/etc_fstab.rb
|
576
580
|
- lib/resources/etc_group.rb
|
577
581
|
- lib/resources/etc_hosts.rb
|
578
582
|
- lib/resources/etc_hosts_allow_deny.rb
|
579
583
|
- lib/resources/file.rb
|
584
|
+
- lib/resources/filesystem.rb
|
580
585
|
- lib/resources/firewalld.rb
|
581
586
|
- lib/resources/gem.rb
|
582
587
|
- lib/resources/groups.rb
|