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
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module InspecPlugins
|
5
|
+
module Init
|
6
|
+
class Renderer
|
7
|
+
# Creates a renderer able to render the given template type
|
8
|
+
# 1. iterate over all files
|
9
|
+
# 2. read content in erb
|
10
|
+
# 3. write to full_destination_root_path
|
11
|
+
|
12
|
+
attr_reader :overwrite_mode, :ui
|
13
|
+
def initialize(cli_ui, cli_options = {})
|
14
|
+
@ui = cli_ui
|
15
|
+
@overwrite_mode = cli_options['overwrite']
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop: disable Metrics/AbcSize
|
19
|
+
def render_with_values(template_type, template_values = {})
|
20
|
+
# look for template directory
|
21
|
+
base_dir = File.join(File.dirname(__FILE__), 'templates', template_type)
|
22
|
+
# prepare glob for all subdirectories and files
|
23
|
+
template_glob = File.join(base_dir, '**', '{*,.*}')
|
24
|
+
# Use the name attribute to define the path to the profile.
|
25
|
+
profile_path = template_values[:name]
|
26
|
+
# Use slashes (\, /) to split up the name into an Array then use the last entry
|
27
|
+
# to reset the name of the profile.
|
28
|
+
template_values[:name] = template_values[:name].split(%r{\\|\/}).last
|
29
|
+
# Generate the full full_destination_root_path path on disk
|
30
|
+
full_destination_root_path = Pathname.new(Dir.pwd).join(profile_path)
|
31
|
+
ui.plain_text "Create new #{template_type} at #{ui.mark_text(full_destination_root_path)}"
|
32
|
+
|
33
|
+
# check that the directory does not exist
|
34
|
+
if File.exist?(full_destination_root_path) && !overwrite_mode
|
35
|
+
ui.plain_text "#{ui.mark_text(full_destination_root_path)} exists already, use --overwrite"
|
36
|
+
ui.exit(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
# ensure that full_destination_root_path directory is available
|
40
|
+
FileUtils.mkdir_p(full_destination_root_path)
|
41
|
+
|
42
|
+
# iterate over files and write to full_destination_root_path
|
43
|
+
Dir.glob(template_glob) do |file|
|
44
|
+
relative_destination_item_path = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
|
45
|
+
full_destination_item_path = Pathname.new(full_destination_root_path).join(relative_destination_item_path)
|
46
|
+
if File.directory?(file)
|
47
|
+
ui.li "Create directory #{ui.mark_text(relative_destination_item_path)}"
|
48
|
+
FileUtils.mkdir_p(full_destination_item_path)
|
49
|
+
elsif File.file?(file)
|
50
|
+
ui.li "Create file #{ui.mark_text(relative_destination_item_path)}"
|
51
|
+
# read & render content
|
52
|
+
content = render(File.read(file), template_values)
|
53
|
+
# write file content
|
54
|
+
File.write(full_destination_item_path, content)
|
55
|
+
else
|
56
|
+
ui.plain_text "Ignore #{file}, because its not an file or directoy"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# rubocop: enable Metrics/AbcSize
|
61
|
+
|
62
|
+
# This is a render helper to bind hash values to a ERB template
|
63
|
+
# ERB provides result_with_hash in ruby 2.5.0+, which does exactly this
|
64
|
+
def render(template_content, hash)
|
65
|
+
# create a new binding class
|
66
|
+
cls = Class.new do
|
67
|
+
hash.each do |key, value|
|
68
|
+
define_method key.to_sym do
|
69
|
+
value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
# expose binding
|
73
|
+
define_method :bind do
|
74
|
+
binding
|
75
|
+
end
|
76
|
+
end
|
77
|
+
ERB.new(template_content).result(cls.new.bind)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
File without changes
|
data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/controls/example.rb
RENAMED
File without changes
|
File without changes
|
data/lib/{bundles → plugins/inspec-init/lib}/inspec-init/templates/profile/libraries/.gitkeep
RENAMED
File without changes
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative '../../../shared/core_plugin_test_helper.rb'
|
4
|
+
|
5
|
+
class InitCli < MiniTest::Test
|
6
|
+
include CorePluginFunctionalHelper
|
7
|
+
|
8
|
+
def test_generating_inspec_profile
|
9
|
+
Dir.mktmpdir do |dir|
|
10
|
+
profile = File.join(dir, 'test-profile')
|
11
|
+
out = run_inspec_process("init profile test-profile", prefix: "cd #{dir} &&")
|
12
|
+
assert_equal 0, out.exit_status
|
13
|
+
assert_includes out.stdout, 'Create new profile at'
|
14
|
+
assert_includes out.stdout, profile
|
15
|
+
assert_includes Dir.entries(profile).join, 'inspec.yml'
|
16
|
+
assert_includes Dir.entries(profile).join, 'README.md'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_profile_with_slash_name
|
21
|
+
Dir.mktmpdir do |dir|
|
22
|
+
profile = dir + '/test/deeper/profile'
|
23
|
+
out = run_inspec_process("init profile test/deeper/profile", prefix: "cd #{dir} &&")
|
24
|
+
assert_equal 0, out.exit_status
|
25
|
+
assert_equal true, File.exist?(profile)
|
26
|
+
profile = YAML.load_file("#{profile}/inspec.yml")
|
27
|
+
assert_equal 'profile', profile['name']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
# Load test harness - MiniTest
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/unit'
|
5
|
+
require 'minitest/pride'
|
6
|
+
require 'minitest/spec'
|
7
|
+
|
8
|
+
# Data formats commonly used in testing
|
9
|
+
require 'json'
|
10
|
+
require 'ostruct'
|
11
|
+
|
12
|
+
# Utilities often needed
|
13
|
+
require 'fileutils'
|
14
|
+
|
15
|
+
# Configure MiniTest to expose things like `let`
|
16
|
+
class Module
|
17
|
+
include Minitest::Spec::DSL
|
18
|
+
end
|
19
|
+
|
20
|
+
module CorePluginBaseHelper
|
21
|
+
let(:repo_path) { File.expand_path(File.join(__FILE__, '..', '..', '..', '..')) }
|
22
|
+
let(:inspec_bin_path) { File.join(repo_path, 'bin', 'inspec') }
|
23
|
+
let(:core_mock_path) { File.join(repo_path, 'test', 'unit', 'mock') }
|
24
|
+
let(:core_fixture_plugins_path) { File.join(core_mock_path, 'plugins') }
|
25
|
+
let(:core_config_dir_path) { File.join(core_mock_path, 'config_dirs') }
|
26
|
+
|
27
|
+
let(:registry) { Inspec::Plugin::V2::Registry.instance }
|
28
|
+
end
|
29
|
+
|
30
|
+
module CorePluginFunctionalHelper
|
31
|
+
include CorePluginBaseHelper
|
32
|
+
|
33
|
+
require 'train'
|
34
|
+
TRAIN_CONNECTION = Train.create('local', command_runner: :generic).connection
|
35
|
+
|
36
|
+
def run_inspec_process(command_line, opts = {})
|
37
|
+
prefix = ''
|
38
|
+
if opts.key?(:prefix)
|
39
|
+
prefix = opts[:prefix]
|
40
|
+
elsif opts.key?(:env)
|
41
|
+
prefix = opts[:env].to_a.map { |assignment| "#{assignment[0]}=#{assignment[1]}" }.join(' ')
|
42
|
+
end
|
43
|
+
TRAIN_CONNECTION.run_command("#{prefix} #{inspec_bin_path} #{command_line}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module CorePluginUnitHelper
|
48
|
+
include CorePluginBaseHelper
|
49
|
+
require 'inspec'
|
50
|
+
end
|
data/lib/resources/gem.rb
CHANGED
@@ -49,9 +49,10 @@ module Inspec::Resources
|
|
49
49
|
}
|
50
50
|
return @info unless @info[:installed]
|
51
51
|
|
52
|
-
versions = params[2].split(',')
|
52
|
+
versions = params[2].split(',').map(&:strip)
|
53
53
|
@info[:name] = params[1]
|
54
54
|
@info[:version] = versions[0]
|
55
|
+
@info[:versions] = versions
|
55
56
|
@info
|
56
57
|
end
|
57
58
|
|
@@ -63,6 +64,11 @@ module Inspec::Resources
|
|
63
64
|
info[:version]
|
64
65
|
end
|
65
66
|
|
67
|
+
# this returns an array of strings
|
68
|
+
def versions
|
69
|
+
info[:versions]
|
70
|
+
end
|
71
|
+
|
66
72
|
def to_s
|
67
73
|
"gem package #{@package_name}"
|
68
74
|
end
|
@@ -29,7 +29,7 @@ module Inspec::Resources
|
|
29
29
|
end
|
30
30
|
"
|
31
31
|
|
32
|
-
attr_reader :user, :password, :host, :port, :instance, :local_mode
|
32
|
+
attr_reader :user, :password, :host, :port, :instance, :local_mode, :db_name
|
33
33
|
def initialize(opts = {})
|
34
34
|
@user = opts[:user]
|
35
35
|
@password = opts[:password] || opts[:pass]
|
@@ -46,6 +46,7 @@ module Inspec::Resources
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
@instance = opts[:instance]
|
49
|
+
@db_name = opts[:db_name]
|
49
50
|
|
50
51
|
# check if sqlcmd is available
|
51
52
|
raise Inspec::Exceptions::ResourceSkipped, 'sqlcmd is missing' unless inspec.command('sqlcmd').exist?
|
@@ -53,11 +54,12 @@ module Inspec::Resources
|
|
53
54
|
raise Inspec::Exceptions::ResourceSkipped, "Can't connect to the MS SQL Server." unless test_connection
|
54
55
|
end
|
55
56
|
|
56
|
-
def query(q)
|
57
|
+
def query(q) # rubocop:disable Metrics/PerceivedComplexity
|
57
58
|
escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
|
58
59
|
# surpress 'x rows affected' in SQLCMD with 'set nocount on;'
|
59
60
|
cmd_string = "sqlcmd -Q \"set nocount on; #{escaped_query}\" -W -w 1024 -s ','"
|
60
61
|
cmd_string += " -U '#{@user}' -P '#{@password}'" unless @user.nil? || @password.nil?
|
62
|
+
cmd_string += " -d '#{@db_name}'" unless @db_name.nil?
|
61
63
|
unless local_mode?
|
62
64
|
if @port.nil?
|
63
65
|
cmd_string += " -S '#{@host}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.112
|
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-09-
|
11
|
+
date: 2018-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train-core
|
@@ -482,9 +482,6 @@ files:
|
|
482
482
|
- examples/profile/libraries/gordon_config.rb
|
483
483
|
- inspec-core.gemspec
|
484
484
|
- lib/bundles/README.md
|
485
|
-
- lib/bundles/inspec-artifact.rb
|
486
|
-
- lib/bundles/inspec-artifact/README.md
|
487
|
-
- lib/bundles/inspec-artifact/cli.rb
|
488
485
|
- lib/bundles/inspec-compliance.rb
|
489
486
|
- lib/bundles/inspec-compliance/.kitchen.yml
|
490
487
|
- lib/bundles/inspec-compliance/README.md
|
@@ -498,18 +495,6 @@ files:
|
|
498
495
|
- lib/bundles/inspec-compliance/support.rb
|
499
496
|
- lib/bundles/inspec-compliance/target.rb
|
500
497
|
- lib/bundles/inspec-compliance/test/integration/default/cli.rb
|
501
|
-
- lib/bundles/inspec-habitat.rb
|
502
|
-
- lib/bundles/inspec-habitat/cli.rb
|
503
|
-
- lib/bundles/inspec-habitat/log.rb
|
504
|
-
- lib/bundles/inspec-habitat/profile.rb
|
505
|
-
- lib/bundles/inspec-init.rb
|
506
|
-
- lib/bundles/inspec-init/README.md
|
507
|
-
- lib/bundles/inspec-init/cli.rb
|
508
|
-
- lib/bundles/inspec-init/renderer.rb
|
509
|
-
- lib/bundles/inspec-init/templates/profile/README.md
|
510
|
-
- lib/bundles/inspec-init/templates/profile/controls/example.rb
|
511
|
-
- lib/bundles/inspec-init/templates/profile/inspec.yml
|
512
|
-
- lib/bundles/inspec-init/templates/profile/libraries/.gitkeep
|
513
498
|
- lib/bundles/inspec-supermarket.rb
|
514
499
|
- lib/bundles/inspec-supermarket/README.md
|
515
500
|
- lib/bundles/inspec-supermarket/api.rb
|
@@ -611,6 +596,25 @@ files:
|
|
611
596
|
- lib/inspec/source_reader.rb
|
612
597
|
- lib/inspec/version.rb
|
613
598
|
- lib/matchers/matchers.rb
|
599
|
+
- lib/plugins/README.md
|
600
|
+
- lib/plugins/inspec-artifact/lib/inspec-artifact.rb
|
601
|
+
- lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb
|
602
|
+
- lib/plugins/inspec-artifact/lib/inspec-artifact/cli.rb
|
603
|
+
- lib/plugins/inspec-artifact/test/functional/inspec_artifact_test.rb
|
604
|
+
- lib/plugins/inspec-habitat/lib/inspec-habitat.rb
|
605
|
+
- lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb
|
606
|
+
- lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb
|
607
|
+
- lib/plugins/inspec-habitat/test/unit/profile_test.rb
|
608
|
+
- lib/plugins/inspec-init/README.md
|
609
|
+
- lib/plugins/inspec-init/lib/inspec-init.rb
|
610
|
+
- lib/plugins/inspec-init/lib/inspec-init/cli.rb
|
611
|
+
- lib/plugins/inspec-init/lib/inspec-init/renderer.rb
|
612
|
+
- lib/plugins/inspec-init/lib/inspec-init/templates/profile/README.md
|
613
|
+
- lib/plugins/inspec-init/lib/inspec-init/templates/profile/controls/example.rb
|
614
|
+
- lib/plugins/inspec-init/lib/inspec-init/templates/profile/inspec.yml
|
615
|
+
- lib/plugins/inspec-init/lib/inspec-init/templates/profile/libraries/.gitkeep
|
616
|
+
- lib/plugins/inspec-init/test/functional/inspec_init_test.rb
|
617
|
+
- lib/plugins/shared/core_plugin_test_helper.rb
|
614
618
|
- lib/resources/aide_conf.rb
|
615
619
|
- lib/resources/apache.rb
|
616
620
|
- lib/resources/apache_conf.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
# TODO
|
@@ -1,278 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# frozen_string_literal: true
|
3
|
-
require 'base64'
|
4
|
-
require 'openssl'
|
5
|
-
require 'pathname'
|
6
|
-
require 'set'
|
7
|
-
require 'tempfile'
|
8
|
-
require 'yaml'
|
9
|
-
require 'inspec/base_cli'
|
10
|
-
|
11
|
-
# Notes:
|
12
|
-
#
|
13
|
-
# Generate keys
|
14
|
-
# The initial implementation uses 2048 bit RSA key pairs (public + private).
|
15
|
-
# Public keys must be available for a customer to install and verify an artifact.
|
16
|
-
# Private keys should be stored in a secure location and NOT be distributed.
|
17
|
-
# (They're only for creating artifacts).
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# .IAF file format
|
21
|
-
# .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
|
22
|
-
# The iaf file wraps a binary artifact with some metadata. The first implementation
|
23
|
-
# looks like this:
|
24
|
-
#
|
25
|
-
# INSPEC-PROFILE-1
|
26
|
-
# name_of_signing_key
|
27
|
-
# algorithm
|
28
|
-
# signature
|
29
|
-
# <empty line>
|
30
|
-
# binary-blob
|
31
|
-
# <eof>
|
32
|
-
#
|
33
|
-
# Let's look at each line:
|
34
|
-
# INSPEC-PROFILE-1:
|
35
|
-
# This is the artifact version descriptor. It should't change unless the
|
36
|
-
# format of the archive changes.
|
37
|
-
#
|
38
|
-
# name_of_signing_key
|
39
|
-
# The name of the public key that can be used to verify an artifact
|
40
|
-
#
|
41
|
-
# algorithm
|
42
|
-
# The digest used to sign, I picked SHA512 to start with.
|
43
|
-
# If we support multiple digests, we'll need to have the verify() method
|
44
|
-
# support each digest.
|
45
|
-
#
|
46
|
-
# signature
|
47
|
-
# The result of passing the binary artifact through the digest algorithm above.
|
48
|
-
# Result is base64 encoded.
|
49
|
-
#
|
50
|
-
# <empty line>
|
51
|
-
# We use an empty line to separate artifact header from artifact body (binary blob).
|
52
|
-
# The artifact body can be anything you like.
|
53
|
-
#
|
54
|
-
# binary-blob
|
55
|
-
# A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
|
56
|
-
# stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
|
57
|
-
# format, the artifact version descriptor must be incremented, and the sign()
|
58
|
-
# and verify() methods must be updated to support a newer version.
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# Key revocation
|
62
|
-
# This implementation doesn't support key revocation. However, a customer
|
63
|
-
# can remove the public cert file before installation, and artifacts will then
|
64
|
-
# fail verification.
|
65
|
-
#
|
66
|
-
# Key locations
|
67
|
-
# This implementation uses the current working directory to find public and
|
68
|
-
# private keys. We should establish a common key directory (similar to /hab/cache/keys
|
69
|
-
# or ~/.hab/cache/keys in Habitat).
|
70
|
-
#
|
71
|
-
# Extracting artifacts outside of Inspec
|
72
|
-
# As in Habitat, the artifact format for Inspec allows the use of common
|
73
|
-
# Unix tools to read the header and body of an artifact.
|
74
|
-
# To extract the header from a .iaf:
|
75
|
-
# sed '/^$/q' foo.iaf
|
76
|
-
# To extract the raw content from a .iaf:
|
77
|
-
# sed '1,/^$/d' foo.iaf
|
78
|
-
|
79
|
-
module Artifact
|
80
|
-
KEY_BITS=2048
|
81
|
-
KEY_ALG=OpenSSL::PKey::RSA
|
82
|
-
|
83
|
-
INSPEC_PROFILE_VERSION_1='INSPEC-PROFILE-1'
|
84
|
-
INSPEC_REPORT_VERSION_1='INSPEC-REPORT-1'
|
85
|
-
|
86
|
-
ARTIFACT_DIGEST=OpenSSL::Digest::SHA512
|
87
|
-
ARTIFACT_DIGEST_NAME='SHA512'
|
88
|
-
|
89
|
-
VALID_PROFILE_VERSIONS=Set.new [INSPEC_PROFILE_VERSION_1]
|
90
|
-
VALID_PROFILE_DIGESTS=Set.new [ARTIFACT_DIGEST_NAME]
|
91
|
-
|
92
|
-
SIGNED_PROFILE_SUFFIX='iaf'
|
93
|
-
SIGNED_REPORT_SUFFIX='iar'
|
94
|
-
class CLI < Inspec::BaseCLI
|
95
|
-
namespace 'artifact'
|
96
|
-
|
97
|
-
# TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
|
98
|
-
def self.banner(command, _namespace = nil, _subcommand = false)
|
99
|
-
"#{basename} #{subcommand_prefix} #{command.usage}"
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.subcommand_prefix
|
103
|
-
namespace
|
104
|
-
end
|
105
|
-
|
106
|
-
desc 'generate', 'Generate a RSA key pair for signing and verification'
|
107
|
-
option :keyname, type: :string, required: true,
|
108
|
-
desc: 'Desriptive name of key'
|
109
|
-
option :keydir, type: :string, default: './',
|
110
|
-
desc: 'Directory to search for keys'
|
111
|
-
def generate_keys
|
112
|
-
puts 'Generating keys'
|
113
|
-
keygen
|
114
|
-
end
|
115
|
-
|
116
|
-
desc 'sign-profile', 'Create a signed .iaf artifact'
|
117
|
-
option :profile, type: :string, required: true,
|
118
|
-
desc: 'Path to profile directory'
|
119
|
-
option :keyname, type: :string, required: true,
|
120
|
-
desc: 'Desriptive name of key'
|
121
|
-
def sign_profile
|
122
|
-
profile_sign
|
123
|
-
end
|
124
|
-
|
125
|
-
desc 'verify-profile', 'Verify a signed .iaf artifact'
|
126
|
-
option :infile, type: :string, required: true,
|
127
|
-
desc: '.iaf file to verify'
|
128
|
-
def verify_profile
|
129
|
-
profile_verify
|
130
|
-
end
|
131
|
-
|
132
|
-
desc 'install-profile', 'Verify and install a signed .iaf artifact'
|
133
|
-
option :infile, type: :string, required: true,
|
134
|
-
desc: '.iaf file to install'
|
135
|
-
option :destdir, type: :string, required: true,
|
136
|
-
desc: 'Installation directory'
|
137
|
-
def install_profile
|
138
|
-
profile_install
|
139
|
-
end
|
140
|
-
|
141
|
-
private
|
142
|
-
|
143
|
-
def keygen
|
144
|
-
key = KEY_ALG.new KEY_BITS
|
145
|
-
puts 'Generating private key'
|
146
|
-
open "#{options['keyname']}.pem.key", 'w' do |io| io.write key.to_pem end
|
147
|
-
puts 'Generating public key'
|
148
|
-
open "#{options['keyname']}.pem.pub", 'w' do |io| io.write key.public_key.to_pem end
|
149
|
-
end
|
150
|
-
|
151
|
-
def read_profile_metadata(path_to_profile)
|
152
|
-
begin
|
153
|
-
p = Pathname.new(path_to_profile)
|
154
|
-
p = p.join('inspec.yml')
|
155
|
-
if not p.exist?
|
156
|
-
raise "#{path_to_profile} doesn't appear to be a valid Inspec profile"
|
157
|
-
end
|
158
|
-
yaml = YAML.load_file(p.to_s)
|
159
|
-
yaml = yaml.to_hash
|
160
|
-
|
161
|
-
if not yaml.key? 'name'
|
162
|
-
raise 'Profile is invalid, name is not defined'
|
163
|
-
end
|
164
|
-
|
165
|
-
if not yaml.key? 'version'
|
166
|
-
raise 'Profile is invalid, version is not defined'
|
167
|
-
end
|
168
|
-
rescue => e
|
169
|
-
# rewrap it and pass it up to the CLI
|
170
|
-
raise "Error reading Inspec profile metadata: #{e}"
|
171
|
-
end
|
172
|
-
|
173
|
-
yaml
|
174
|
-
end
|
175
|
-
|
176
|
-
def profile_compress(path_to_profile, profile_md, workdir)
|
177
|
-
profile_name = profile_md['name']
|
178
|
-
profile_version = profile_md['version']
|
179
|
-
outfile_name = "#{workdir}/#{profile_name}-#{profile_version}.tar.gz"
|
180
|
-
`tar czf #{outfile_name} -C #{path_to_profile} .`
|
181
|
-
outfile_name
|
182
|
-
end
|
183
|
-
|
184
|
-
def profile_sign
|
185
|
-
Dir.mktmpdir do |workdir|
|
186
|
-
puts "Signing #{options['profile']} with key #{options['keyname']}"
|
187
|
-
path_to_profile = options['profile']
|
188
|
-
profile_md = read_profile_metadata(path_to_profile)
|
189
|
-
artifact_filename = "#{profile_md['name']}-#{profile_md['version']}.#{SIGNED_PROFILE_SUFFIX}"
|
190
|
-
tarfile = profile_compress(path_to_profile, profile_md, workdir)
|
191
|
-
content = IO.binread(tarfile)
|
192
|
-
signing_key = KEY_ALG.new File.read "#{options['keyname']}.pem.key"
|
193
|
-
sha = ARTIFACT_DIGEST.new
|
194
|
-
signature = signing_key.sign sha, content
|
195
|
-
# convert the signature to Base64
|
196
|
-
signature_base64 = Base64.encode64(signature)
|
197
|
-
tar_content = IO.binread(tarfile)
|
198
|
-
File.open(artifact_filename, 'wb') do |f|
|
199
|
-
f.puts(INSPEC_PROFILE_VERSION_1)
|
200
|
-
f.puts(options['keyname'])
|
201
|
-
f.puts(ARTIFACT_DIGEST_NAME)
|
202
|
-
f.puts(signature_base64)
|
203
|
-
f.puts('') # newline separates artifact header with body
|
204
|
-
f.write(tar_content)
|
205
|
-
end
|
206
|
-
puts "Successfully generated #{artifact_filename}"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def valid_header?(file_alg, file_version, file_keyname)
|
211
|
-
public_keyfile = "#{file_keyname}.pem.pub"
|
212
|
-
puts "Looking for #{public_keyfile} to verify artifact"
|
213
|
-
if !File.exist? public_keyfile
|
214
|
-
raise "Can't find #{public_keyfile}"
|
215
|
-
end
|
216
|
-
|
217
|
-
raise 'Invalid artifact digest algorithm detected' if !VALID_PROFILE_DIGESTS.member?(file_alg)
|
218
|
-
raise 'Invalid artifact version detected' if !VALID_PROFILE_VERSIONS.member?(file_version)
|
219
|
-
end
|
220
|
-
|
221
|
-
def verify(file_to_verifiy, &content_block)
|
222
|
-
f = File.open(file_to_verifiy, 'r')
|
223
|
-
file_version = f.readline.strip!
|
224
|
-
file_keyname = f.readline.strip!
|
225
|
-
file_alg = f.readline.strip!
|
226
|
-
|
227
|
-
file_sig = ''
|
228
|
-
# the signature is multi-line
|
229
|
-
while (line = f.readline) != "\n"
|
230
|
-
file_sig += line
|
231
|
-
end
|
232
|
-
file_sig.strip!
|
233
|
-
f.close
|
234
|
-
|
235
|
-
valid_header?(file_alg, file_version, file_keyname)
|
236
|
-
|
237
|
-
public_keyfile = "#{file_keyname}.pem.pub"
|
238
|
-
verification_key = KEY_ALG.new File.read public_keyfile
|
239
|
-
|
240
|
-
f = File.open(file_to_verifiy, 'r')
|
241
|
-
while f.readline != "\n" do end
|
242
|
-
content = f.read
|
243
|
-
|
244
|
-
signature = Base64.decode64(file_sig)
|
245
|
-
digest = ARTIFACT_DIGEST.new
|
246
|
-
if verification_key.verify digest, signature, content
|
247
|
-
content_block.yield(content)
|
248
|
-
else
|
249
|
-
puts 'Artifact is invalid'
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def profile_verify
|
254
|
-
file_to_verifiy = options['infile']
|
255
|
-
puts "Verifying #{file_to_verifiy}"
|
256
|
-
verify(file_to_verifiy) do ||
|
257
|
-
puts 'Artifact is valid'
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def profile_install
|
262
|
-
puts 'Installing profile'
|
263
|
-
file_to_verifiy = options['infile']
|
264
|
-
dest_dir = options['destdir']
|
265
|
-
verify(file_to_verifiy) do |content|
|
266
|
-
Dir.mktmpdir do |workdir|
|
267
|
-
tmpfile = Pathname.new(workdir).join('artifact_to_install.tar.gz')
|
268
|
-
File.write(tmpfile, content)
|
269
|
-
puts "Installing to #{dest_dir}"
|
270
|
-
`tar xzf #{tmpfile} -C #{dest_dir}`
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
# register the subcommand to Inspec CLI registry
|
277
|
-
Inspec::Plugins::CLI.add_subcommand(Artifact::CLI, 'artifact', 'artifact SUBCOMMAND ...', 'Sign, verify and install artifacts', {})
|
278
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# author: Adam Leff
|
3
|
-
|
4
|
-
require 'thor'
|
5
|
-
require 'inspec/base_cli'
|
6
|
-
|
7
|
-
module Habitat
|
8
|
-
class HabitatProfileCLI < Thor
|
9
|
-
namespace 'habitat profile'
|
10
|
-
|
11
|
-
desc 'create PATH', 'Create a one-time Habitat artifact for the profile found at PATH'
|
12
|
-
option :output_dir, type: :string, required: false,
|
13
|
-
desc: 'Directory in which to save the generated Habitat artifact. Default: current directory'
|
14
|
-
def create(path)
|
15
|
-
Habitat::Profile.create(path, options)
|
16
|
-
end
|
17
|
-
|
18
|
-
desc 'setup PATH', 'Configure the profile at PATH for Habitat, including a plan and hooks'
|
19
|
-
def setup(path)
|
20
|
-
Habitat::Profile.setup(path)
|
21
|
-
end
|
22
|
-
|
23
|
-
desc 'upload PATH', 'Create a one-time Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
|
24
|
-
def upload(path)
|
25
|
-
Habitat::Profile.upload(path, options)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class HabitatCLI < Inspec::BaseCLI
|
30
|
-
namespace 'habitat'
|
31
|
-
|
32
|
-
desc 'profile', 'Manage InSpec profiles as Habitat artifacts'
|
33
|
-
subcommand 'profile', HabitatProfileCLI
|
34
|
-
end
|
35
|
-
|
36
|
-
Inspec::Plugins::CLI.add_subcommand(HabitatCLI, 'habitat', 'habitat SUBCOMMAND ...', 'Commands for InSpec + Habitat Integration', {})
|
37
|
-
end
|