inspec-chef 0.1.0
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 +7 -0
- data/Gemfile +14 -0
- data/README.md +102 -0
- data/inspec-chef.gemspec +39 -0
- data/lib/inspec-chef/input.rb +103 -0
- data/lib/inspec-chef/plugin.rb +25 -0
- data/lib/inspec-chef/version.rb +10 -0
- data/lib/inspec-chef.rb +4 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: afe50e575ce5384475bd4c1f0ba240df92727baf14cde3a96f35e2a474eceb8c
|
4
|
+
data.tar.gz: 6f51ad5775fa43d5ea7eff553608f91f24de4e3248ffcd8722fd92c4e393fba7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 645ab50c1f28a99b7b545d205aa460a819bcdc1380853ed6cbdf0538a53dfb140dd08d0733dab722c9a2cc9b9f4198ae808ff5fe0efb8a2e5ac05ca7e7d2b01a
|
7
|
+
data.tar.gz: e7a43170a680e81d11416e72aca2b81e85aa961ca6120fe44e2d4a0c366cebab152741336d3ff5e4ad9f06b914e66de73790ca3ae67bf59f53a4cd832b32491d
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# inspec-chef
|
2
|
+
|
3
|
+
Input plugin for InSpec to access Chef Server data within profiles
|
4
|
+
|
5
|
+
## Use Case
|
6
|
+
|
7
|
+
Some systems rely on Chef Databags or Attributes to be provisioned in the right
|
8
|
+
state, triggering specific configuration which is then hard to verify using
|
9
|
+
InSpec. For example, one system could have an SQL Server 2012 installed
|
10
|
+
while the other has SQL Server 2019. While both might perform an identical
|
11
|
+
role, the InSpec tests have to distinguish between both and get some
|
12
|
+
information on the characteristics to test.
|
13
|
+
|
14
|
+
As the configuration information is already present in those constructs,
|
15
|
+
it makes little sense to manually configure separate profiles.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Simply execute `inspec plugin install inspec-chef`, which will get
|
20
|
+
the plugin from RubyGems and install/register it with InSpec.
|
21
|
+
|
22
|
+
You can verify successful installation via `inspec plugin list`
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
Each plugin option may be set either as an environment variable, or as a plugin
|
27
|
+
option in your Chef InSpec configuration file at ~/.inspec/config.json. For
|
28
|
+
example, to set the chef server information in the config file, lay out the
|
29
|
+
config file as follows:
|
30
|
+
|
31
|
+
```json
|
32
|
+
{
|
33
|
+
"version": "1.2",
|
34
|
+
"plugins":{
|
35
|
+
"inspec-chef":{
|
36
|
+
"chef_api_endpoint": "https://chef.example.com/organizations/testing",
|
37
|
+
"chef_api_client": "workstation",
|
38
|
+
"chef_api_key": "/etc/chef/workstation.pem"
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
```
|
43
|
+
|
44
|
+
Config file option names are always lowercase.
|
45
|
+
|
46
|
+
This plugin supports the following options:
|
47
|
+
|
48
|
+
| Environment Variable | config.json Option | Description |
|
49
|
+
| - | - | - |
|
50
|
+
| INSPEC_CHEF_ENDPOINT | chef_api_endpoint | The URL of your Chef server, including the organization |
|
51
|
+
| INSPEC_CHEF_CLIENT | chef_api_client | The name of the client of the Chef server to connect as |
|
52
|
+
| INSPEC_CHEF_KEY | chef_api_key | Path to the private certificate identifying the node |
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
When this plugin is loaded, you can use databag items as inputs:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
hostname = input('databag://name/item/some/nested/value')
|
60
|
+
|
61
|
+
describe host(hostname, port: 80, protocol: 'tcp') do
|
62
|
+
it { should be_reachable }
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
In the same way, you can also add attributes of arbitary nodes:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
hostname = input('node://name/attributes/some/nested/attribute')
|
70
|
+
|
71
|
+
describe host(hostname, port: 80, protocol: 'tcp') do
|
72
|
+
it { should be_reachable }
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
InSpec will go through all loaded input plugins by priority and determine the value.
|
77
|
+
|
78
|
+
Keep in mind, that the node executing your InSpec runs needs to be
|
79
|
+
registered with the chef server to be able to access the data. Lookup
|
80
|
+
is __not__ done on the clients tested, but the executing workstation.
|
81
|
+
|
82
|
+
## Example
|
83
|
+
|
84
|
+
With this plugin, the input names consist of the name of the databag,
|
85
|
+
the item and a path getting a specific value within an item.
|
86
|
+
This way, you can have a databag "configuration" with an item "database" like:
|
87
|
+
|
88
|
+
```json
|
89
|
+
{
|
90
|
+
"SQL": {
|
91
|
+
"Type": "SQL2019"
|
92
|
+
}
|
93
|
+
}
|
94
|
+
```
|
95
|
+
|
96
|
+
and then use `input('databag://configuration/database/SQL/Type')` to get the
|
97
|
+
"SQL2019" value out.
|
98
|
+
|
99
|
+
## Limitations
|
100
|
+
|
101
|
+
There currently is no support for arrays or more complex expressions within
|
102
|
+
the query, but support via JMESPath expressions is planned.
|
data/inspec-chef.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
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-chef/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-chef"
|
17
|
+
|
18
|
+
# It is polite to namespace your plugin under InspecPlugins::YourPluginInCamelCase
|
19
|
+
spec.version = InspecPlugins::Chef::VERSION
|
20
|
+
spec.authors = ["Thomas Heinen"]
|
21
|
+
spec.email = ["theinen@tecracer.de"]
|
22
|
+
spec.summary = "InSpec input plugin to access Chef Infra Server databags and attributes."
|
23
|
+
spec.description = "This plugin allows InSpec 'inputs' to be provided by Chef Server."
|
24
|
+
spec.homepage = "https://github.com/tecracer-theinen/inspec-chef"
|
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-chef.gemspec Gemfile
|
32
|
+
} + Dir.glob(
|
33
|
+
"lib/**/*", File::FNM_DOTMATCH
|
34
|
+
).reject { |f| File.directory?(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
|
37
|
+
spec.add_dependency "chef-api", "~> 0.10"
|
38
|
+
spec.add_dependency "jmespath", "~> 1.4"
|
39
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "chef-api"
|
2
|
+
require "jmespath"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module InspecPlugins::Chef
|
6
|
+
class Input < Inspec.plugin(2, :input)
|
7
|
+
VALID_PATTERNS = [
|
8
|
+
Regexp.new("^databag://[^/]+/[^/]+/.+$"),
|
9
|
+
Regexp.new("^node://[^/]+/attributes/.+$"),
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
attr_reader :plugin_conf, :chef_endpoint, :chef_client, :chef_api_key
|
13
|
+
attr_reader :chef_api
|
14
|
+
|
15
|
+
# Set up new class
|
16
|
+
def initialize
|
17
|
+
@plugin_conf = Inspec::Config.cached.fetch_plugin_config("inspec-chef")
|
18
|
+
|
19
|
+
@chef_endpoint = fetch_plugin_setting("endpoint")
|
20
|
+
@chef_client = fetch_plugin_setting("client")
|
21
|
+
@chef_api_key = fetch_plugin_setting("key")
|
22
|
+
|
23
|
+
if chef_endpoint.nil? || chef_client.nil? || chef_api_key.nil?
|
24
|
+
raise "ERROR: Need configuration of chef endpoint, client name and api key."
|
25
|
+
end
|
26
|
+
|
27
|
+
connect_to_chef_server
|
28
|
+
end
|
29
|
+
|
30
|
+
# Fetch method used for Input plugins
|
31
|
+
def fetch(_profile_name, input_uri)
|
32
|
+
return nil unless valid_plugin_input? input_uri
|
33
|
+
|
34
|
+
input = parse_input(input_uri)
|
35
|
+
|
36
|
+
if input[:type] == :databag
|
37
|
+
data = get_databag_item(input[:object], input[:item])
|
38
|
+
elsif input[:type] == :node
|
39
|
+
data = get_attributes(input[:object]) if input[:item] == "attributes"
|
40
|
+
end
|
41
|
+
|
42
|
+
JMESPath.search(input[:query].join("."), data)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Get plugin setting via environment, config file or default
|
48
|
+
def fetch_plugin_setting(setting_name, default = nil)
|
49
|
+
env_var_name = "INSPEC_CHEF_#{setting_name.upcase}"
|
50
|
+
config_name = "chef_api_#{setting_name.downcase}"
|
51
|
+
ENV[env_var_name] || plugin_conf[config_name] || default
|
52
|
+
end
|
53
|
+
|
54
|
+
# Establish a Chef Server connection
|
55
|
+
def connect_to_chef_server
|
56
|
+
@chef_api ||= ChefAPI::Connection.new(
|
57
|
+
endpoint: chef_endpoint,
|
58
|
+
client: chef_client,
|
59
|
+
key: chef_api_key
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Retrieve a Databag item from Chef Server
|
64
|
+
def get_databag_item(databag, item)
|
65
|
+
chef_api.data_bag_item.fetch(item, bag: databag).data
|
66
|
+
end
|
67
|
+
|
68
|
+
# Retrieve attributes of a node
|
69
|
+
def get_attributes(node)
|
70
|
+
data = get_search(:node, "name:#{node}")
|
71
|
+
|
72
|
+
merge_attributes(data)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Low-level Chef search expression
|
76
|
+
def get_search(index, expression)
|
77
|
+
chef_api.search.query(index, expression).rows.first
|
78
|
+
end
|
79
|
+
|
80
|
+
# Merge attributes in hierarchy like Chef
|
81
|
+
def merge_attributes(data)
|
82
|
+
data["default"].merge(data["normal"]).merge(data["override"]).merge(data["automatic"])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Verify if input is valid for this plugin
|
86
|
+
def valid_plugin_input?(input)
|
87
|
+
VALID_PATTERNS.any? { |regex| regex.match? input }
|
88
|
+
end
|
89
|
+
|
90
|
+
# Parse InSpec input name into Databag, Item and search query
|
91
|
+
def parse_input(input_uri)
|
92
|
+
uri = URI(input_uri)
|
93
|
+
item, *components = uri.path.slice(1..-1).split("/")
|
94
|
+
|
95
|
+
{
|
96
|
+
type: uri.scheme.to_sym,
|
97
|
+
object: uri.host,
|
98
|
+
item: item,
|
99
|
+
query: components,
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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
|
+
require "inspec-chef/version"
|
12
|
+
module InspecPlugins
|
13
|
+
module Chef
|
14
|
+
class Plugin < ::Inspec.plugin(2)
|
15
|
+
# Internal machine name of the plugin. InSpec will use this in errors, etc.
|
16
|
+
plugin_name :'inspec-chef'
|
17
|
+
|
18
|
+
# Define an Input plugin type.
|
19
|
+
input :chef do
|
20
|
+
require_relative "input.rb"
|
21
|
+
InspecPlugins::Chef::Input
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/inspec-chef.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: inspec-chef
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Heinen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: chef-api
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jmespath
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
41
|
+
description: This plugin allows InSpec 'inputs' to be provided by Chef Server.
|
42
|
+
email:
|
43
|
+
- theinen@tecracer.de
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- inspec-chef.gemspec
|
51
|
+
- lib/inspec-chef.rb
|
52
|
+
- lib/inspec-chef/input.rb
|
53
|
+
- lib/inspec-chef/plugin.rb
|
54
|
+
- lib/inspec-chef/version.rb
|
55
|
+
homepage: https://github.com/tecracer-theinen/inspec-chef
|
56
|
+
licenses:
|
57
|
+
- Apache-2.0
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.0.3
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: InSpec input plugin to access Chef Infra Server databags and attributes.
|
78
|
+
test_files: []
|