chef 16.7.61-universal-mingw32 → 16.8.9-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/README.md +1 -1
  4. data/chef.gemspec +2 -1
  5. data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll +0 -0
  6. data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll +0 -0
  7. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
  8. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
  9. data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
  10. data/distro/ruby_bin_folder/x86/Chef.PowerShell.dll +0 -0
  11. data/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll +0 -0
  12. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
  13. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
  14. data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
  15. data/lib/chef/application/base.rb +1 -1
  16. data/lib/chef/client.rb +3 -0
  17. data/lib/chef/compliance/default_attributes.rb +89 -0
  18. data/lib/chef/compliance/fetcher/automate.rb +69 -0
  19. data/lib/chef/compliance/fetcher/chef_server.rb +134 -0
  20. data/lib/chef/compliance/reporter/automate.rb +202 -0
  21. data/lib/chef/compliance/reporter/chef_server_automate.rb +92 -0
  22. data/lib/chef/compliance/reporter/compliance_enforcer.rb +20 -0
  23. data/lib/chef/compliance/reporter/json_file.rb +19 -0
  24. data/lib/chef/compliance/runner.rb +250 -0
  25. data/lib/chef/cookbook_manifest.rb +1 -0
  26. data/lib/chef/encrypted_data_bag_item/assertions.rb +1 -1
  27. data/lib/chef/exceptions.rb +4 -0
  28. data/lib/chef/http/ssl_policies.rb +6 -0
  29. data/lib/chef/knife/bootstrap/train_connector.rb +1 -1
  30. data/lib/chef/knife/core/ui.rb +4 -1
  31. data/lib/chef/knife/ssh.rb +1 -1
  32. data/lib/chef/mixin/powershell_exec.rb +3 -1
  33. data/lib/chef/platform/query_helpers.rb +4 -4
  34. data/lib/chef/powershell.rb +2 -0
  35. data/lib/chef/provider/dsc_resource.rb +12 -24
  36. data/lib/chef/provider/dsc_script.rb +16 -20
  37. data/lib/chef/provider/git.rb +5 -5
  38. data/lib/chef/resource/chef_client_config.rb +1 -1
  39. data/lib/chef/resource/dsc_script.rb +8 -1
  40. data/lib/chef/resource/hostname.rb +3 -3
  41. data/lib/chef/resource/template.rb +2 -2
  42. data/lib/chef/resource/windows_certificate.rb +7 -1
  43. data/lib/chef/resource_collection/resource_set.rb +1 -1
  44. data/lib/chef/util/dsc/configuration_generator.rb +52 -11
  45. data/lib/chef/util/dsc/lcm_output_parser.rb +3 -4
  46. data/lib/chef/util/dsc/local_configuration_manager.rb +17 -14
  47. data/lib/chef/util/dsc/resource_store.rb +5 -11
  48. data/lib/chef/version.rb +1 -1
  49. data/lib/chef/win32/api/file.rb +4 -0
  50. data/spec/functional/resource/dsc_script_spec.rb +3 -6
  51. data/spec/integration/client/client_spec.rb +2 -1
  52. data/spec/integration/compliance/compliance_spec.rb +81 -0
  53. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -0
  54. data/spec/spec_helper.rb +1 -1
  55. data/spec/unit/client_spec.rb +1 -0
  56. data/spec/unit/compliance/fetcher/automate_spec.rb +134 -0
  57. data/spec/unit/compliance/fetcher/chef_server_spec.rb +93 -0
  58. data/spec/unit/compliance/reporter/automate_spec.rb +427 -0
  59. data/spec/unit/compliance/reporter/chef_server_automate_spec.rb +177 -0
  60. data/spec/unit/compliance/reporter/compliance_enforcer_spec.rb +48 -0
  61. data/spec/unit/compliance/runner_spec.rb +113 -0
  62. data/spec/unit/http/ssl_policies_spec.rb +11 -0
  63. data/spec/unit/knife/core/node_editor_spec.rb +1 -1
  64. data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
  65. data/spec/unit/platform/query_helpers_spec.rb +11 -12
  66. data/spec/unit/provider/dsc_resource_spec.rb +10 -27
  67. data/spec/unit/provider/dsc_script_spec.rb +1 -1
  68. data/spec/unit/provider/mount/windows_spec.rb +1 -0
  69. data/spec/unit/provider/systemd_unit_spec.rb +1 -1
  70. data/spec/unit/resource/windows_certificate_spec.rb +12 -0
  71. data/spec/unit/util/dsc/configuration_generator_spec.rb +79 -0
  72. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +27 -35
  73. metadata +37 -12
  74. data/lib/chef/util/powershell/cmdlet.rb +0 -169
  75. data/lib/chef/util/powershell/cmdlet_result.rb +0 -61
  76. data/spec/functional/util/powershell/cmdlet_spec.rb +0 -111
  77. data/spec/unit/util/powershell/cmdlet_spec.rb +0 -106
