summaryrefslogtreecommitdiff
path: root/lib/chef
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef')
-rw-r--r--lib/chef/compliance/default_attributes.rb3
-rw-r--r--lib/chef/compliance/fetcher/automate.rb7
-rw-r--r--lib/chef/compliance/reporter/automate.rb24
-rw-r--r--lib/chef/compliance/reporter/chef_server_automate.rb17
-rw-r--r--lib/chef/compliance/reporter/cli.rb4
-rw-r--r--lib/chef/compliance/reporter/compliance_enforcer.rb4
-rw-r--r--lib/chef/compliance/reporter/json_file.rb6
-rw-r--r--lib/chef/compliance/runner.rb72
8 files changed, 93 insertions, 44 deletions
diff --git a/lib/chef/compliance/default_attributes.rb b/lib/chef/compliance/default_attributes.rb
index a1ffff440f..f214135d59 100644
--- a/lib/chef/compliance/default_attributes.rb
+++ b/lib/chef/compliance/default_attributes.rb
@@ -38,11 +38,12 @@ class Chef
# Allow for connections to HTTPS endpoints using self-signed ssl certificates.
"insecure" => nil,
- # Controls verbosity of Chef InSpec runner.
+ # Controls verbosity of Chef InSpec runner. See less output when true.
"quiet" => true,
# Chef Inspec Compliance profiles to be used for scan of node.
# See README.md for details
+ # MPTD - ^there is no README.md anymore^
"profiles" => {},
# Extra inputs passed to Chef InSpec to allow finer-grained control over behavior.
diff --git a/lib/chef/compliance/fetcher/automate.rb b/lib/chef/compliance/fetcher/automate.rb
index 64aff6833a..47dc7f2e6a 100644
--- a/lib/chef/compliance/fetcher/automate.rb
+++ b/lib/chef/compliance/fetcher/automate.rb
@@ -46,13 +46,6 @@ class Chef
config["token"] = Chef::Config[:data_collector][:token]
- if config["token"].nil?
- raise Inspec::FetcherFailure,
- "No data-collector token set, which is required by the chef-automate fetcher. " \
- "Set the `data_collector.token` configuration parameter in your client.rb " \
- 'or use the "chef-server-automate" reporter which does not require any ' \
- "data-collector settings and uses #{ChefUtils::Dist::Server::PRODUCT} to fetch profiles."
- end
end
new(profile_fetch_url, config)
diff --git a/lib/chef/compliance/reporter/automate.rb b/lib/chef/compliance/reporter/automate.rb
index cae0256085..23514c857d 100644
--- a/lib/chef/compliance/reporter/automate.rb
+++ b/lib/chef/compliance/reporter/automate.rb
@@ -28,18 +28,28 @@ class Chef
@token = Chef::Config[:data_collector][:token]
end
- # Method used in order to send the inspec report to the data_collector server
- def send_report(report)
- unless @entity_uuid && @run_id
- Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
- return false
+ def validate_config!
+ unless @entity_uuid
+ # TODO - this is a weird leakage of naming from the parent class
+ # but entity_uuid is never an attribute that the user can see;
+ # it is sourced from chef_guid, which we don't technically know about in this class -
+ # but telling the operator about a missing chef_guid is more helpful than telling
+ # them about a missing field they've never heard of. Better would be a dock link
+ # that described how to fix this situation.
+ raise "CMPL004: automate_reporter: chef_guid is not available and must be provided. Aborting because we cannot report the scan."
+ end
+
+ unless @run_id
+ raise "CMPL005: automate_reporter: run_id is not available, aborting because we cannot report the scan."
end
unless @url && @token
- Chef::Log.warn "data_collector.token and data_collector.server_url must be defined in client.rb! Further information: https://docs.chef.io/chef_compliance_phase/#direct-reporting-to-chef-automate"
- return false
+ raise "CMPL006: data_collector.token and data_collector.server_url must be configured in client.rb! Further information: https://docs.chef.io/chef_compliance_phase/#direct-reporting-to-chef-automate"
end
+ end
+ # Method used in order to send the inspec report to the data_collector server
+ def send_report(report)
headers = {
"Content-Type" => "application/json",
"x-data-collector-auth" => "version=1.0",
diff --git a/lib/chef/compliance/reporter/chef_server_automate.rb b/lib/chef/compliance/reporter/chef_server_automate.rb
index 46ca7dc7ba..569fb22426 100644
--- a/lib/chef/compliance/reporter/chef_server_automate.rb
+++ b/lib/chef/compliance/reporter/chef_server_automate.rb
@@ -30,11 +30,6 @@ class Chef
end
def send_report(report)
- unless @entity_uuid && @run_id
- Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
- return false
- end
-
automate_report = truncate_controls_results(enriched_report(report), @control_results_limit)
report_size = Chef::JSONCompat.to_json(automate_report, validate_utf8: false).bytesize
@@ -51,6 +46,16 @@ class Chef
false
end
+ def validate_config!
+ unless @entity_uuid
+ raise "CMPL007: chef_server_automate reporter: chef_guid is not available and must be provided. Aborting because we cannot report the scan"
+ end
+
+ unless @run_id
+ raise "CMPL008: chef_server_automate reporter: run_id is not available, aborting because we cannot report the scan."
+ end
+ end
+
def http_client
config = if @insecure
Chef::Config.merge(ssl_verify_mode: :verify_none)
@@ -80,7 +85,7 @@ class Chef
when /404/
Chef::Log.error "Object does not exist on remote server."
when /413/
- 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 the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting) or the Chef Infra Server configuration documentation (https://docs.chef.io/server/config_rb_server/)"
+ Chef::Log.error "You most likely hit the request size limit in #{ChefUtils::Dist::Server::PRODUCT} that defaults to ~2MB. To increase this limit see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting) or the Chef Infra Server configuration documentation (https://docs.chef.io/server/config_rb_server/)"
when /429/
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."
end
diff --git a/lib/chef/compliance/reporter/cli.rb b/lib/chef/compliance/reporter/cli.rb
index 4905c1f653..7061e5751c 100644
--- a/lib/chef/compliance/reporter/cli.rb
+++ b/lib/chef/compliance/reporter/cli.rb
@@ -22,6 +22,10 @@ class Chef
puts output.join("\n")
end
+ def validate_config!
+ true
+ end
+
private
# pastel.decorate is a lightweight replacement for highline.color
diff --git a/lib/chef/compliance/reporter/compliance_enforcer.rb b/lib/chef/compliance/reporter/compliance_enforcer.rb
index 1c63e43b28..47b3a4d2df 100644
--- a/lib/chef/compliance/reporter/compliance_enforcer.rb
+++ b/lib/chef/compliance/reporter/compliance_enforcer.rb
@@ -14,6 +14,10 @@ class Chef
end
true
end
+
+ def validate_config!
+ true
+ end
end
end
end
diff --git a/lib/chef/compliance/reporter/json_file.rb b/lib/chef/compliance/reporter/json_file.rb
index 471d9f64b1..4d074242ca 100644
--- a/lib/chef/compliance/reporter/json_file.rb
+++ b/lib/chef/compliance/reporter/json_file.rb
@@ -13,6 +13,12 @@ class Chef
File.write(@path, Chef::JSONCompat.to_json(report))
end
+
+ def validate_config!
+ if @path.nil? || @path.class != String || @path.empty?
+ raise "CMPL007: json_file reporter: node['audit']['json_file']['location'] must contain a file path"
+ end
+ end
end
end
end
diff --git a/lib/chef/compliance/runner.rb b/lib/chef/compliance/runner.rb
index c083109875..7aebeeab36 100644
--- a/lib/chef/compliance/runner.rb
+++ b/lib/chef/compliance/runner.rb
@@ -1,17 +1,15 @@
autoload :Inspec, "inspec"
require_relative "default_attributes"
-require_relative "reporter/automate"
-require_relative "reporter/chef_server_automate"
-require_relative "reporter/compliance_enforcer"
-require_relative "reporter/cli"
-require_relative "reporter/json_file"
class Chef
module Compliance
class Runner < EventDispatch::Base
extend Forwardable
+ SUPPORTED_REPORTERS = %w{chef-automate chef-server-automate json-file audit-enforcer cli}.freeze
+ SUPPORTED_FETCHERS = %w{chef-automate chef-server}.freeze
+
attr_accessor :run_id
attr_reader :node
def_delegators :node, :logger
@@ -47,6 +45,15 @@ class Chef
self.run_id = run_status.run_id
end
+ def converge_start(run_context)
+ # With all attributes - including cookook - loaded, we now have enough data to validate
+ # configuration. Because the converge is best coupled with the associated compliance run, these validations
+ # will raise (and abort the converge) if the compliance phase configuration is incorrect/will
+ # prevent compliance phase from completing and submitting its report to all configured reporters.
+ # can abort the converge if the compliance phase configuration (node attributes and client config)
+ load_and_validate!
+ end
+
def run_completed(_node, _run_status)
return unless enabled?
@@ -85,6 +92,8 @@ class Chef
end
def report(report = generate_report)
+ # This is invoked at report-time instead of with the normal validations at node loaded,
+ # because we want to ensure that it is visible in the output - and not lost in back-scroll.
warn_for_deprecated_config_values!
if report.empty?
@@ -92,8 +101,9 @@ class Chef
return
end
- Array(node["audit"]["reporter"]).each do |reporter|
- send_report(reporter, report)
+ Array(node["audit"]["reporter"]).each do |reporter_type|
+ logger.info "Reporting to #{reporter_type}"
+ @reporters[reporter_type].send_report(report)
end
end
@@ -119,10 +129,8 @@ class Chef
def inspec_profiles
profiles = node["audit"]["profiles"]
-
- # TODO: Custom exception class here?
unless profiles.respond_to?(:map) && profiles.all? { |_, p| p.respond_to?(:transform_keys) && p.respond_to?(:update) }
- raise "#{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
+ raise "CMPL010: #{Inspec::Dist::PRODUCT_NAME} profiles specified in an unrecognized format, expected a hash of hashes."
end
profiles.map do |name, profile|
@@ -138,8 +146,6 @@ class Chef
require_relative "fetcher/chef_server"
when nil
# intentionally blank
- else
- raise "Invalid value specified for Compliance Phase's fetcher: '#{node["audit"]["fetcher"]}'. Valid values are 'chef-automate', 'chef-server', or nil."
end
end
@@ -212,17 +218,10 @@ class Chef
}
end
- def send_report(reporter_type, report)
- logger.info "Reporting to #{reporter_type}"
-
- reporter = reporter(reporter_type)
-
- reporter.send_report(report) if reporter
- end
-
def reporter(reporter_type)
- case reporter_type
+ case reporter_type.downcase
when "chef-automate"
+ require_relative "reporter/automate"
opts = {
control_results_limit: node["audit"]["control_results_limit"],
entity_uuid: node["chef_guid"],
@@ -233,6 +232,7 @@ class Chef
}
Chef::Compliance::Reporter::Automate.new(opts)
when "chef-server-automate"
+ require_relative "reporter/chef_server_automate"
opts = {
control_results_limit: node["audit"]["control_results_limit"],
entity_uuid: node["chef_guid"],
@@ -244,15 +244,16 @@ class Chef
}
Chef::Compliance::Reporter::ChefServerAutomate.new(opts)
when "json-file"
+ require_relative "reporter/json_file"
path = node["audit"]["json_file"]["location"]
logger.info "Writing compliance report to #{path}"
Chef::Compliance::Reporter::JsonFile.new(file: path)
when "audit-enforcer"
+ require_relative "reporter/compliance_enforcer"
Chef::Compliance::Reporter::ComplianceEnforcer.new
when "cli"
+ require_relative "reporter/cli"
Chef::Compliance::Reporter::Cli.new
- else
- raise "'#{reporter_type}' is not a supported reporter for Compliance Phase."
end
end
@@ -269,6 +270,31 @@ class Chef
url.path = File.join(url.path, "organizations/#{org}/data-collector")
url
end
+
+ # Load the resources required for this runner, and validate configuration
+ # is correct to proceed. Requires node state to be loaded.
+ # Will raise exception if fetcher is not valid, if a reporter is not valid,
+ # or the configuration required by a reporter is not provided.
+ def load_and_validate!
+ return unless enabled?
+
+ @reporters = {}
+ Array(node["audit"]["reporter"]).each do |reporter_type|
+ type = reporter_type.downcase
+ unless SUPPORTED_REPORTERS.include? type
+ raise "CMPL003: '#{reporter_type}' found in node['audit']['reporter'] is not a supported reporter for Compliance Phase. Supported reporters are: #{SUPPORTED_REPORTERS.join(",")}. For more information, see the documentation at https://docs.chef.io/chef_compliance_phase/chef_compliance_runners/#reporters"
+ end
+
+ @reporters[type] = reporter(type)
+ @reporters[type].validate_config!
+ end
+
+ unless (fetcher = node["audit"]["fetcher"]).nil?
+ unless SUPPORTED_FETCHERS.include? fetcher.downcase
+ raise "CMPL002: Unrecognized Compliance Phase fetcher (node['audit']['fetcher'] is #{fetcher}. Supported fetchers are: or #{SUPPORTED_FETCHERS.join(",")}, or nil. For more information, see the documentation at https://docs.chef.io/chef_compliance_phase/chef_compliance_runners/#fetchers"
+ end
+ end
+ end
end
end
end