diff options
Diffstat (limited to 'lib/chef')
-rw-r--r-- | lib/chef/compliance/default_attributes.rb | 3 | ||||
-rw-r--r-- | lib/chef/compliance/fetcher/automate.rb | 7 | ||||
-rw-r--r-- | lib/chef/compliance/reporter/automate.rb | 24 | ||||
-rw-r--r-- | lib/chef/compliance/reporter/chef_server_automate.rb | 17 | ||||
-rw-r--r-- | lib/chef/compliance/reporter/cli.rb | 4 | ||||
-rw-r--r-- | lib/chef/compliance/reporter/compliance_enforcer.rb | 4 | ||||
-rw-r--r-- | lib/chef/compliance/reporter/json_file.rb | 6 | ||||
-rw-r--r-- | lib/chef/compliance/runner.rb | 72 |
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 |