inspec-chef 0.2.0 → 0.3.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 +4 -4
- data/README.md +28 -20
- data/lib/inspec-chef/input.rb +57 -10
- data/lib/inspec-chef/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3689a6a012e43692366a808c0b50e67e7ef4cb35b303593f84ca0d0cd485fd9
|
4
|
+
data.tar.gz: 9c21ced8d8c706daf806553c6317b8ff375b3f5d40b0a6ac06573a33c6cb5df4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2f09ae34094154758c65afa303f4311ab2a4ea85f70b0a9e96a4cf58791c03cf74b33efce3f9477c26464de24027ce36c80fed92b3902949a7ecbef0219db62
|
7
|
+
data.tar.gz: a71465e35f0ed3a99b5ce1e61f8252669b8a4544d1bddac24c447bdfbf55d66a58598bc4644b412975c50fdf8c8ca5a5ca269255d46a7552030b094459385dae
|
data/README.md
CHANGED
@@ -53,10 +53,10 @@ This plugin supports the following options:
|
|
53
53
|
|
54
54
|
## Configuration for TestKitchen
|
55
55
|
|
56
|
-
To allow dev/prod parity, this input plugin detects if it is called
|
57
|
-
TestKitchen. As these tests should not access the Chef Server (to
|
58
|
-
needed test data instead of live data), it will then revert on using
|
59
|
-
`data_bags_path` and `attributes` from kitchen's `provisioner` section:
|
56
|
+
To allow for more dev/prod parity, this input plugin detects if it is called
|
57
|
+
from within TestKitchen. As these tests should not access the Chef Server (to
|
58
|
+
provide the needed test data instead of live data), it will then revert on using
|
59
|
+
the `data_bags_path` and `attributes` from kitchen's `provisioner` section:
|
60
60
|
|
61
61
|
```yaml
|
62
62
|
suites:
|
@@ -78,34 +78,32 @@ and below of the `kitchen-inspec` verifier plugin. Please check
|
|
78
78
|
When this plugin is loaded, you can use databag items as inputs:
|
79
79
|
|
80
80
|
```ruby
|
81
|
-
hostname = input('databag://
|
81
|
+
hostname = input('databag://databag_name/item/some/nested/value')
|
82
82
|
|
83
83
|
describe host(hostname, port: 80, protocol: 'tcp') do
|
84
84
|
it { should be_reachable }
|
85
85
|
end
|
86
86
|
```
|
87
87
|
|
88
|
-
In the same way, you can also add attributes of
|
88
|
+
In the same way, you can also add attributes of arbitrary nodes:
|
89
89
|
|
90
90
|
```ruby
|
91
|
-
hostname = input('node://
|
91
|
+
hostname = input('node://node_name/attributes/some/nested/attribute')
|
92
92
|
|
93
93
|
describe host(hostname, port: 80, protocol: 'tcp') do
|
94
94
|
it { should be_reachable }
|
95
95
|
end
|
96
96
|
```
|
97
97
|
|
98
|
-
|
98
|
+
It is possible to skip the node name, in which case the plugin will try to look up
|
99
|
+
the Chef client name of the node being scanned. This will, depending on the address
|
100
|
+
passed to InSpec, check via `ipaddress:`, `hostname:` or `fqdn:` queries. If this
|
101
|
+
fails, another lookup will be tried for Amazon EC2 via `ec2_public_ipv4` and
|
102
|
+
`ec2_public_hostname`.
|
99
103
|
|
100
|
-
|
101
|
-
registered with the chef server to be able to access the data. Lookup
|
102
|
-
is __not__ done on the clients tested, but the executing workstation.
|
103
|
-
|
104
|
-
## Example
|
104
|
+
## Databags Example
|
105
105
|
|
106
|
-
|
107
|
-
the item and a path getting a specific value within an item.
|
108
|
-
This way, you can have a databag "configuration" with an item "database" like:
|
106
|
+
If you have a databag "configuration" with an item "database" like:
|
109
107
|
|
110
108
|
```json
|
111
109
|
{
|
@@ -115,10 +113,20 @@ This way, you can have a databag "configuration" with an item "database" like:
|
|
115
113
|
}
|
116
114
|
```
|
117
115
|
|
118
|
-
|
119
|
-
"SQL2019" value
|
116
|
+
you can use `input('databag://configuration/database/SQL/Type')` to get the
|
117
|
+
"SQL2019" value.
|
118
|
+
|
119
|
+
### Important Note
|
120
|
+
|
121
|
+
Keep in mind, that the node executing your InSpec runs needs to be
|
122
|
+
registered with the Chef Infra Server to be able to access the data. Lookup
|
123
|
+
is __not__ done on the clients tested, but the workstation executing InSpec.
|
120
124
|
|
121
125
|
## Limitations
|
122
126
|
|
123
|
-
There currently is no support for arrays or more complex expressions within
|
124
|
-
the query, but support via JMESPath expressions is planned.
|
127
|
+
- There currently is no support for arrays or more complex expressions within
|
128
|
+
the query, but support via JMESPath expressions is planned.
|
129
|
+
- Automatic Chef node name lookup will fail for addresses not searchable in the
|
130
|
+
`ipaddress`, `hostname` or `fqdn` fields. One case would be IPv6 target
|
131
|
+
nodes. Trying to resolve will result in error "Unable too lookup remote Chef
|
132
|
+
client name"
|
data/lib/inspec-chef/input.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require "chef-api"
|
2
2
|
require "jmespath"
|
3
|
+
require "resolv"
|
3
4
|
require "uri"
|
4
5
|
|
5
6
|
module InspecPlugins::Chef
|
6
7
|
class Input < Inspec.plugin(2, :input)
|
7
8
|
VALID_PATTERNS = [
|
8
9
|
Regexp.new("^databag://[^/]+/[^/]+/.+$"),
|
9
|
-
Regexp.new("^node://[^/]
|
10
|
+
Regexp.new("^node://[^/]*/attributes/.+$"),
|
10
11
|
].freeze
|
11
12
|
|
12
13
|
attr_reader :plugin_conf, :chef_endpoint, :chef_client, :chef_api_key
|
@@ -16,7 +17,7 @@ module InspecPlugins::Chef
|
|
16
17
|
def initialize
|
17
18
|
@plugin_conf = Inspec::Config.cached.fetch_plugin_config("inspec-chef")
|
18
19
|
|
19
|
-
unless
|
20
|
+
unless defined?(Kitchen)
|
20
21
|
@chef_endpoint = fetch_plugin_setting("endpoint")
|
21
22
|
@chef_client = fetch_plugin_setting("client")
|
22
23
|
@chef_api_key = fetch_plugin_setting("key")
|
@@ -41,7 +42,10 @@ module InspecPlugins::Chef
|
|
41
42
|
data = get_attributes(input[:object]) if input[:item] == "attributes"
|
42
43
|
end
|
43
44
|
|
44
|
-
JMESPath.search(input[:query].join("."), data)
|
45
|
+
result = JMESPath.search(input[:query].join("."), data)
|
46
|
+
raise format("Could not resolve value for %s, check if databag/item or attribute exist", input_uri) if result.nil?
|
47
|
+
|
48
|
+
result
|
45
49
|
end
|
46
50
|
|
47
51
|
private
|
@@ -51,10 +55,24 @@ module InspecPlugins::Chef
|
|
51
55
|
!! defined?(::Kitchen::Logger)
|
52
56
|
end
|
53
57
|
|
58
|
+
# Check if this is an IP
|
59
|
+
def ip?(ip_or_name)
|
60
|
+
# Get address always returns an IP, so if nothing changes this was one
|
61
|
+
Resolv.getaddress(ip_or_name) == ip_or_name
|
62
|
+
rescue Resolv::ResolvError
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if this is an FQDN
|
67
|
+
def fqdn?(ip_or_name)
|
68
|
+
# If it is not an IP but contains a Dot, it is an FQDN
|
69
|
+
!ip?(ip_or_name) && ip_or_name.include?(".")
|
70
|
+
end
|
71
|
+
|
54
72
|
# Reach for Kitchen data and return its evaluated config
|
55
73
|
def kitchen_provisioner_config
|
56
|
-
require
|
57
|
-
kitchen = binding.callers.find { |b| b.frame_description ==
|
74
|
+
require "binding_of_caller"
|
75
|
+
kitchen = binding.callers.find { |b| b.frame_description == "verify" }.receiver
|
58
76
|
|
59
77
|
kitchen.provisioner.send(:provided_config)
|
60
78
|
end
|
@@ -66,6 +84,12 @@ module InspecPlugins::Chef
|
|
66
84
|
ENV[env_var_name] || plugin_conf[config_name] || default
|
67
85
|
end
|
68
86
|
|
87
|
+
# Get remote address for this scan from InSpec
|
88
|
+
def inspec_target
|
89
|
+
target = Inspec::Config.cached.final_options["target"]
|
90
|
+
URI.parse(target)&.host
|
91
|
+
end
|
92
|
+
|
69
93
|
# Establish a Chef Server connection
|
70
94
|
def connect_to_chef_server
|
71
95
|
@chef_api ||= ChefAPI::Connection.new(
|
@@ -73,6 +97,8 @@ module InspecPlugins::Chef
|
|
73
97
|
client: chef_client,
|
74
98
|
key: chef_api_key
|
75
99
|
)
|
100
|
+
|
101
|
+
Inspec::Log.debug format("Connected to %s as client %s", chef_endpoint, chef_client)
|
76
102
|
end
|
77
103
|
|
78
104
|
# Retrieve a Databag item from Chef Server
|
@@ -85,12 +111,12 @@ module InspecPlugins::Chef
|
|
85
111
|
chef_api.data_bag_item.fetch(item, bag: databag).data
|
86
112
|
else
|
87
113
|
config = kitchen_provisioner_config
|
88
|
-
filename = File.join(config[:data_bags_path], databag, item +
|
114
|
+
filename = File.join(config[:data_bags_path], databag, item + ".json")
|
89
115
|
|
90
116
|
begin
|
91
|
-
|
117
|
+
return JSON.load(File.read(filename))
|
92
118
|
rescue
|
93
|
-
raise format(
|
119
|
+
raise format("Error accessing databag file %s, check TestKitchen configuration", filename)
|
94
120
|
end
|
95
121
|
end
|
96
122
|
end
|
@@ -106,9 +132,30 @@ module InspecPlugins::Chef
|
|
106
132
|
end
|
107
133
|
end
|
108
134
|
|
135
|
+
# Try to look up Chef Client name by the address requested
|
136
|
+
def get_clientname(ip_or_name)
|
137
|
+
query = "hostname:%<address>s"
|
138
|
+
query = "ipaddress:%<address>s" if ip?(ip_or_name)
|
139
|
+
query = "fqdn:%<address>s" if fqdn?(ip_or_name)
|
140
|
+
result = get_search(:node, format(query, address: ip_or_name))
|
141
|
+
Inspec::Log.debug format("Automatic lookup of node name (IPv4 or hostname) returned: %s", result&.fetch("name") || "(nothing)")
|
142
|
+
|
143
|
+
# Try EC2 lookup, if nothing found (assuming public IP)
|
144
|
+
if result.nil?
|
145
|
+
query = "ec2_public_ipv4:%<address>s OR ec2_public_hostname:%<address>s"
|
146
|
+
result = get_search(:node, format(query, address: ip_or_name))
|
147
|
+
Inspec::Log.debug format("Automatic lookup of node name (EC2 public IPv4 or hostname) returned: %s", result&.fetch("name"))
|
148
|
+
end
|
149
|
+
|
150
|
+
# This will fail for cases like trying to connect to IPv6, so it will
|
151
|
+
# need extension in the future
|
152
|
+
|
153
|
+
result&.fetch("name") || raise(format("Unable too lookup remote Chef client name from %s", ip_or_name))
|
154
|
+
end
|
155
|
+
|
109
156
|
# Low-level Chef search expression
|
110
157
|
def get_search(index, expression)
|
111
|
-
chef_api.search.query(index, expression).rows.first
|
158
|
+
chef_api.search.query(index, expression, rows: 1).rows.first
|
112
159
|
end
|
113
160
|
|
114
161
|
# Merge attributes in hierarchy like Chef
|
@@ -128,7 +175,7 @@ module InspecPlugins::Chef
|
|
128
175
|
|
129
176
|
{
|
130
177
|
type: uri.scheme.to_sym,
|
131
|
-
object: uri.host,
|
178
|
+
object: uri.host || get_clientname(inspec_target),
|
132
179
|
item: item,
|
133
180
|
query: components,
|
134
181
|
}
|
data/lib/inspec-chef/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-chef
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Heinen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-api
|