inspec-core 2.2.102 → 2.2.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -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
|