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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e1a10fa109bd6acf6791160ec84a2b00541332f
4
- data.tar.gz: 0ca5fa62d228cc13b973f272fd9ac55635cafba7
3
+ metadata.gz: e4952aa70e173665e538c1e9a462da104c533ad1
4
+ data.tar.gz: a2b79c2109ade3f8f96afbb95104a71ea9d8b16c
5
5
  SHA512:
6
- metadata.gz: 47dec2f9f7653e27b31489524ccb93ab364a8683f66ce00e7eec849a0fcc1cc0b5cbf773cb673e6ac4a532c0c3ccabffa5446c9feb50674f4cd130e46d5ccfb6
7
- data.tar.gz: 385cbd5d676ef7f3bfb5905a23da6a2359abbdf5fc025e77ebb885bb20dde32172573df4b51176919edba5425bab9c95b6b8b02188b3941dc77f9f05940b2338
6
+ metadata.gz: e5444459f580693c6cce709ffb6cb99e47d817e1ddd35064a1636a299899165da235197640edb3cc2766d5b93d8fae7194996e731303c63df69f057527e02555
7
+ data.tar.gz: c866512ba5541fcb0b4dc2fb44c47f3ea22233b9d80590894e646e654f649160fbe0042dabb48941a7b500a709ee1c0d5f73c1725806083ce85713593d0b9939
@@ -1,33 +1,50 @@
1
1
  # Change Log
2
2
  <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
3
- <!-- latest_release unreleased -->
4
- ## Unreleased
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
- #### Merged Pull Requests
7
- - Bump version manually to trigger Habitat build [#2466](https://github.com/chef/inspec/pull/2466) ([adamleff](https://github.com/adamleff))
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.49.2 -->
11
- ### Changes since 1.49.2 release
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
- - xml resource: support fetching attributes [#2423](https://github.com/chef/inspec/pull/2423) ([tarcinil](https://github.com/tarcinil)) <!-- 1.49.7 -->
20
- - mssql_session resource: add port parameter [#2429](https://github.com/chef/inspec/pull/2429) ([tarcinil](https://github.com/tarcinil)) <!-- 1.49.5 -->
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
- - Bump version manually to trigger Habitat build [#2466](https://github.com/chef/inspec/pull/2466) ([adamleff](https://github.com/adamleff)) <!-- 1.50.1 -->
24
- - Bump minor version [#2465](https://github.com/chef/inspec/pull/2465) ([adamleff](https://github.com/adamleff)) <!-- 1.50.0 -->
25
- - Bump Omnibus Ruby (and Travis Rubies) to 2.4.3 [#2452](https://github.com/chef/inspec/pull/2452) ([adamleff](https://github.com/adamleff)) <!-- 1.49.9 -->
26
- - Update the inspec support check to warn to stderr. [#2446](https://github.com/chef/inspec/pull/2446) ([jquick](https://github.com/jquick)) <!-- 1.49.4 -->
27
- - Fix package manager detection on Arch Linux [#2436](https://github.com/chef/inspec/pull/2436) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.49.3 -->
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/).
@@ -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
- ; SMTP = smtp.gmail.com
56
- ; smtp_port = 465
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>
@@ -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)
@@ -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'
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.50.1'
7
+ VERSION = '1.51.0'
8
8
  end
@@ -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 parse_containers
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
- raw_containers = inspec.command("docker ps -a --no-trunc --format '{#{format.join(', ')}}'").stdout
159
- ps = []
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
- raw_containers.each_line { |entry|
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 = j.map { |k, v|
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 = ensure_container_keys(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
- ps.push(j)
193
+ output.push(j)
178
194
  }
179
- ps
195
+ output
180
196
  rescue JSON::ParserError => _e
181
- warn 'Could not parse `docker ps` output'
197
+ warn "Could not parse `docker #{subcommand}` output"
182
198
  []
183
199
  end
184
200
 
185
- def ensure_container_keys(entry)
186
- %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status Networks LocalVolumes}.each { |key|
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 container_info.entries.length == 1
45
+ status.downcase.start_with?('up') if object_info.entries.length == 1
51
46
  end
52
47
 
53
48
  def status
54
- container_info.status[0] if container_info.entries.length == 1
49
+ object_info.status[0] if object_info.entries.length == 1
55
50
  end
56
51
 
57
52
  def labels
58
- container_info.labels[0] if container_info.entries.length == 1
53
+ object_info.labels[0] if object_info.entries.length == 1
59
54
  end
60
55
 
61
56
  def ports
62
- container_info.ports[0] if container_info.entries.length == 1
57
+ object_info.ports[0] if object_info.entries.length == 1
63
58
  end
64
59
 
65
60
  def command
66
- return unless container_info.entries.length == 1
61
+ return unless object_info.entries.length == 1
67
62
 
68
- cmd = container_info.commands[0]
63
+ cmd = object_info.commands[0]
69
64
  cmd.slice(1, cmd.length - 2)
70
65
  end
71
66
 
72
67
  def image
73
- container_info.images[0] if container_info.entries.length == 1
68
+ object_info.images[0] if object_info.entries.length == 1
74
69
  end
75
70
 
76
71
  def repo
77
- return if image.nil? || image_name_from_image.nil?
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
- return if image_name_from_image.nil?
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 image_name_from_image
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 image_info.entries.size == 1
43
+ "#{repo}:#{tag}" if object_info.entries.size == 1
48
44
  end
49
45
 
50
46
  def repo
51
- image_info.repositories[0] if image_info.entries.size == 1
47
+ object_info.repositories[0] if object_info.entries.size == 1
52
48
  end
53
49
 
54
50
  def tag
55
- image_info.tags[0] if image_info.entries.size == 1
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 image_info
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
@@ -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
- # Find all "menuentry" lines and then parse them into arrays
75
- menu_entry = 0
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
- kernel_opts = {}
78
- kernel_opts['insmod'] = []
79
- lines.each_with_index do |file_line, index|
80
- next unless file_line =~ /(^|\s)menuentry\s.*/
81
- lines.drop(index+1).each do |kernel_line|
82
- next if kernel_line =~ /(^|\s)(menu|}).*/
83
- if menu_entry == conf['GRUB_DEFAULT'].to_i && @kernel == 'default'
84
- if kernel_line =~ /(^|\s)initrd.*/
85
- kernel_opts['initrd'] = kernel_line.split(' ')[1]
86
- end
87
- if kernel_line =~ /(^|\s)linux.*/
88
- kernel_opts['kernel'] = kernel_line.split
89
- end
90
- if kernel_line =~ /(^|\s)set root=.*/
91
- kernel_opts['root'] = kernel_line.split('=')[1].tr('\'', '')
92
- end
93
- if kernel_line =~ /(^|\s)insmod.*/
94
- kernel_opts['insmod'].push(kernel_line.split(' ')[1])
95
- end
96
- else
97
- menu_entry += 1
98
- break
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
- kernel_opts
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 =~ /^\*\S/
146
- val.split(',').map { |v|
147
- v.sub('*S', 'S')
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]
@@ -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").exit_status == 0
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.50.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-17 00:00:00.000000000 Z
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