inspec-vault 0.0.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +16 -0
- data/README.md +114 -0
- data/inspec-vault.gemspec +40 -0
- data/lib/inspec-vault.rb +16 -0
- data/lib/inspec-vault/input.rb +79 -0
- data/lib/inspec-vault/plugin.rb +27 -0
- data/lib/inspec-vault/version.rb +10 -0
- metadata +33 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cfbbbb425fb095bdf5a4124f20a3475f87f6d983074339889156e70eccb1cbc7
|
4
|
+
data.tar.gz: c45dcca2381837fbb2380467ac66eae9321fce02ef23d5ab8cea27403ce9ca21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe1329781eca561a3788ed933bc5a46729447819be483e047095217caf4ae53d233e23936bf0201729e99b5c3cfc3a7096b7b9586c58a52908dec34fdaa88684
|
7
|
+
data.tar.gz: 803e458ad47d5f518691b0e265c997689ffa045c3c144be241361436e342669f4fc4229f3c888c411b1c97657b19f2f01a391ca6e5601d277fffcb6cbb3bbbe6
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# inspec-vault Plugin
|
2
|
+
|
3
|
+
This is a plugin for [Chef InSpec](https://www.inspec.io/) that allows [Inputs](https://www.inspec.io/docs/reference/inputs/) to be read from [HashiCorp Vault](https://www.vaultproject.io/).
|
4
|
+
|
5
|
+
* **Project State: Active** (but EXPERIMENTAL)
|
6
|
+
* **Issues Response SLA: 3 business days**
|
7
|
+
* **Pull Request Response SLA: 3 business days**
|
8
|
+
|
9
|
+
For more information on project states and SLAs, see [this documentation](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md).
|
10
|
+
|
11
|
+
## Notice - Experimental Project
|
12
|
+
|
13
|
+
This Chef InSpec plugin is in the early stages of research and development. Functionality may be defective, incomplete, or be withdrawn in the future. If you are interested in helping this project mature, please join the conversation or contribute code at the [inspec-vault project](https://github.com/inspec/inspec-vault).
|
14
|
+
|
15
|
+
## To Install This Plugin
|
16
|
+
|
17
|
+
Assuming it has been published to RubyGems, you can install this gem using:
|
18
|
+
|
19
|
+
```
|
20
|
+
you@machine $ inspec plugin install inspec-vault
|
21
|
+
```
|
22
|
+
|
23
|
+
## Loading Secrets into Vault
|
24
|
+
|
25
|
+
A full introduction to Vault is beyond the scope of this document, but begin by downloading a recent version from https://www.vaultproject.io . Then, start a Vault dev-mode server with the following command:
|
26
|
+
|
27
|
+
```
|
28
|
+
$ vault server -dev
|
29
|
+
```
|
30
|
+
|
31
|
+
From there, you can then store an input. For example, look at the command below to store an input named `my_input` with the value of 2, for the `my_profile` profile. Once entered, Vault responds with metadata about the entry.
|
32
|
+
|
33
|
+
```
|
34
|
+
[cwolfe@lodi inspec-vault]$ vault kv put secret/inspec/my_profile my_input=2
|
35
|
+
Key Value
|
36
|
+
--- -----
|
37
|
+
created_time 2019-09-10T17:54:16.237055Z
|
38
|
+
deletion_time n/a
|
39
|
+
destroyed false
|
40
|
+
version 1
|
41
|
+
```
|
42
|
+
|
43
|
+
With that value stored, Chef InSpec will now be able to retrieve the value.
|
44
|
+
|
45
|
+
## What This Plugin Does
|
46
|
+
|
47
|
+
With the inspec-vault plugin enabled, Chef InSpec will contact the Vault server whenever an `input()` DSL call appears in profile control code. For example, whenever profile code like this is encountered:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# In profile "my_profile"
|
51
|
+
describe input("some_input") do
|
52
|
+
it { should cmp "some_expected_value" }
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
Chef InSpec will determine a secret lookup path and access Vault. With no other settings, Chef InSpec will look for a Vault secret located at `secret/inspec/my_profile` with a key named "some_input". Chef InSpec will use the Vault secret if found, but otherwise it will fall back to other means of resolving the input, such as the profile metadata or CLI values.
|
57
|
+
|
58
|
+
## Configuring the Plugin
|
59
|
+
|
60
|
+
Each plugin option may be set either as an environment variable, or as a plugin option in your Chef InSpec configuration file at `~/.inspec/config.json`. For example, to set the `prefix_path` option in the config file, lay out the config file as follows:
|
61
|
+
|
62
|
+
```json
|
63
|
+
{
|
64
|
+
"version": "1.2",
|
65
|
+
"plugins":{
|
66
|
+
"inspec-vault":{
|
67
|
+
"prefix_path":"my-profiles"
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
```
|
72
|
+
|
73
|
+
Config file option names are always lowercase.
|
74
|
+
|
75
|
+
This plugin supports the following options:
|
76
|
+
|
77
|
+
### INSPEC_VAULT_MOUNT_POINT
|
78
|
+
|
79
|
+
### mount_point
|
80
|
+
|
81
|
+
A string that indicates where the key-value path should begin; default value is "secret". The path is constructed as `<mount_point>/data/<path_prefix>/<profile_name>`.
|
82
|
+
|
83
|
+
### INSPEC_VAULT_PATH_PREFIX
|
84
|
+
|
85
|
+
### path_prefix
|
86
|
+
|
87
|
+
A string that indicates the latter portion of the key-value path; default value is "inspec". The path is constructed as `<mount_point>/data/<path_prefix>/<profile_name>`.
|
88
|
+
|
89
|
+
### INSPEC_VAULT_PRIORITY
|
90
|
+
|
91
|
+
### priority
|
92
|
+
|
93
|
+
A number between 0 and 100, default 60. When two input provides both provide a value for the same input name, the priority determines which providers' value is used, with the higher priority prevailing. Core Chef InSpec providers only range up to 50, so inspec-vault will (by default) override any other input provider.
|
94
|
+
|
95
|
+
### VAULT_ADDR
|
96
|
+
|
97
|
+
### vault_addr
|
98
|
+
|
99
|
+
This environment variable is the URL and port of your Vault installation. Default is http://127.0.0.1:8200.
|
100
|
+
|
101
|
+
### VAULT_TOKEN
|
102
|
+
|
103
|
+
This value is the secret used to authenticate to Vault. Required, no default provided.
|
104
|
+
|
105
|
+
## Developing This Plugin
|
106
|
+
|
107
|
+
Please have a look at our CONTRIBUTING.md for general guidelines.
|
108
|
+
|
109
|
+
### Testing
|
110
|
+
|
111
|
+
Run `bundle exec rake test:lint` for linting, `bundle exec rake test:unit` for unit tests, and `bundle exec rake test:integration` for integration tests.
|
112
|
+
|
113
|
+
Note that integration tests will download and run Vault server locally.
|
114
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# As plugins are usually packaged and distributed as a RubyGem,
|
4
|
+
# we have to provide a .gemspec file, which controls the gembuild
|
5
|
+
# and publish process. This is a fairly generic gemspec.
|
6
|
+
|
7
|
+
# It is traditional in a gemspec to dynamically load the current version
|
8
|
+
# from a file in the source tree. The next three lines make that happen.
|
9
|
+
lib = File.expand_path("../lib", __FILE__)
|
10
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
11
|
+
require "inspec-vault/version"
|
12
|
+
|
13
|
+
Gem::Specification.new do |spec|
|
14
|
+
# Importantly, all InSpec plugins must be prefixed with `inspec-` (most
|
15
|
+
# plugins) or `train-` (plugins which add new connectivity features).
|
16
|
+
spec.name = "inspec-vault"
|
17
|
+
|
18
|
+
# It is polite to namespace your plugin under InspecPlugins::YourPluginInCamelCase
|
19
|
+
spec.version = InspecPlugins::Vault::VERSION
|
20
|
+
spec.authors = ["InSpec Core Engineering"]
|
21
|
+
spec.email = ["inspec@chef.io"]
|
22
|
+
spec.summary = "Use HashiCorp Vault data in your InSpec profiles"
|
23
|
+
spec.description = "This plugin allows InSpec 'inputs' to be provided by a HashiCorp Vault installation. This enables you to unify your secrets management with your compliance automation."
|
24
|
+
spec.homepage = "https://github.com/inspec/inspec-vault"
|
25
|
+
spec.license = "Apache-2.0"
|
26
|
+
|
27
|
+
# Though complicated-looking, this is pretty standard for a gemspec.
|
28
|
+
# It just filters what will actually be packaged in the gem (leaving
|
29
|
+
# out tests, etc)
|
30
|
+
spec.files = %w{
|
31
|
+
README.md inspec-vault.gemspec Gemfile
|
32
|
+
} + Dir.glob(
|
33
|
+
"lib/**/*", File::FNM_DOTMATCH
|
34
|
+
).reject { |f| File.directory?(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
|
37
|
+
# If you rely on any other gems, list them here with any constraints.
|
38
|
+
# This is how `inspec plugin install` is able to manage your dependencies.
|
39
|
+
spec.add_dependency "vault", "~> 0.12"
|
40
|
+
end
|
data/lib/inspec-vault.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This file is known as the "entry point."
|
4
|
+
# This is the file InSpec will try to load if it
|
5
|
+
# thinks your plugin is installed.
|
6
|
+
|
7
|
+
# The *only* thing this file should do is setup the
|
8
|
+
# load path, then load the plugin definition file.
|
9
|
+
|
10
|
+
# Next two lines simply add the path of the gem to the load path.
|
11
|
+
# This is not needed when being loaded as a gem; but when doing
|
12
|
+
# plugin development, you may need it. Either way, it's harmless.
|
13
|
+
libdir = File.dirname(__FILE__)
|
14
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
15
|
+
|
16
|
+
require "inspec-vault/plugin"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "vault"
|
2
|
+
|
3
|
+
# See https://github.com/inspec/inspec/blob/master/docs/dev/plugins.md#implementing-input-plugins
|
4
|
+
|
5
|
+
module InspecPlugins::Vault
|
6
|
+
class Input < Inspec.plugin(2, :input)
|
7
|
+
|
8
|
+
attr_reader :plugin_conf
|
9
|
+
attr_reader :mount_point
|
10
|
+
attr_reader :path_prefix
|
11
|
+
attr_reader :vault
|
12
|
+
attr_reader :priority
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@plugin_conf = Inspec::Config.cached.fetch_plugin_config("inspec-vault")
|
16
|
+
|
17
|
+
@mount_point = fetch_plugin_setting("mount_point", "secret")
|
18
|
+
@path_prefix = fetch_plugin_setting("path_prefix", "inspec")
|
19
|
+
|
20
|
+
# We need priority to be numeric; even though env vars or JSON may present it as string - hence the to_i
|
21
|
+
@priority = fetch_plugin_setting("priority", 60).to_i
|
22
|
+
|
23
|
+
@vault = Vault::Client.new(
|
24
|
+
address: fetch_vault_setting("vault_addr"),
|
25
|
+
token: fetch_vault_setting("vault_token")
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# What priority should an input value recieve from us?
|
30
|
+
# This plgin does not currently allow setting this on a per-input basis,
|
31
|
+
# so they all recieve the same "default" value.
|
32
|
+
# Implements https://github.com/inspec/inspec/blob/master/docs/dev/plugins.md#default_priority
|
33
|
+
def default_priority
|
34
|
+
priority
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns Array of input names as strings
|
38
|
+
def list_inputs(profile_name)
|
39
|
+
vault.with_retries(Vault::HTTPConnectionError) do
|
40
|
+
path = logical_path_for_profile(profile_name)
|
41
|
+
doc = vault.logical.read(path)
|
42
|
+
return [] unless doc
|
43
|
+
return doc.data[:data].keys.map(&:to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Fetch a value of a single input from Vault
|
48
|
+
# Assumption: inputs have been stored on documents named for their
|
49
|
+
# profiles, and each input has a key-value pair in the document.
|
50
|
+
# TODO we should probably cache these - https://github.com/inspec/inspec-vault/issues/15
|
51
|
+
def fetch(profile_name, input_name)
|
52
|
+
path = logical_path_for_profile(profile_name)
|
53
|
+
vault.with_retries(Vault::HTTPConnectionError) do
|
54
|
+
doc = vault.logical.read(path)
|
55
|
+
# Keys from vault are always symbolized
|
56
|
+
return doc.data[:data][input_name.to_sym] if doc
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def logical_path_for_profile(profile_name)
|
63
|
+
# When you actually read a value, on the KV2 backend you must
|
64
|
+
# read secret/data/path, not secret/path (as on the CLI)
|
65
|
+
# https://www.vaultproject.io/api/secret/kv/kv-v2.html#read-secret-version
|
66
|
+
# Is this true for all backends?
|
67
|
+
"#{mount_point}/data/#{path_prefix}/#{profile_name}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch_plugin_setting(setting_name, default = nil)
|
71
|
+
env_var_name = "INSPEC_VAULT_#{setting_name.upcase}"
|
72
|
+
ENV[env_var_name] || plugin_conf[setting_name] || default
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch_vault_setting(setting_name)
|
76
|
+
ENV[setting_name.upcase] || plugin_conf[setting_name]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Plugin Definition file
|
4
|
+
# The purpose of this file is to declare to InSpec what plugin_types (capabilities)
|
5
|
+
# are included in this plugin, and provide hooks that will load them as needed.
|
6
|
+
|
7
|
+
# It is important that this file load successfully and *quickly*.
|
8
|
+
# Your plugin's functionality may never be used on this InSpec run; so we keep things
|
9
|
+
# fast and light by only loading heavy things when they are needed.
|
10
|
+
|
11
|
+
# Presumably this is light
|
12
|
+
require "inspec-vault/version"
|
13
|
+
module InspecPlugins
|
14
|
+
module Vault
|
15
|
+
class Plugin < ::Inspec.plugin(2)
|
16
|
+
# Internal machine name of the plugin. InSpec will use this in errors, etc.
|
17
|
+
plugin_name :'inspec-vault'
|
18
|
+
|
19
|
+
# Define an Input plugin type.
|
20
|
+
input :vault do
|
21
|
+
require_relative "input.rb"
|
22
|
+
InspecPlugins::Vault::Input
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,23 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-vault
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- InSpec Core Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2019-09-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: vault
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.12'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.12'
|
27
|
+
description: This plugin allows InSpec 'inputs' to be provided by a HashiCorp Vault
|
28
|
+
installation. This enables you to unify your secrets management with your compliance
|
29
|
+
automation.
|
14
30
|
email:
|
15
|
-
-
|
31
|
+
- inspec@chef.io
|
16
32
|
executables: []
|
17
33
|
extensions: []
|
18
34
|
extra_rdoc_files: []
|
19
|
-
files:
|
20
|
-
|
35
|
+
files:
|
36
|
+
- Gemfile
|
37
|
+
- README.md
|
38
|
+
- inspec-vault.gemspec
|
39
|
+
- lib/inspec-vault.rb
|
40
|
+
- lib/inspec-vault/input.rb
|
41
|
+
- lib/inspec-vault/plugin.rb
|
42
|
+
- lib/inspec-vault/version.rb
|
43
|
+
homepage: https://github.com/inspec/inspec-vault
|
21
44
|
licenses:
|
22
45
|
- Apache-2.0
|
23
46
|
metadata: {}
|
@@ -36,9 +59,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
59
|
- !ruby/object:Gem::Version
|
37
60
|
version: '0'
|
38
61
|
requirements: []
|
39
|
-
|
40
|
-
rubygems_version: 2.6.14
|
62
|
+
rubygems_version: 3.0.3
|
41
63
|
signing_key:
|
42
64
|
specification_version: 4
|
43
|
-
summary:
|
65
|
+
summary: Use HashiCorp Vault data in your InSpec profiles
|
44
66
|
test_files: []
|