@@ -0,0 +1,92 @@
1
+ require_relative "automate"
2
+
3
+ class Chef
4
+ module Compliance
5
+ module Reporter
6
+ #
7
+ # Used to send inspec reports to Chef Automate server via Chef Server
8
+ #
9
+ class ChefServerAutomate < Chef::Compliance::Reporter::Automate
10
+ def initialize(opts)
11
+ @entity_uuid = opts[:entity_uuid]
12
+ @run_id = opts[:run_id]
13
+ @node_name = opts[:node_info][:node]
14
+ @insecure = opts[:insecure]
15
+ @environment = opts[:node_info][:environment]
16
+ @roles = opts[:node_info][:roles]
17
+ @recipes = opts[:node_info][:recipes]
18
+ @url = opts[:url]
19
+ @chef_tags = opts[:node_info][:chef_tags]
20
+ @policy_group = opts[:node_info][:policy_group]
21
+ @policy_name = opts[:node_info][:policy_name]
22
+ @source_fqdn = opts[:node_info][:source_fqdn]
23
+ @organization_name = opts[:node_info][:organization_name]
24
+ @ipaddress = opts[:node_info][:ipaddress]
25
+ @fqdn = opts[:node_info][:fqdn]
26
+ @control_results_limit = opts[:control_results_limit]
27
+ @timestamp = opts.fetch(:timestamp) { Time.now }
28
+ end
29
+
30
+ def send_report(report)
31
+ unless @entity_uuid && @run_id
32
+ Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
33
+ return false
34
+ end
35
+
36
+ automate_report = truncate_controls_results(enriched_report(report), @control_results_limit)
37
+
38
+ report_size = Chef::JSONCompat.to_json(automate_report, validate_utf8: false).bytesize
39
+ # this is set to slightly less than the oc_erchef limit
40
+ if report_size > 900 * 1024
41
+ Chef::Log.warn "Generated report size is #{(report_size / (1024 * 1024.0)).round(2)} MB. #{ChefUtils::Dist::Server::PRODUCT} < 13.0 defaults to a limit of ~1MB, 13.0+ defaults to a limit of ~2MB."
42
+ end
43
+
44
+ Chef::Log.info "Report to #{ChefUtils::Dist::Automate::PRODUCT} via #{ChefUtils::Dist::Server::PRODUCT}: #{@url}"
45
+ with_http_rescue do
46
+ http_client.post(@url, automate_report)
47
+ return true
48
+ end
49
+ false
50
+ end
51
+
52
+ def http_client
53
+ config = if @insecure
54
+ Chef::Config.merge(ssl_verify_mode: :verify_none)
55
+ else
56
+ Chef::Config
57
+ end
58
+
59
+ Chef::ServerAPI.new(@url, config)
60
+ end
61
+
62
+ def with_http_rescue
63
+ response = yield
64
+ if response.respond_to?(:code)
65
+ # handle non 200 error codes, they are not raised as Net::HTTPClientException
66
+ handle_http_error_code(response.code) if response.code.to_i >= 300
67
+ end
68
+ response
69
+ rescue Net::HTTPClientException => e
70
+ Chef::Log.error e
71
+ handle_http_error_code(e.response.code)
72
+ end
73
+
74
+ def handle_http_error_code(code)
75
+ case code
76
+ when /401|403/
77
+ Chef::Log.error "Auth issue: see audit cookbook TROUBLESHOOTING.md"
78
+ when /404/
79
+ Chef::Log.error "Object does not exist on remote server."
80
+ when /413/
81
+ Chef::Log.error "You most likely hit the erchef request size in #{ChefUtils::Dist::Server::PRODUCT} that defaults to ~2MB. To increase this limit see audit cookbook TROUBLESHOOTING.md OR https://docs.chef.io/config_rb_server.html"
82
+ when /429/
83
+ Chef::Log.error "This error typically means the data sent was larger than #{ChefUtils::Dist::Automate::PRODUCT}'s limit (4 MB). Run InSpec locally to identify any controls producing large diffs."
84
+ end
85
+ msg = "Received HTTP error #{code}"
86
+ Chef::Log.error msg
87
+ raise msg
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,20 @@
1
+ class Chef
2
+ module Compliance
3
+ module Reporter
4
+ class AuditEnforcer
5
+ class ControlFailure < StandardError; end
6
+
7
+ def send_report(report)
8
+ report.fetch(:profiles, []).each do |profile|
9
+ profile.fetch(:controls, []).each do |control|
10
+ control.fetch(:results, []).each do |result|
11
+ raise ControlFailure, "Audit #{control[:id]} has failed. Aborting #{ChefUtils::Dist::Infra::CLIENT} run." if result[:status] == "failed"
12
+ end
13
+ end
14
+ end
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "../../json_compat"
2
+
3
+ class Chef
4
+ module Compliance
5
+ module Reporter
6
+ class JsonFile
7
+ def initialize(opts)
8
+ @path = opts.fetch(:file)
9
+ end
10
+
11
+ def send_report(report)
12
+ FileUtils.mkdir_p(File.dirname(@path), mode: 0700)
13
+
14
+ File.write(@path, Chef::JSONCompat.to_json(report))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,250 @@
1
+ autoload :Inspec, "inspec"
2
+
3
+ require_relative "default_attributes"
4
+ require_relative "reporter/automate"
5
+ require_relative "reporter/chef_server_automate"
6
+ require_relative "reporter/compliance_enforcer"
7
+ require_relative "reporter/json_file"
8
+
9
+ class Chef
10
+ module Compliance
11
+ class Runner < EventDispatch::Base
12
+ extend Forwardable
13
+
14
+ attr_accessor :run_id, :recipes
15
+ attr_reader :node
16
+ def_delegators :node, :logger
17
+
18
+ def enabled?
19
+ audit_cookbook_present = recipes.include?("audit::default")
20
+
21
+ logger.info("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
22
+ logger.info("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
23
+
24
+ inspec_profiles.any? && !audit_cookbook_present
25
+ end
26
+
27
+ def node=(node)
28
+ @node = node
29
+ node.default["audit"] = Chef::Compliance::DEFAULT_ATTRIBUTES.merge(node.default["audit"])
30
+ end
31
+
32
+ def node_load_completed(node, _expanded_run_list, _config)
33
+ self.node = node
34
+ end
35
+
36
+ def run_started(run_status)
37
+ self.run_id = run_status.run_id
38
+ end
39
+
40
+ def run_list_expanded(run_list_expansion)
41
+ self.recipes = run_list_expansion.recipes
42
+ end
43
+
44
+ def run_completed(_node, _run_status)
45
+ return unless enabled?
46
+
47
+ logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
48
+
49
+ report
50
+ end
51
+
52
+ def run_failed(_exception, _run_status)
53
+ return unless enabled?
54
+
55
+ logger.info("#{self.class}##{__method__}: enabling Compliance Phase")
56
+
57
+ report
58
+ end
59
+
60
+ ### Below code adapted from audit cookbook's files/default/handler/audit_report.rb
61
+
62
+ DEPRECATED_CONFIG_VALUES = %w{
63
+ attributes_save
64
+ chef_node_attribute_enabled
65
+ fail_if_not_present
66
+ inspec_gem_source
67
+ inspec_version
68
+ interval
69
+ owner
70
+ raise_if_unreachable
71
+ }.freeze
72
+
73
+ def warn_for_deprecated_config_values!
74
+ deprecated_config_values = (node["audit"].keys & DEPRECATED_CONFIG_VALUES)
75
+
76
+ if deprecated_config_values.any?
77
+ values = deprecated_config_values.sort.map { |v| "'#{v}'" }.join(", ")
78
+ logger.warn "audit cookbook config values #{values} are not supported in #{ChefUtils::Dist::Infra::PRODUCT}'s Compliance Phase."
79
+ end
80
+ end
81
+
82
+ def report(report = generate_report)
83
+ warn_for_deprecated_config_values!
84
+
85
+ if report.empty?
86
+ logger.error "Compliance report was not generated properly, skipped reporting"
87
+ return
88
+ end
89
+
90
+ Array(node["audit"]["reporter"]).each do |reporter|
91
+ send_report(reporter, report)
92
+ end
93
+ end
94
+
95
+ def inspec_opts
96
+ {
97
+ backend_cache: node["audit"]["inspec_backend_cache"],
98
+ inputs: node["audit"]["attributes"],
99
+ logger: logger,
100
+ output: node["audit"]["quiet"] ? ::File::NULL : STDOUT,
101
+ report: true,
102
+ reporter: ["json-automate"],
103
+ reporter_backtrace_inclusion: node["audit"]["result_include_backtrace"],
104
+ reporter_message_truncation: node["audit"]["result_message_limit"],
105
+ waiver_file: Array(node["audit"]["waiver_file"]),
106
+ }
107
+ end
108
+
109
+ def inspec_profiles
110
+ profiles = node["audit"]["profiles"]
111
+
112
+ # TODO: Custom exception class here?
113
+ unless profiles.respond_to?(:map) && profiles.all? { |_, p| p.respond_to?(:transform_keys) && p.respond_to?(:update) }
114
+ raise "#{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
115
+ end
116
+
117
+ profiles.map do |name, profile|
118
+ profile.transform_keys(&:to_sym).update(name: name)
119
+ end
120
+ end
121
+
122
+ def load_fetchers!
123
+ case node["audit"]["fetcher"]
124
+ when "chef-automate"
125
+ require_relative "fetcher/automate"
126
+ when "chef-server"
127
+ require_relative "fetcher/chef_server"
128
+ when nil
129
+ # intentionally blank
130
+ else
131
+ raise "Invalid value specified for Compliance Phase's fetcher: '#{node["audit"]["fetcher"]}'. Valid values are 'chef-automate', 'chef-server', or nil."
132
+ end
133
+ end
134
+
135
+ def generate_report(opts: inspec_opts, profiles: inspec_profiles)
136
+ load_fetchers!
137
+
138
+ logger.debug "Options are set to: #{opts}"
139
+ runner = ::Inspec::Runner.new(opts)
140
+
141
+ if profiles.empty?
142
+ failed_report("No #{Inspec::Dist::PRODUCT_NAME} profiles are defined.")
143
+ return
144
+ end
145
+
146
+ profiles.each { |target| runner.add_target(target) }
147
+
148
+ logger.info "Running profiles from: #{profiles.inspect}"
149
+ runner.run
150
+ runner.report.tap do |r|
151
+ logger.debug "Compliance Report #{r}"
152
+ end
153
+ rescue Inspec::FetcherFailure => e
154
+ failed_report("Cannot fetch all profiles: #{profiles}. Please make sure you're authenticated and the server is reachable. #{e.message}")
155
+ rescue => e
156
+ failed_report(e.message)
157
+ end
158
+
159
+ # In case InSpec raises a runtime exception without providing a valid report,
160
+ # we make one up and add two new fields to it: `status` and `status_message`
161
+ def failed_report(err)
162
+ logger.error "#{Inspec::Dist::PRODUCT_NAME} has raised a runtime exception. Generating a minimal failed report."
163
+ logger.error err
164
+ {
165
+ "platform": {
166
+ "name": "unknown",
167
+ "release": "unknown",
168
+ },
169
+ "profiles": [],
170
+ "statistics": {
171
+ "duration": 0.0000001,
172
+ },
173
+ "version": Inspec::VERSION,
174
+ "status": "failed",
175
+ "status_message": err,
176
+ }
177
+ end
178
+
179
+ # extracts relevant node data
180
+ def node_info
181
+ runlist_roles = node.run_list.select { |item| item.type == :role }.map(&:name)
182
+ runlist_recipes = node.run_list.select { |item| item.type == :recipe }.map(&:name)
183
+ {
184
+ node: node.name,
185
+ os: {
186
+ release: node["platform_version"],
187
+ family: node["platform"],
188
+ },
189
+ environment: node.environment,
190
+ roles: runlist_roles,
191
+ recipes: runlist_recipes,
192
+ policy_name: node.policy_name || "",
193
+ policy_group: node.policy_group || "",
194
+ chef_tags: node.tags,
195
+ organization_name: chef_server_uri.path.split("/").last || "",
196
+ source_fqdn: chef_server_uri.host || "",
197
+ ipaddress: node["ipaddress"],
198
+ fqdn: node["fqdn"],
199
+ }
200
+ end
201
+
202
+ def send_report(reporter, report)
203
+ logger.info "Reporting to #{reporter}"
204
+
205
+ insecure = node["audit"]["insecure"]
206
+ run_time_limit = node["audit"]["run_time_limit"]
207
+ control_results_limit = node["audit"]["control_results_limit"]
208
+
209
+ case reporter
210
+ when "chef-automate"
211
+ opts = {
212
+ entity_uuid: node["chef_guid"],
213
+ run_id: run_id,
214
+ node_info: node_info,
215
+ insecure: insecure,
216
+ run_time_limit: run_time_limit,
217
+ control_results_limit: control_results_limit,
218
+ }
219
+ Chef::Compliance::Reporter::Automate.new(opts).send_report(report)
220
+ when "chef-server-automate"
221
+ chef_url = node["audit"]["server"] || base_chef_server_url
222
+ chef_org = Chef::Config[:chef_server_url].split("/").last
223
+ if chef_url
224
+ url = construct_url(chef_url, File.join("organizations", chef_org, "data-collector"))
225
+ opts = {
226
+ entity_uuid: node["chef_guid"],
227
+ run_id: run_id,
228
+ node_info: node_info,
229
+ insecure: insecure,
230
+ url: url,
231
+ run_time_limit: run_time_limit,
232
+ control_results_limit: control_results_limit,
233
+ }
234
+ Chef::Compliance::Reporter::ChefServer.new(opts).send_report(report)
235
+ else
236
+ logger.warn "Unable to determine #{ChefUtils::Dist::Server::PRODUCT} url required by #{Inspec::Dist::PRODUCT_NAME} report collector '#{reporter}'. Skipping..."
237
+ end
238
+ when "json-file"
239
+ path = node["audit"]["json_file"]["location"]
240
+ logger.info "Writing compliance report to #{path}"
241
+ Chef::Compliance::Reporter::JsonFile.new(file: path).send_report(report)
242
+ when "audit-enforcer"
243
+ Chef::Compliance::Reporter::ComplianceEnforcer.new.send_report(report)
244
+ else
245
+ logger.warn "#{reporter} is not a supported #{Inspec::Dist::PRODUCT_NAME} report collector"
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -317,6 +317,7 @@ class Chef
317
317
  end
318
318
 
319
319
  end
320
+
320
321
  class CookbookManifestVersions
321
322
 
322
323
  extend Chef::Mixin::VersionedAPIFactory
@@ -30,7 +30,7 @@ class Chef::EncryptedDataBagItem
30
30
  unless format_version.is_a?(Integer) && format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
31
31
  raise UnacceptableEncryptedDataBagItemFormat,
32
32
  "The encrypted data bag item has format version `#{format_version}', " +
33
- "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
33
+ "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
34
34
  end
35
35
  end
36
36
 
@@ -84,11 +84,13 @@ class Chef
84
84
  class InvalidPrivateKey < ArgumentError; end
85
85
  class MissingKeyAttribute < ArgumentError; end
86
86
  class KeyCommandInputError < ArgumentError; end
87
+
87
88
  class BootstrapCommandInputError < ArgumentError
88
89
  def initialize
89
90
  super "You cannot pass both --json-attributes and --json-attribute-file. Please pass one or none."
90
91
  end
91
92
  end
93
+
92
94
  class InvalidKeyArgument < ArgumentError; end
93
95
  class InvalidKeyAttribute < ArgumentError; end
94
96
  class InvalidUserAttribute < ArgumentError; end
@@ -195,6 +197,7 @@ class Chef
195
197
  class IllegalVersionConstraint < NotImplementedError; end # rubocop:disable Lint/InheritException
196
198
 
197
199
  class MetadataNotValid < StandardError; end
200
+
198
201
  class MetadataNotFound < StandardError
199
202
  attr_reader :install_path
200
203
  attr_reader :cookbook_name
@@ -283,6 +286,7 @@ class Chef
283
286
  end
284
287
 
285
288
  end
289
+
286
290
  # Exception class for collecting multiple failures. Used when running
287
291
  # delayed notifications so that chef can process each delayed
288
292
  # notification even if chef client or other notifications fail.
@@ -70,6 +70,12 @@ class Chef
70
70
  end
71
71
 
72
72
  http_client.ca_file = config[:ssl_ca_file]
73
+ elsif ENV["SSL_CERT_FILE"]
74
+ unless ::File.exist?(ENV["SSL_CERT_FILE"])
75
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{ENV["SSL_CERT_FILE"]} does not exist"
76
+ end
77
+
78
+ http_client.ca_file = ENV["SSL_CERT_FILE"]
73
79
  end
74
80
  end
75
81
 
@@ -285,7 +285,7 @@ class Chef
285
285
  # Train.unpack_target_from_uri only works for complete URIs in
286
286
  # form of proto://[user[:pass]@]host[:port]/
287
287
  # So we'll add the protocol prefix if it's not supplied.
288
- uri_to_check = if URI.regexp.match(uri)
288
+ uri_to_check = if URI::DEFAULT_PARSER.make_regexp.match(uri)
289
289
  uri
290
290
  else
291
291
  "#{default_protocol}://#{uri}"