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
@@ -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
|