inspec-core 2.2.102 → 2.2.112
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -7
- data/docs/profiles.md +9 -0
- data/docs/resources/gem.md.erb +24 -5
- data/docs/resources/mssql_session.md.erb +8 -0
- data/lib/inspec/plugin/v2/loader.rb +33 -7
- data/lib/inspec/reporters/json_automate.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/README.md +16 -0
- data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +162 -0
- data/lib/plugins/inspec-artifact/lib/inspec-artifact/cli.rb +114 -0
- data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +12 -0
- data/lib/plugins/inspec-artifact/test/functional/inspec_artifact_test.rb +46 -0
- data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +39 -0
- data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +394 -0
- data/lib/plugins/inspec-habitat/lib/inspec-habitat.rb +11 -0
- data/lib/plugins/inspec-habitat/test/unit/profile_test.rb +184 -0
- data/lib/{bundles → plugins}/inspec-init/README.md +0 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +28 -0
- data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +81 -0
- data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/README.md +0 -0
- data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/controls/example.rb +0 -0
- data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/inspec.yml +0 -0
- data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/libraries/.gitkeep +0 -0
- data/lib/plugins/inspec-init/lib/inspec-init.rb +12 -0
- data/lib/plugins/inspec-init/test/functional/inspec_init_test.rb +30 -0
- data/lib/plugins/shared/core_plugin_test_helper.rb +50 -0
- data/lib/resources/gem.rb +7 -1
- data/lib/resources/mssql_session.rb +4 -2
- metadata +21 -17
- data/lib/bundles/inspec-artifact/README.md +0 -1
- data/lib/bundles/inspec-artifact/cli.rb +0 -278
- data/lib/bundles/inspec-artifact.rb +0 -7
- data/lib/bundles/inspec-habitat/cli.rb +0 -37
- data/lib/bundles/inspec-habitat/log.rb +0 -10
- data/lib/bundles/inspec-habitat/profile.rb +0 -391
- data/lib/bundles/inspec-habitat.rb +0 -12
- data/lib/bundles/inspec-init/cli.rb +0 -39
- data/lib/bundles/inspec-init/renderer.rb +0 -79
- data/lib/bundles/inspec-init.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08e8c25f0f329b43923160911ebafc36bebe17e7f7a09d5f4254a3534849429e'
|
4
|
+
data.tar.gz: 9238855161cf160af08a51119847757864374c75f6dfcbef2a2abec6e8033358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 648d001c2eea839d426543d34f80da62928abe93a9ffbf6de4838a1e100b4aaa16475dc16bf93fb647f8f0e41f2db02eab834678a0f88c628d358dfdfbd6bbf8
|
7
|
+
data.tar.gz: c170162f6a199addb3fb8595e6e265a2e14efb715ec8eb890cc827cbe248d07bd516b83f81b0f431b28e18f1a645a1586cabf7d4177043fa7e55339bdd8c543d
|
data/CHANGELOG.md
CHANGED
@@ -1,20 +1,39 @@
|
|
1
1
|
# Change Log
|
2
2
|
<!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
|
3
|
-
<!-- latest_release 2.2.
|
4
|
-
## [v2.2.
|
3
|
+
<!-- latest_release 2.2.112 -->
|
4
|
+
## [v2.2.112](https://github.com/inspec/inspec/tree/v2.2.112) (2018-09-19)
|
5
5
|
|
6
6
|
#### Merged Pull Requests
|
7
|
-
-
|
7
|
+
- Move artifact to v2 plugin [#3406](https://github.com/inspec/inspec/pull/3406) ([jquick](https://github.com/jquick))
|
8
8
|
<!-- latest_release -->
|
9
9
|
|
10
|
-
<!-- release_rollup since=2.2.
|
11
|
-
### Changes since 2.2.
|
10
|
+
<!-- release_rollup since=2.2.102 -->
|
11
|
+
### Changes since 2.2.102 release
|
12
|
+
|
13
|
+
#### Enhancements
|
14
|
+
- adding `versions` to the `gem` resource [#3398](https://github.com/inspec/inspec/pull/3398) ([majormoses](https://github.com/majormoses)) <!-- 2.2.107 -->
|
15
|
+
- Plugins: Add support for 'bundles' migration [#3384](https://github.com/inspec/inspec/pull/3384) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 2.2.105 -->
|
16
|
+
|
17
|
+
#### New Features
|
18
|
+
- Update AWS Security Group to work with IPV6 rules. [#3394](https://github.com/inspec/inspec/pull/3394) ([MartinLogan](https://github.com/MartinLogan)) <!-- 2.2.111 -->
|
19
|
+
- Added db_name flag [#3383](https://github.com/inspec/inspec/pull/3383) ([kdoores](https://github.com/kdoores)) <!-- 2.2.104 -->
|
12
20
|
|
13
21
|
#### Merged Pull Requests
|
14
|
-
-
|
22
|
+
- Move artifact to v2 plugin [#3406](https://github.com/inspec/inspec/pull/3406) ([jquick](https://github.com/jquick)) <!-- 2.2.112 -->
|
23
|
+
- Move inspec init to v2 plugins [#3407](https://github.com/inspec/inspec/pull/3407) ([jquick](https://github.com/jquick)) <!-- 2.2.110 -->
|
24
|
+
- Fix gem tests from recent merge [#3409](https://github.com/inspec/inspec/pull/3409) ([jquick](https://github.com/jquick)) <!-- 2.2.109 -->
|
25
|
+
- Fix json automate tests and render call [#3408](https://github.com/inspec/inspec/pull/3408) ([jquick](https://github.com/jquick)) <!-- 2.2.108 -->
|
26
|
+
- Move habitat to v2 plugin [#3404](https://github.com/inspec/inspec/pull/3404) ([jquick](https://github.com/jquick)) <!-- 2.2.106 -->
|
27
|
+
- Fix rendering of profiles docs [#3393](https://github.com/inspec/inspec/pull/3393) ([jquick](https://github.com/jquick)) <!-- 2.2.103 -->
|
15
28
|
<!-- release_rollup -->
|
16
29
|
|
17
30
|
<!-- latest_stable_release -->
|
31
|
+
## [v2.2.102](https://github.com/inspec/inspec/tree/v2.2.102) (2018-09-17)
|
32
|
+
|
33
|
+
#### Merged Pull Requests
|
34
|
+
- Add json-automate to the report method [#3401](https://github.com/inspec/inspec/pull/3401) ([jquick](https://github.com/jquick))
|
35
|
+
<!-- latest_stable_release -->
|
36
|
+
|
18
37
|
## [v2.2.101](https://github.com/inspec/inspec/tree/v2.2.101) (2018-09-14)
|
19
38
|
|
20
39
|
#### New Features
|
@@ -44,7 +63,6 @@
|
|
44
63
|
- Bump omnibus ruby to 2.5.1 [#3390](https://github.com/inspec/inspec/pull/3390) ([jquick](https://github.com/jquick))
|
45
64
|
- Add platforms schema command [#3346](https://github.com/inspec/inspec/pull/3346) ([jquick](https://github.com/jquick))
|
46
65
|
- Fix profile vendoring on Windows [#3378](https://github.com/inspec/inspec/pull/3378) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
47
|
-
<!-- latest_stable_release -->
|
48
66
|
|
49
67
|
## [v2.2.78](https://github.com/inspec/inspec/tree/v2.2.78) (2018-08-30)
|
50
68
|
|
data/docs/profiles.md
CHANGED
@@ -348,6 +348,7 @@ Attributes may contain the following options:
|
|
348
348
|
|
349
349
|
|
350
350
|
You can specify attributes in your `inspec.yml` using the `attributes` setting. For example, to add a `user` attribute for your profile:
|
351
|
+
|
351
352
|
```YAML
|
352
353
|
attributes:
|
353
354
|
- name: user
|
@@ -356,6 +357,7 @@ attributes:
|
|
356
357
|
```
|
357
358
|
|
358
359
|
Example of adding a array object of servers:
|
360
|
+
|
359
361
|
```YAML
|
360
362
|
attributes:
|
361
363
|
- name: servers
|
@@ -369,6 +371,7 @@ attributes:
|
|
369
371
|
To access an attribute you will use the `attribute` keyword. You can use this anywhere in your control code.
|
370
372
|
|
371
373
|
For example:
|
374
|
+
|
372
375
|
```Ruby
|
373
376
|
current_user = attribute('user')
|
374
377
|
|
@@ -386,6 +389,7 @@ end
|
|
386
389
|
For sensitive data it is recomended to use a secrets YAML file located on the local machine to populate the values of attributes. A secrets file will always overwrite a attributes default value. To use the secrets file run `inspec exec` and specify the path to that Yaml file using the `--attrs` attribute.
|
387
390
|
|
388
391
|
For example, a inspec.yml:
|
392
|
+
|
389
393
|
```YAML
|
390
394
|
attributes:
|
391
395
|
- name: username
|
@@ -432,6 +436,7 @@ $ inspec exec examples/profile-attribute --attrs examples/profile-attribute.yml
|
|
432
436
|
To change your attributes for platform specific cases you can setup multiple `--attrs` files.
|
433
437
|
|
434
438
|
For example, a inspec.yml:
|
439
|
+
|
435
440
|
```YAML
|
436
441
|
attributes:
|
437
442
|
- name: users
|
@@ -440,6 +445,7 @@ attributes:
|
|
440
445
|
```
|
441
446
|
|
442
447
|
A YAML file named `windows.yml`
|
448
|
+
|
443
449
|
```YAML
|
444
450
|
users:
|
445
451
|
- Administrator
|
@@ -448,6 +454,7 @@ users:
|
|
448
454
|
```
|
449
455
|
|
450
456
|
A YAML file named `linux.yml`
|
457
|
+
|
451
458
|
```YAML
|
452
459
|
users:
|
453
460
|
- root
|
@@ -456,6 +463,7 @@ users:
|
|
456
463
|
```
|
457
464
|
|
458
465
|
The control file:
|
466
|
+
|
459
467
|
```RUBY
|
460
468
|
control 'system-users' do
|
461
469
|
impact 0.8
|
@@ -468,6 +476,7 @@ end
|
|
468
476
|
```
|
469
477
|
|
470
478
|
The following command runs the tests and applies the attributes specified:
|
479
|
+
|
471
480
|
```bash
|
472
481
|
$ inspec exec examples/profile-attribute --attrs examples/windows.yml
|
473
482
|
$ inspec exec examples/profile-attribute --attrs examples/linux.yml
|
data/docs/resources/gem.md.erb
CHANGED
@@ -46,6 +46,15 @@ The following examples show how to use this InSpec audit resource.
|
|
46
46
|
its('version') { should eq '0.33.0' }
|
47
47
|
end
|
48
48
|
|
49
|
+
### Verify that a particular version is installed when there are multiple versions installed
|
50
|
+
|
51
|
+
describe gem('rubocop') do
|
52
|
+
it { should be_installed }
|
53
|
+
its('versions') { should include /0.51.0/ }
|
54
|
+
its('versions.count') { should_not be > 3 }
|
55
|
+
end
|
56
|
+
|
57
|
+
|
49
58
|
### Verify that a gem package is not installed
|
50
59
|
|
51
60
|
describe gem('rubocop') do
|
@@ -72,6 +81,21 @@ The following examples show how to use this InSpec audit resource.
|
|
72
81
|
|
73
82
|
<br>
|
74
83
|
|
84
|
+
## Properties
|
85
|
+
|
86
|
+
### version (String)
|
87
|
+
|
88
|
+
The `version` property returns a string of the default version on the system:
|
89
|
+
|
90
|
+
its('version') { should eq '0.33.0' }
|
91
|
+
|
92
|
+
### versions
|
93
|
+
|
94
|
+
The `versions` property returns an array of strings of all the versions of the gem installed on the system:
|
95
|
+
|
96
|
+
its('versions') { should include /0.33/ }
|
97
|
+
|
98
|
+
|
75
99
|
## Matchers
|
76
100
|
|
77
101
|
For a full list of available matchers, please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
@@ -82,8 +106,3 @@ The `be_installed` matcher tests if the named Gem package is installed:
|
|
82
106
|
|
83
107
|
it { should be_installed }
|
84
108
|
|
85
|
-
### version
|
86
|
-
|
87
|
-
The `version` matcher tests if the named package version is on the system:
|
88
|
-
|
89
|
-
its('version') { should eq '0.33.0' }
|
@@ -62,6 +62,14 @@ The following examples show how to use this InSpec audit resource.
|
|
62
62
|
describe sql.query("SELECT SERVERPROPERTY('ProductVersion') as result").row(0).column('result') do
|
63
63
|
its("value") { should cmp > '12.00.4457' }
|
64
64
|
end
|
65
|
+
|
66
|
+
### Test a specific database
|
67
|
+
|
68
|
+
sql = mssql_session(user: 'my_user', password: 'password', db_name: 'test')
|
69
|
+
|
70
|
+
describe sql.query("SELECT Name AS result FROM Product WHERE ProductID == 1").row(0).column('result') do
|
71
|
+
its("value") { should eq 'foo' }
|
72
|
+
end
|
65
73
|
|
66
74
|
<br>
|
67
75
|
|
@@ -17,7 +17,14 @@ module Inspec::Plugin::V2
|
|
17
17
|
determine_plugin_conf_file
|
18
18
|
read_conf_file
|
19
19
|
unpack_conf_file
|
20
|
+
|
21
|
+
# Old-style (v0, v1) co-distributed plugins were called 'bundles'
|
22
|
+
# and were located in lib/bundles
|
20
23
|
detect_bundled_plugins unless options[:omit_bundles]
|
24
|
+
|
25
|
+
# New-style (v2) co-distributed plugins are in lib/plugins,
|
26
|
+
# and may be safely loaded
|
27
|
+
detect_core_plugins unless options[:omit_core_plugins]
|
21
28
|
end
|
22
29
|
|
23
30
|
def load_all
|
@@ -26,17 +33,20 @@ module Inspec::Plugin::V2
|
|
26
33
|
# rubocop: disable Lint/RescueException
|
27
34
|
begin
|
28
35
|
# We could use require, but under testing, we need to repeatedly reload the same
|
29
|
-
# plugin.
|
30
|
-
if plugin_details.
|
31
|
-
|
32
|
-
else
|
36
|
+
# plugin. However, gems only work with require (rubygems dooes not overload `load`)
|
37
|
+
if plugin_details.installation_type == :gem
|
38
|
+
activate_managed_gems_for_plugin(plugin_name)
|
33
39
|
require plugin_details.entry_point
|
40
|
+
else
|
41
|
+
load_path = plugin_details.entry_point
|
42
|
+
load_path += '.rb' unless plugin_details.entry_point.end_with?('.rb')
|
43
|
+
load load_path
|
34
44
|
end
|
35
45
|
plugin_details.loaded = true
|
36
46
|
annotate_status_after_loading(plugin_name)
|
37
47
|
rescue ::Exception => ex
|
38
48
|
plugin_details.load_exception = ex
|
39
|
-
Inspec::Log.error "Could not load plugin #{plugin_name}"
|
49
|
+
Inspec::Log.error "Could not load plugin #{plugin_name}: #{ex.message}"
|
40
50
|
end
|
41
51
|
# rubocop: enable Lint/RescueException
|
42
52
|
end
|
@@ -60,7 +70,7 @@ module Inspec::Plugin::V2
|
|
60
70
|
end
|
61
71
|
|
62
72
|
def activate(plugin_type, hook_name)
|
63
|
-
activator = registry.find_activators(plugin_type: plugin_type,
|
73
|
+
activator = registry.find_activators(plugin_type: plugin_type, activator_name: hook_name).first
|
64
74
|
# We want to capture literally any possible exception here, since we are storing them.
|
65
75
|
# rubocop: disable Lint/RescueException
|
66
76
|
begin
|
@@ -80,7 +90,7 @@ module Inspec::Plugin::V2
|
|
80
90
|
next if act.activated
|
81
91
|
# If there is anything in the CLI args with the same name, activate it
|
82
92
|
# If the word 'help' appears in the first position, load all CLI plugins
|
83
|
-
if cli_args.include?(act.activator_name.to_s) || cli_args[0] == 'help'
|
93
|
+
if cli_args.include?(act.activator_name.to_s) || cli_args[0] == 'help' || cli_args.size.zero?
|
84
94
|
activate(:cli_command, act.activator_name)
|
85
95
|
act.implementation_class.register_with_thor
|
86
96
|
end
|
@@ -138,6 +148,22 @@ module Inspec::Plugin::V2
|
|
138
148
|
@plugin_conf_file_path = File.join(@plugin_conf_file_path, 'plugins.json')
|
139
149
|
end
|
140
150
|
|
151
|
+
def detect_core_plugins
|
152
|
+
core_plugins_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'plugins'))
|
153
|
+
# These are expected to be organized as proper separate projects,
|
154
|
+
# with lib/ dirs, etc.
|
155
|
+
Dir.glob(File.join(core_plugins_dir, 'inspec-*')).each do |plugin_dir|
|
156
|
+
status = Inspec::Plugin::V2::Status.new
|
157
|
+
status.name = File.basename(plugin_dir)
|
158
|
+
status.entry_point = File.join(plugin_dir, 'lib', status.name + '.rb')
|
159
|
+
status.installation_type = :path
|
160
|
+
status.loaded = false
|
161
|
+
registry[status.name.to_sym] = status
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# TODO: DRY up re: Installer read_or_init_config_file
|
166
|
+
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
|
141
167
|
def read_conf_file
|
142
168
|
if File.exist?(@plugin_conf_file_path)
|
143
169
|
@plugin_file_contents = JSON.parse(File.read(@plugin_conf_file_path))
|
data/lib/inspec/version.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Core Plugins of InSpec
|
2
|
+
|
3
|
+
This area contains the plugins that ship with InSpec. They are automatically detected by the plugin loader.
|
4
|
+
|
5
|
+
## inspec-* directories
|
6
|
+
|
7
|
+
Each subdirectory here that begins with `inspec-` is intended to be a self-contained plugin project,
|
8
|
+
with code, docs, and tests.
|
9
|
+
|
10
|
+
## shared directory
|
11
|
+
|
12
|
+
This directory contains material that is reusable for core plugins, such as a test helper tuned to assisting
|
13
|
+
core plugin testing.
|
14
|
+
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'pathname'
|
4
|
+
require 'set'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
module InspecPlugins
|
9
|
+
module Artifact
|
10
|
+
class Base
|
11
|
+
KEY_BITS=2048
|
12
|
+
KEY_ALG=OpenSSL::PKey::RSA
|
13
|
+
|
14
|
+
INSPEC_PROFILE_VERSION_1='INSPEC-PROFILE-1'.freeze
|
15
|
+
INSPEC_REPORT_VERSION_1='INSPEC-REPORT-1'.freeze
|
16
|
+
|
17
|
+
ARTIFACT_DIGEST=OpenSSL::Digest::SHA512
|
18
|
+
ARTIFACT_DIGEST_NAME='SHA512'.freeze
|
19
|
+
|
20
|
+
VALID_PROFILE_VERSIONS=Set.new [INSPEC_PROFILE_VERSION_1]
|
21
|
+
VALID_PROFILE_DIGESTS=Set.new [ARTIFACT_DIGEST_NAME]
|
22
|
+
|
23
|
+
SIGNED_PROFILE_SUFFIX='iaf'.freeze
|
24
|
+
SIGNED_REPORT_SUFFIX='iar'.freeze
|
25
|
+
|
26
|
+
def self.keygen(options)
|
27
|
+
key = KEY_ALG.new KEY_BITS
|
28
|
+
puts 'Generating private key'
|
29
|
+
open "#{options['keyname']}.pem.key", 'w' do |io| io.write key.to_pem end
|
30
|
+
puts 'Generating public key'
|
31
|
+
open "#{options['keyname']}.pem.pub", 'w' do |io| io.write key.public_key.to_pem end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.profile_sign(options)
|
35
|
+
artifact = new
|
36
|
+
Dir.mktmpdir do |workdir|
|
37
|
+
puts "Signing #{options['profile']} with key #{options['keyname']}"
|
38
|
+
path_to_profile = options['profile']
|
39
|
+
profile_md = artifact.read_profile_metadata(path_to_profile)
|
40
|
+
artifact_filename = "#{profile_md['name']}-#{profile_md['version']}.#{SIGNED_PROFILE_SUFFIX}"
|
41
|
+
tarfile = artifact.profile_compress(path_to_profile, profile_md, workdir)
|
42
|
+
content = IO.binread(tarfile)
|
43
|
+
signing_key = KEY_ALG.new File.read "#{options['keyname']}.pem.key"
|
44
|
+
sha = ARTIFACT_DIGEST.new
|
45
|
+
signature = signing_key.sign sha, content
|
46
|
+
# convert the signature to Base64
|
47
|
+
signature_base64 = Base64.encode64(signature)
|
48
|
+
tar_content = IO.binread(tarfile)
|
49
|
+
File.open(artifact_filename, 'wb') do |f|
|
50
|
+
f.puts(INSPEC_PROFILE_VERSION_1)
|
51
|
+
f.puts(options['keyname'])
|
52
|
+
f.puts(ARTIFACT_DIGEST_NAME)
|
53
|
+
f.puts(signature_base64)
|
54
|
+
f.puts('') # newline separates artifact header with body
|
55
|
+
f.write(tar_content)
|
56
|
+
end
|
57
|
+
puts "Successfully generated #{artifact_filename}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.profile_verify(options)
|
62
|
+
artifact = new
|
63
|
+
file_to_verifiy = options['infile']
|
64
|
+
puts "Verifying #{file_to_verifiy}"
|
65
|
+
artifact.verify(file_to_verifiy) do ||
|
66
|
+
puts 'Artifact is valid'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.profile_install(options)
|
71
|
+
artifact = new
|
72
|
+
puts 'Installing profile'
|
73
|
+
file_to_verifiy = options['infile']
|
74
|
+
dest_dir = options['destdir']
|
75
|
+
artifact.verify(file_to_verifiy) do |content|
|
76
|
+
Dir.mktmpdir do |workdir|
|
77
|
+
tmpfile = Pathname.new(workdir).join('artifact_to_install.tar.gz')
|
78
|
+
File.write(tmpfile, content)
|
79
|
+
puts "Installing to #{dest_dir}"
|
80
|
+
`tar xzf #{tmpfile} -C #{dest_dir}`
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_profile_metadata(path_to_profile)
|
86
|
+
begin
|
87
|
+
p = Pathname.new(path_to_profile)
|
88
|
+
p = p.join('inspec.yml')
|
89
|
+
if not p.exist?
|
90
|
+
raise "#{path_to_profile} doesn't appear to be a valid Inspec profile"
|
91
|
+
end
|
92
|
+
yaml = YAML.load_file(p.to_s)
|
93
|
+
yaml = yaml.to_hash
|
94
|
+
|
95
|
+
if not yaml.key? 'name'
|
96
|
+
raise 'Profile is invalid, name is not defined'
|
97
|
+
end
|
98
|
+
|
99
|
+
if not yaml.key? 'version'
|
100
|
+
raise 'Profile is invalid, version is not defined'
|
101
|
+
end
|
102
|
+
rescue => e
|
103
|
+
# rewrap it and pass it up to the CLI
|
104
|
+
raise "Error reading Inspec profile metadata: #{e}"
|
105
|
+
end
|
106
|
+
|
107
|
+
yaml
|
108
|
+
end
|
109
|
+
|
110
|
+
def profile_compress(path_to_profile, profile_md, workdir)
|
111
|
+
profile_name = profile_md['name']
|
112
|
+
profile_version = profile_md['version']
|
113
|
+
outfile_name = "#{workdir}/#{profile_name}-#{profile_version}.tar.gz"
|
114
|
+
`tar czf #{outfile_name} -C #{path_to_profile} .`
|
115
|
+
outfile_name
|
116
|
+
end
|
117
|
+
|
118
|
+
def valid_header?(file_alg, file_version, file_keyname)
|
119
|
+
public_keyfile = "#{file_keyname}.pem.pub"
|
120
|
+
puts "Looking for #{public_keyfile} to verify artifact"
|
121
|
+
if !File.exist? public_keyfile
|
122
|
+
raise "Can't find #{public_keyfile}"
|
123
|
+
end
|
124
|
+
|
125
|
+
raise 'Invalid artifact digest algorithm detected' if !VALID_PROFILE_DIGESTS.member?(file_alg)
|
126
|
+
raise 'Invalid artifact version detected' if !VALID_PROFILE_VERSIONS.member?(file_version)
|
127
|
+
end
|
128
|
+
|
129
|
+
def verify(file_to_verifiy, &content_block)
|
130
|
+
f = File.open(file_to_verifiy, 'r')
|
131
|
+
file_version = f.readline.strip!
|
132
|
+
file_keyname = f.readline.strip!
|
133
|
+
file_alg = f.readline.strip!
|
134
|
+
|
135
|
+
file_sig = ''
|
136
|
+
# the signature is multi-line
|
137
|
+
while (line = f.readline) != "\n"
|
138
|
+
file_sig += line
|
139
|
+
end
|
140
|
+
file_sig.strip!
|
141
|
+
f.close
|
142
|
+
|
143
|
+
valid_header?(file_alg, file_version, file_keyname)
|
144
|
+
|
145
|
+
public_keyfile = "#{file_keyname}.pem.pub"
|
146
|
+
verification_key = KEY_ALG.new File.read public_keyfile
|
147
|
+
|
148
|
+
f = File.open(file_to_verifiy, 'r')
|
149
|
+
while f.readline != "\n" do end
|
150
|
+
content = f.read
|
151
|
+
|
152
|
+
signature = Base64.decode64(file_sig)
|
153
|
+
digest = ARTIFACT_DIGEST.new
|
154
|
+
if verification_key.verify digest, signature, content
|
155
|
+
content_block.yield(content)
|
156
|
+
else
|
157
|
+
puts 'Artifact is invalid'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Notes:
|
6
|
+
#
|
7
|
+
# Generate keys
|
8
|
+
# The initial implementation uses 2048 bit RSA key pairs (public + private).
|
9
|
+
# Public keys must be available for a customer to install and verify an artifact.
|
10
|
+
# Private keys should be stored in a secure location and NOT be distributed.
|
11
|
+
# (They're only for creating artifacts).
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# .IAF file format
|
15
|
+
# .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
|
16
|
+
# The iaf file wraps a binary artifact with some metadata. The first implementation
|
17
|
+
# looks like this:
|
18
|
+
#
|
19
|
+
# INSPEC-PROFILE-1
|
20
|
+
# name_of_signing_key
|
21
|
+
# algorithm
|
22
|
+
# signature
|
23
|
+
# <empty line>
|
24
|
+
# binary-blob
|
25
|
+
# <eof>
|
26
|
+
#
|
27
|
+
# Let's look at each line:
|
28
|
+
# INSPEC-PROFILE-1:
|
29
|
+
# This is the artifact version descriptor. It should't change unless the
|
30
|
+
# format of the archive changes.
|
31
|
+
#
|
32
|
+
# name_of_signing_key
|
33
|
+
# The name of the public key that can be used to verify an artifact
|
34
|
+
#
|
35
|
+
# algorithm
|
36
|
+
# The digest used to sign, I picked SHA512 to start with.
|
37
|
+
# If we support multiple digests, we'll need to have the verify() method
|
38
|
+
# support each digest.
|
39
|
+
#
|
40
|
+
# signature
|
41
|
+
# The result of passing the binary artifact through the digest algorithm above.
|
42
|
+
# Result is base64 encoded.
|
43
|
+
#
|
44
|
+
# <empty line>
|
45
|
+
# We use an empty line to separate artifact header from artifact body (binary blob).
|
46
|
+
# The artifact body can be anything you like.
|
47
|
+
#
|
48
|
+
# binary-blob
|
49
|
+
# A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
|
50
|
+
# stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
|
51
|
+
# format, the artifact version descriptor must be incremented, and the sign()
|
52
|
+
# and verify() methods must be updated to support a newer version.
|
53
|
+
#
|
54
|
+
#
|
55
|
+
# Key revocation
|
56
|
+
# This implementation doesn't support key revocation. However, a customer
|
57
|
+
# can remove the public cert file before installation, and artifacts will then
|
58
|
+
# fail verification.
|
59
|
+
#
|
60
|
+
# Key locations
|
61
|
+
# This implementation uses the current working directory to find public and
|
62
|
+
# private keys. We should establish a common key directory (similar to /hab/cache/keys
|
63
|
+
# or ~/.hab/cache/keys in Habitat).
|
64
|
+
#
|
65
|
+
# Extracting artifacts outside of Inspec
|
66
|
+
# As in Habitat, the artifact format for Inspec allows the use of common
|
67
|
+
# Unix tools to read the header and body of an artifact.
|
68
|
+
# To extract the header from a .iaf:
|
69
|
+
# sed '/^$/q' foo.iaf
|
70
|
+
# To extract the raw content from a .iaf:
|
71
|
+
# sed '1,/^$/d' foo.iaf
|
72
|
+
|
73
|
+
module InspecPlugins
|
74
|
+
module Artifact
|
75
|
+
class CLI < Inspec.plugin(2, :cli_command)
|
76
|
+
subcommand_desc 'artifact SUBCOMMAND', 'Manage InSpec Artifacts'
|
77
|
+
|
78
|
+
desc 'generate', 'Generate a RSA key pair for signing and verification'
|
79
|
+
option :keyname, type: :string, required: true,
|
80
|
+
desc: 'Desriptive name of key'
|
81
|
+
option :keydir, type: :string, default: './',
|
82
|
+
desc: 'Directory to search for keys'
|
83
|
+
def generate_keys
|
84
|
+
puts 'Generating keys'
|
85
|
+
InspecPlugins::Artifact::Base.keygen(options)
|
86
|
+
end
|
87
|
+
|
88
|
+
desc 'sign-profile', 'Create a signed .iaf artifact'
|
89
|
+
option :profile, type: :string, required: true,
|
90
|
+
desc: 'Path to profile directory'
|
91
|
+
option :keyname, type: :string, required: true,
|
92
|
+
desc: 'Desriptive name of key'
|
93
|
+
def sign_profile
|
94
|
+
InspecPlugins::Artifact::Base.profile_sign(options)
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'verify-profile', 'Verify a signed .iaf artifact'
|
98
|
+
option :infile, type: :string, required: true,
|
99
|
+
desc: '.iaf file to verify'
|
100
|
+
def verify_profile
|
101
|
+
InspecPlugins::Artifact::Base.profile_verify(options)
|
102
|
+
end
|
103
|
+
|
104
|
+
desc 'install-profile', 'Verify and install a signed .iaf artifact'
|
105
|
+
option :infile, type: :string, required: true,
|
106
|
+
desc: '.iaf file to install'
|
107
|
+
option :destdir, type: :string, required: true,
|
108
|
+
desc: 'Installation directory'
|
109
|
+
def install_profile
|
110
|
+
InspecPlugins::Artifact::Base.profile_install(options)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative '../../../shared/core_plugin_test_helper.rb'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
class ArtifactCli < MiniTest::Test
|
8
|
+
include CorePluginFunctionalHelper
|
9
|
+
|
10
|
+
def test_generating_archive_keys
|
11
|
+
Dir.mktmpdir do |dir|
|
12
|
+
unique_key_name = SecureRandom.uuid()
|
13
|
+
out = run_inspec_process("artifact generate --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
|
14
|
+
assert_equal 0, out.exit_status
|
15
|
+
|
16
|
+
stdout = out.stdout.force_encoding(Encoding::UTF_8)
|
17
|
+
assert_includes stdout, 'Generating private key'
|
18
|
+
assert_includes stdout, 'Generating public key'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_verify_and_install_signed_profile
|
23
|
+
Dir.mktmpdir do |dir|
|
24
|
+
unique_key_name = SecureRandom.uuid()
|
25
|
+
install_dir = File.join(dir, SecureRandom.uuid())
|
26
|
+
profile = File.join(dir, 'profile')
|
27
|
+
FileUtils.mkdir(install_dir)
|
28
|
+
|
29
|
+
# create profile
|
30
|
+
profile = File.join(dir, 'artifact-profile')
|
31
|
+
run_inspec_process("init profile artifact-profile", prefix: "cd #{dir} &&")
|
32
|
+
|
33
|
+
out = run_inspec_process("artifact generate --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
|
34
|
+
assert_equal 0, out.exit_status
|
35
|
+
|
36
|
+
out = run_inspec_process("artifact sign-profile --profile #{profile} --keyname #{unique_key_name}", prefix: "cd #{dir} &&")
|
37
|
+
assert_equal 0, out.exit_status
|
38
|
+
|
39
|
+
out = run_inspec_process("artifact install-profile --infile artifact-profile-0.1.0.iaf --destdir #{install_dir}", prefix: "cd #{dir} &&")
|
40
|
+
assert_equal 0, out.exit_status
|
41
|
+
|
42
|
+
assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Installing to #{install_dir}"
|
43
|
+
assert_includes Dir.entries(install_dir).join, 'inspec.yml'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|