inspec-chef 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f13512640633f549c0a190e61f94cc2044c9d551401ccb4371e8f1cd37cd102b
4
- data.tar.gz: a47a60292470aad2385de9697c024f7457883fc64eedff78b455a79f7406b367
3
+ metadata.gz: e3689a6a012e43692366a808c0b50e67e7ef4cb35b303593f84ca0d0cd485fd9
4
+ data.tar.gz: 9c21ced8d8c706daf806553c6317b8ff375b3f5d40b0a6ac06573a33c6cb5df4
5
5
  SHA512:
6
- metadata.gz: e848365caa7f68c3a0f4509284a530f62095274540787201a458c39fdd077862765261b46d95acc9267874580c0f9045914172f5a53da284114e889831639936
7
- data.tar.gz: 4ff246d1449d0266ebb3c8bb9774f7f77f0ff209f04cb9263defb492bc628d0ba964740c8fcd803a011507dca113a842a15e8797496b7319ff76cceaef8f91ce
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 from within
57
- TestKitchen. As these tests should not access the Chef Server (to provide the
58
- needed test data instead of live data), it will then revert on using the
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://name/item/some/nested/value')
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 arbitary nodes:
88
+ In the same way, you can also add attributes of arbitrary nodes:
89
89
 
90
90
  ```ruby
91
- hostname = input('node://name/attributes/some/nested/attribute')
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
- InSpec will go through all loaded input plugins by priority and determine the value.
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
- Keep in mind, that the node executing your InSpec runs needs to be
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
- With this plugin, the input names consist of the name of the databag,
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
- and then use `input('databag://configuration/database/SQL/Type')` to get the
119
- "SQL2019" value out.
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"
@@ -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://[^/]+/attributes/.+$"),
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 Inspec::Config.cached.final_options.logger.is_a?(Kitchen::Logger)
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 'binding_of_caller'
57
- kitchen = binding.callers.find { |b| b.frame_description == 'verify' }.receiver
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 + '.json')
114
+ filename = File.join(config[:data_bags_path], databag, item + ".json")
89
115
 
90
116
  begin
91
- contents = JSON.load(File.read(filename))
117
+ return JSON.load(File.read(filename))
92
118
  rescue
93
- raise format('Error accessing databag file %s, check TestKitchen configuration', filename)
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
  }
@@ -5,6 +5,6 @@
5
5
  # to learn the current version.
6
6
  module InspecPlugins
7
7
  module Chef
8
- VERSION = "0.2.0".freeze
8
+ VERSION = "0.3.0".freeze
9
9
  end
10
10
  end
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.2.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-10 00:00:00.000000000 Z
11
+ date: 2020-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-api