chef 16.7.61 → 16.9.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -5
  3. data/README.md +2 -2
  4. data/chef.gemspec +12 -2
  5. data/lib/chef/application/base.rb +1 -1
  6. data/lib/chef/client.rb +3 -0
  7. data/lib/chef/compliance/default_attributes.rb +93 -0
  8. data/lib/chef/compliance/fetcher/automate.rb +69 -0
  9. data/lib/chef/compliance/fetcher/chef_server.rb +134 -0
  10. data/lib/chef/compliance/reporter/automate.rb +201 -0
  11. data/lib/chef/compliance/reporter/chef_server_automate.rb +94 -0
  12. data/lib/chef/compliance/reporter/compliance_enforcer.rb +20 -0
  13. data/lib/chef/compliance/reporter/json_file.rb +19 -0
  14. data/lib/chef/compliance/runner.rb +262 -0
  15. data/lib/chef/cookbook_manifest.rb +1 -0
  16. data/lib/chef/encrypted_data_bag_item/assertions.rb +1 -1
  17. data/lib/chef/exceptions.rb +4 -0
  18. data/lib/chef/http/ssl_policies.rb +33 -14
  19. data/lib/chef/knife/bootstrap/train_connector.rb +1 -1
  20. data/lib/chef/knife/core/formatting_options.rb +49 -0
  21. data/lib/chef/knife/core/node_presenter.rb +0 -25
  22. data/lib/chef/knife/core/status_presenter.rb +1 -26
  23. data/lib/chef/knife/core/ui.rb +4 -1
  24. data/lib/chef/knife/core/windows_bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/node_show.rb +2 -1
  26. data/lib/chef/knife/search.rb +2 -1
  27. data/lib/chef/knife/ssh.rb +3 -1
  28. data/lib/chef/knife/status.rb +8 -11
  29. data/lib/chef/mixin/powershell_exec.rb +3 -1
  30. data/lib/chef/platform/query_helpers.rb +4 -4
  31. data/lib/chef/policy_builder/policyfile.rb +1 -1
  32. data/lib/chef/powershell.rb +2 -0
  33. data/lib/chef/provider/dsc_resource.rb +12 -24
  34. data/lib/chef/provider/dsc_script.rb +16 -20
  35. data/lib/chef/provider/git.rb +5 -5
  36. data/lib/chef/provider/package.rb +53 -19
  37. data/lib/chef/provider/package/dnf.rb +39 -12
  38. data/lib/chef/provider/package/dnf/dnf_helper.py +18 -5
  39. data/lib/chef/provider/package/dnf/python_helper.rb +6 -6
  40. data/lib/chef/provider/package/freebsd/pkgng.rb +3 -1
  41. data/lib/chef/provider/yum_repository.rb +2 -2
  42. data/lib/chef/resource/chef_client_config.rb +1 -1
  43. data/lib/chef/resource/chef_gem.rb +2 -2
  44. data/lib/chef/resource/cron/cron_d.rb +1 -0
  45. data/lib/chef/resource/dsc_script.rb +8 -1
  46. data/lib/chef/resource/file.rb +1 -1
  47. data/lib/chef/resource/gem_package.rb +2 -2
  48. data/lib/chef/resource/homebrew_cask.rb +3 -3
  49. data/lib/chef/resource/hostname.rb +3 -3
  50. data/lib/chef/resource/http_request.rb +1 -1
  51. data/lib/chef/resource/locale.rb +1 -1
  52. data/lib/chef/resource/mdadm.rb +2 -2
  53. data/lib/chef/resource/osx_profile.rb +7 -7
  54. data/lib/chef/resource/remote_directory.rb +1 -1
  55. data/lib/chef/resource/ruby.rb +1 -5
  56. data/lib/chef/resource/ruby_block.rb +1 -1
  57. data/lib/chef/resource/template.rb +2 -2
  58. data/lib/chef/resource/user/windows_user.rb +5 -0
  59. data/lib/chef/resource/windows_certificate.rb +9 -13
  60. data/lib/chef/resource/yum_repository.rb +5 -0
  61. data/lib/chef/resource_collection/resource_set.rb +1 -1
  62. data/lib/chef/util/dsc/configuration_generator.rb +52 -11
  63. data/lib/chef/util/dsc/lcm_output_parser.rb +3 -4
  64. data/lib/chef/util/dsc/local_configuration_manager.rb +17 -14
  65. data/lib/chef/util/dsc/resource_store.rb +5 -11
  66. data/lib/chef/version.rb +1 -1
  67. data/lib/chef/win32/api/file.rb +4 -0
  68. data/spec/data/rubygems.org/latest_specs.4.8.gz +0 -0
  69. data/spec/data/rubygems.org/nonexistent_gem +0 -0
  70. data/spec/data/rubygems.org/sexp_processor +0 -0
  71. data/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz +0 -0
  72. data/spec/data/ssl/binary/chef-rspec-der.cert +0 -0
  73. data/spec/data/ssl/binary/chef-rspec-der.key +0 -0
  74. data/spec/functional/resource/dnf_package_spec.rb +319 -16
  75. data/spec/functional/resource/dsc_script_spec.rb +3 -6
  76. data/spec/functional/resource/windows_certificate_spec.rb +204 -384
  77. data/spec/integration/client/client_spec.rb +2 -1
  78. data/spec/integration/compliance/compliance_spec.rb +81 -0
  79. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -0
  80. data/spec/spec_helper.rb +1 -1
  81. data/spec/unit/client_spec.rb +1 -0
  82. data/spec/unit/compliance/fetcher/automate_spec.rb +134 -0
  83. data/spec/unit/compliance/fetcher/chef_server_spec.rb +93 -0
  84. data/spec/unit/compliance/reporter/automate_spec.rb +427 -0
  85. data/spec/unit/compliance/reporter/chef_server_automate_spec.rb +177 -0
  86. data/spec/unit/compliance/reporter/compliance_enforcer_spec.rb +48 -0
  87. data/spec/unit/compliance/runner_spec.rb +167 -0
  88. data/spec/unit/http/ssl_policies_spec.rb +107 -68
  89. data/spec/unit/knife/bootstrap_spec.rb +5 -17
  90. data/spec/unit/knife/core/node_editor_spec.rb +1 -1
  91. data/spec/unit/knife/core/status_presenter_spec.rb +54 -0
  92. data/spec/unit/mixin/openssl_helper_spec.rb +0 -7
  93. data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
  94. data/spec/unit/platform/query_helpers_spec.rb +11 -12
  95. data/spec/unit/provider/dsc_resource_spec.rb +10 -27
  96. data/spec/unit/provider/dsc_script_spec.rb +1 -1
  97. data/spec/unit/provider/mount/windows_spec.rb +1 -0
  98. data/spec/unit/provider/package/freebsd/pkgng_spec.rb +1 -1
  99. data/spec/unit/provider/package/rubygems_spec.rb +39 -7
  100. data/spec/unit/provider/systemd_unit_spec.rb +1 -1
  101. data/spec/unit/resource/user/windows_user_spec.rb +36 -0
  102. data/spec/unit/resource/windows_certificate_spec.rb +12 -0
  103. data/spec/unit/util/dsc/configuration_generator_spec.rb +79 -0
  104. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +27 -35
  105. metadata +55 -18
  106. data/lib/chef/util/powershell/cmdlet.rb +0 -169
  107. data/lib/chef/util/powershell/cmdlet_result.rb +0 -61
  108. data/spec/data/trusted_certs_empty/.gitkeep +0 -0
  109. data/spec/data/trusted_certs_empty/README.md +0 -1
  110. data/spec/functional/util/powershell/cmdlet_spec.rb +0 -111
  111. data/spec/scripts/ssl-serve.rb +0 -47
  112. data/spec/unit/util/powershell/cmdlet_spec.rb +0 -106
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45d0c49c6265534a875b8044449678109b0c11a22b83027f28826e3ea14ee792
4
- data.tar.gz: a6375a107a0ece5bbd866f42c0d415e582135d24512d8815b894786e64c685b9
3
+ metadata.gz: 9707377bd0ca4839a54a9be2065b9d134d0425db2eeca3ae8837d632a5f6a0c3
4
+ data.tar.gz: cb0adbf00001fab2415c0e6d74404fcc6177182486cb7b3f00314bb8e8078c12
5
5
  SHA512:
6
- metadata.gz: 8cf6a1c8dd0316944eaa700f6c788e2ff6843dc9bd9252b2710e2465498041e1eb8a6990ace8bbef2bb265e3254e1460a56e9f85489d133a0dffbc03b140c928
7
- data.tar.gz: e57ee2f117d322936e53b384202691b7d9be946b4be9b4cc697d162163f3b65885109ef5977448861fa6f8db3cafbe364294ab2e30d78b61f7e51d8fc7eed16a
6
+ metadata.gz: de061bc2be417f63a03db3de0e96e7823618ba6bc5ea59c7f8391384c0cc29630abd5cea1fa8466bee2c7a35275ced0b3d1d4b88e3f6748f3b9560f161d622e8
7
+ data.tar.gz: 1a283cabcd3fb48178d97cb46fe5f8515201dec8c9250f5b43700bb0af2b5a467d9f9f0c5c986fd128c10f21f575cf08d8e61713ee37723f4488970dc4f823a0
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ source "https://rubygems.org"
7
7
  # of bundler versions prior to 1.12.0 (https://github.com/bundler/bundler/commit/193a14fe5e0d56294c7b370a0e59f93b2c216eed)
8
8
  gem "chef", path: "."
9
9
 
10
- gem "ohai", git: "https://github.com/chef/ohai.git", branch: "master"
10
+ gem "ohai", git: "https://github.com/chef/ohai.git", branch: "16-stable"
11
11
 
12
12
  gem "chef-utils", path: File.expand_path("chef-utils", __dir__) if File.exist?(File.expand_path("chef-utils", __dir__))
13
13
  gem "chef-config", path: File.expand_path("chef-config", __dir__) if File.exist?(File.expand_path("chef-config", __dir__))
@@ -27,8 +27,7 @@ gem "chef-telemetry", ">=1.0.8" # 1.0.8 removes the http dep
27
27
  group(:omnibus_package) do
28
28
  gem "appbundler"
29
29
  gem "rb-readline"
30
- gem "inspec-core", "~> 4.18"
31
- gem "inspec-core-bin", "~> 4.18" # need to provide the binaries for inspec
30
+ gem "inspec-core-bin", "~> 4.24" # need to provide the binaries for inspec
32
31
  gem "chef-vault"
33
32
  end
34
33
 
@@ -57,8 +56,7 @@ group(:development, :test) do
57
56
  end
58
57
 
59
58
  group(:chefstyle) do
60
- # for testing new chefstyle rules
61
- gem "chefstyle", git: "https://github.com/chef/chefstyle.git", branch: "master"
59
+ gem "chefstyle", "= 1.5.9"
62
60
  end
63
61
 
64
62
  instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"]
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Chef Infra
2
2
  [![Code Climate](https://codeclimate.com/github/chef/chef.svg)](https://codeclimate.com/github/chef/chef)
3
- [![Build Status](https://badge.buildkite.com/c82093430ceec7d27af05febb9dcafe3aa331fff9d74c0ab9d.svg?branch=master)](https://buildkite.com/chef-oss/chef-chef-master-verify)
3
+ [![Build Status](https://badge.buildkite.com/c82093430ceec7d27af05febb9dcafe3aa331fff9d74c0ab9d.svg?branch=chef-16)](https://buildkite.com/chef-oss/chef-chef-chef-16-verify)
4
4
  [![Gem Version](https://badge.fury.io/rb/chef.svg)](https://badge.fury.io/rb/chef)
5
- [![](https://img.shields.io/badge/Release%20Policy-Cadence%20Release-brightgreen.svg)](https://github.com/chef/chef/blob/v15.2.21/docs/dev/design_documents/client_release_cadence.md)
5
+ [![](https://img.shields.io/badge/Release%20Policy-Cadence%20Release-brightgreen.svg)](https://github.com/chef/chef/blob/master/docs/dev/design_documents/client_release_cadence.md)
6
6
 
7
7
  **Umbrella Project**: [Chef Infra](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-infra.md)
8
8
 
@@ -27,14 +27,15 @@ Gem::Specification.new do |s|
27
27
  s.add_dependency "mixlib-shellout", ">= 3.1.1", "< 4.0"
28
28
  s.add_dependency "mixlib-archive", ">= 0.4", "< 2.0"
29
29
  s.add_dependency "ohai", "~> 16.0"
30
+ s.add_dependency "inspec-core", "~> 4.23"
30
31
 
31
32
  s.add_dependency "ffi", ">= 1.9.25"
32
33
  s.add_dependency "ffi-yajl", "~> 2.2"
33
- s.add_dependency "net-ssh", ">= 4.2", "< 7"
34
+ s.add_dependency "net-ssh", ">= 5.1", "< 7"
34
35
  s.add_dependency "net-ssh-multi", "~> 1.2", ">= 1.2.1"
35
36
  s.add_dependency "net-sftp", ">= 2.1.2", "< 4.0"
36
37
  s.add_dependency "ed25519", "~> 1.2" # ed25519 ssh key support
37
- s.add_dependency "bcrypt_pbkdf", "= 1.1.0.rc1" # ed25519 ssh key support
38
+ s.add_dependency "bcrypt_pbkdf", "= 1.1.0.rc2" # ed25519 ssh key support
38
39
  s.add_dependency "highline", ">= 1.6.9", "< 3"
39
40
  s.add_dependency "tty-prompt", "~> 0.21" # knife ui.ask prompt
40
41
  s.add_dependency "tty-screen", "~> 0.6" # knife list
@@ -66,4 +67,13 @@ Gem::Specification.new do |s|
66
67
  Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } +
67
68
  Dir.glob("*.gemspec") +
68
69
  Dir.glob("tasks/rspec.rb")
70
+
71
+ s.metadata = {
72
+ "bug_tracker_uri" => "https://github.com/chef/chef/issues",
73
+ "changelog_uri" => "https://github.com/chef/chef/blob/master/CHANGELOG.md",
74
+ "documentation_uri" => "https://docs.chef.io/",
75
+ "homepage_uri" => "https://www.chef.io",
76
+ "mailing_list_uri" => "https://discourse.chef.io/",
77
+ "source_code_uri" => "https://github.com/chef/chef/",
78
+ }
69
79
  end
@@ -366,7 +366,7 @@ class Chef::Application::Base < Chef::Application
366
366
  Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
367
367
  if File.exist?(url)
368
368
  FileUtils.cp(url, path)
369
- elsif URI.regexp.match?(url)
369
+ elsif URI::DEFAULT_PARSER.make_regexp.match?(url)
370
370
  File.open(path, "wb") do |f|
371
371
  open(url) do |r|
372
372
  f.write(r.read)
@@ -57,6 +57,8 @@ require "ohai" unless defined?(Ohai::System)
57
57
  require "rbconfig" unless defined?(RbConfig)
58
58
  require "forwardable" unless defined?(Forwardable)
59
59
 
60
+ require_relative "compliance/runner"
61
+
60
62
  class Chef
61
63
  # == Chef::Client
62
64
  # The main object in a Chef run. Preps a Chef::Node and Chef::RunContext,
@@ -235,6 +237,7 @@ class Chef
235
237
 
236
238
  events.register(Chef::DataCollector::Reporter.new(events))
237
239
  events.register(Chef::ActionCollection.new(events))
240
+ events.register(Chef::Compliance::Runner.new)
238
241
 
239
242
  run_status.run_id = request_id = Chef::RequestID.instance.request_id
240
243
 
@@ -0,0 +1,93 @@
1
+ # Author:: Stephan Renatus <srenatus@chef.io>
2
+ # Copyright:: (c) 2016-2019, Chef Software Inc. <legal@chef.io>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "chef/node/attribute_collections" # for VividMash
17
+ require "chef/util/path_helper"
18
+
19
+ class Chef
20
+ module Compliance
21
+ DEFAULT_ATTRIBUTES = Chef::Node::VividMash.new(
22
+ # If enabled, a cache is built for all backend calls. This should only be
23
+ # disabled if you are expecting unique results from the same backend call.
24
+ # Under the covers, this controls :command and :file caching on Chef InSpec's
25
+ # Train connection.
26
+ "inspec_backend_cache" => true,
27
+
28
+ # Controls what is done with the resulting report after the Chef InSpec run.
29
+ # Accepts a single string value or an array of multiple values.
30
+ # Accepted values: 'chef-server-automate', 'chef-automate', 'json-file', 'audit-enforcer'
31
+ "reporter" => "json-file",
32
+
33
+ # Controls if Chef InSpec profiles should be fetched from Chef Automate or Chef Infra Server
34
+ # in addition to the default fetch locations provided by Chef Inspec.
35
+ # Accepted values: nil, 'chef-server', 'chef-automate'
36
+ "fetcher" => nil,
37
+
38
+ # Allow for connections to HTTPS endpoints using self-signed ssl certificates.
39
+ "insecure" => nil,
40
+
41
+ # Controls verbosity of Chef InSpec runner.
42
+ "quiet" => true,
43
+
44
+ # Chef Inspec Compliance profiles to be used for scan of node.
45
+ # See README.md for details
46
+ "profiles" => {},
47
+
48
+ # Extra inputs passed to Chef InSpec to allow finer-grained control over behavior.
49
+ # These are mapped to Chef InSpec's inputs, but are named attributes here for legacy reasons.
50
+ # See Chef Inspec's documentation for more information: https://docs.chef.io/inspec/inputs/
51
+ "attributes" => {},
52
+
53
+ # A string path or an array of paths to Chef InSpec waiver files.
54
+ # See Chef Inspec's documentation for more information: https://docs.chef.io/inspec/waivers/
55
+ "waiver_file" => nil,
56
+
57
+ "json_file" => {
58
+ # The location on disk that Chef InSpec's json reports are saved to when using the
59
+ # 'json-file' reporter. Defaults to:
60
+ # <chef_cache_path>/compliance_reports/compliance-<timestamp>.json
61
+ "location" => Chef::Util::PathHelper.join(
62
+ Chef::Config[:cache_path],
63
+ "compliance_reports",
64
+ Time.now.utc.strftime("compliance-%Y%m%d%H%M%S.json")
65
+ ),
66
+ },
67
+
68
+ # Control results that have a `run_time` below this limit will
69
+ # be stripped of the `start_time` and `run_time` fields to
70
+ # reduce the size of the reports being sent to Chef Automate.
71
+ "run_time_limit" => 1.0,
72
+
73
+ # A control result message that exceeds this character limit will be truncated.
74
+ # This helps keep reports to a reasonable size. On rare occasions, we've seen messages exceeding 9 MB in size,
75
+ # causing the report to not be ingested in the backend because of the 4 MB report size rpc limitation.
76
+ # Chef InSpec will append this text at the end of any truncated messages: `[Truncated to 10000 characters]`
77
+ "result_message_limit" => 10000,
78
+
79
+ # When a Chef InSpec resource throws an exception, results will contain a short error message and a
80
+ # detailed ruby stacktrace of the error. This attribute instructs Chef InSpec not to include the detailed stacktrace in order
81
+ # to keep the overall report to a manageable size.
82
+ "result_include_backtrace" => false,
83
+
84
+ # The array of results per control will be truncated at this limit to avoid large reports that cannot be
85
+ # processed by Chef Automate. A summary of removed results will be sent with each impacted control.
86
+ "control_results_limit" => 50,
87
+
88
+ # If enabled, a hash representation of the Chef Infra node object will be sent to Chef InSpec in an input
89
+ # named `chef_node`.
90
+ "chef_node_attribute_enabled" => false
91
+ )
92
+ end
93
+ end
@@ -0,0 +1,69 @@
1
+ require "uri" unless defined?(URI)
2
+ require "plugins/inspec-compliance/lib/inspec-compliance"
3
+
4
+ class Chef
5
+ module Compliance
6
+ module Fetcher
7
+ class Automate < ::InspecPlugins::Compliance::Fetcher
8
+ name "chef-automate"
9
+
10
+ # Positions this fetcher before Chef InSpec's `compliance` fetcher.
11
+ # Only load this file if you want to use Compliance Phase in Chef Solo with Chef Automate.
12
+ priority 502
13
+
14
+ CONFIG = {
15
+ "insecure" => true,
16
+ "token" => nil,
17
+ "server_type" => "automate",
18
+ "automate" => {
19
+ "ent" => "default",
20
+ "token_type" => "dctoken",
21
+ },
22
+ }.freeze
23
+
24
+ def self.resolve(target)
25
+ uri = get_target_uri(target)
26
+ return nil if uri.nil?
27
+
28
+ config = CONFIG.dup
29
+
30
+ # we have detailed information available in our lockfile, no need to ask the server
31
+ if target.respond_to?(:key?) && target.key?(:url)
32
+ profile_fetch_url = target[:url]
33
+ else
34
+ # verifies that the target e.g base/ssh exists
35
+ base_path = "/compliance/profiles/#{uri.host}#{uri.path}"
36
+
37
+ profile_path = if target.respond_to?(:key?) && target.key?(:version)
38
+ "#{base_path}/version/#{target[:version]}/tar"
39
+ else
40
+ "#{base_path}/tar"
41
+ end
42
+
43
+ url = URI(Chef::Config[:data_collector][:server_url])
44
+ url.path = profile_path
45
+ profile_fetch_url = url.to_s
46
+
47
+ config["token"] = Chef::Config[:data_collector][:token]
48
+
49
+ if config["token"].nil?
50
+ raise Inspec::FetcherFailure,
51
+ "No data-collector token set, which is required by the chef-automate fetcher. " \
52
+ "Set the `data_collector.token` configuration parameter in your client.rb " \
53
+ 'or use the "chef-server-automate" reporter which does not require any ' \
54
+ "data-collector settings and uses #{ChefUtils::Dist::Server::PRODUCT} to fetch profiles."
55
+ end
56
+ end
57
+
58
+ new(profile_fetch_url, config)
59
+ rescue URI::Error => _e
60
+ nil
61
+ end
62
+
63
+ def to_s
64
+ "#{ChefUtils::Dist::Automate::PRODUCT} for #{ChefUtils::Dist::Solo::PRODUCT} Fetcher"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,134 @@
1
+ require "uri" unless defined?(URI)
2
+ require "plugins/inspec-compliance/lib/inspec-compliance"
3
+
4
+ # This class implements an InSpec fetcher for Chef Server. The implementation
5
+ # is based on the Chef Compliance fetcher and only adapts the calls to redirect
6
+ # the requests via Chef Server.
7
+ #
8
+ # This implementation depends on chef-client runtime, therefore it is only executable
9
+ # inside of a chef-client run
10
+
11
+ class Chef
12
+ module Compliance
13
+ module Fetcher
14
+ class ChefServer < ::InspecPlugins::Compliance::Fetcher
15
+ name "chef-server"
16
+
17
+ # it positions itself before `compliance` fetcher
18
+ # only load it, if the Chef Server is integrated with Chef Compliance
19
+ priority 501
20
+
21
+ CONFIG = { "insecure" => true }.freeze
22
+
23
+ # Accepts URLs to compliance profiles in one of two forms:
24
+ # * a String URL with a compliance scheme, like "compliance://namespace/profile_name"
25
+ # * a Hash with a key of `compliance` and a value like "compliance/profile_name" and optionally a `version` key with a String value
26
+ def self.resolve(target)
27
+ profile_uri = get_target_uri(target)
28
+ return nil if profile_uri.nil?
29
+
30
+ organization = Chef::Config[:chef_server_url].split("/").last
31
+ owner = profile_uri.user ? "#{profile_uri.user}@#{profile_uri.host}" : profile_uri.host
32
+ version = target[:version] if target.respond_to?(:key?)
33
+
34
+ path_parts = [""]
35
+ path_parts << "compliance" if chef_server_reporter? || chef_server_fetcher?
36
+ path_parts << "organizations"
37
+ path_parts << organization
38
+ path_parts << "owners"
39
+ path_parts << owner
40
+ path_parts << "compliance"
41
+ path_parts << profile_uri.path
42
+ path_parts << "version/#{version}" if version
43
+ path_parts << "tar"
44
+
45
+ target_url = URI(Chef::Config[:chef_server_url])
46
+ target_url.path = File.join(path_parts)
47
+ Chef::Log.info("Fetching profile from: #{target_url}")
48
+
49
+ new(target_url, CONFIG)
50
+ rescue URI::Error => _e
51
+ nil
52
+ end
53
+
54
+ #
55
+ # We want to save compliance: in the lockfile rather than url: to
56
+ # make sure we go back through the ComplianceAPI handling.
57
+ #
58
+ def resolved_source
59
+ { compliance: chef_server_url }
60
+ end
61
+
62
+ # Downloads archive to temporary file using a Chef::ServerAPI
63
+ # client so that Chef Server's header-based authentication can be
64
+ # used.
65
+ def download_archive_to_temp
66
+ return @temp_archive_path unless @temp_archive_path.nil?
67
+
68
+ rest = Chef::ServerAPI.new(@target, Chef::Config.merge(ssl_verify_mode: :verify_none))
69
+ archive = with_http_rescue do
70
+ rest.streaming_request(@target)
71
+ end
72
+ @archive_type = ".tar.gz"
73
+
74
+ if archive.nil?
75
+ path = @target.respond_to?(:path) ? @target.path : path
76
+ raise Inspec::FetcherFailure, "Unable to find requested profile on path: '#{path}' on the #{ChefUtils::Dist::Automate::PRODUCT} system."
77
+ end
78
+
79
+ Inspec::Log.debug("Archive stored at temporary location: #{archive.path}")
80
+ @temp_archive_path = archive.path
81
+ end
82
+
83
+ def with_http_rescue
84
+ response = yield
85
+ if response.respond_to?(:code)
86
+ # handle non 200 error codes, they are not raised as Net::HTTPClientException
87
+ handle_http_error_code(response.code) if response.code.to_i >= 300
88
+ end
89
+ response
90
+ rescue Net::HTTPClientException => e
91
+ Chef::Log.error e
92
+ handle_http_error_code(e.response.code)
93
+ end
94
+
95
+ def handle_http_error_code(code)
96
+ case code
97
+ when /401|403/
98
+ Chef::Log.error "Auth issue: see the Compliance Phase troubleshooting documentation (http://docs.chef.io/chef_compliance_phase/#troubleshooting)."
99
+ when /404/
100
+ Chef::Log.error "Object does not exist on remote server."
101
+ when /413/
102
+ 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/)"
103
+ when /429/
104
+ 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."
105
+ end
106
+ msg = "Received HTTP error #{code}"
107
+ Chef::Log.error msg
108
+ raise Inspec::FetcherFailure, msg
109
+ end
110
+
111
+ def to_s
112
+ "#{ChefUtils::Dist::Server::PRODUCT}/Compliance Profile Loader"
113
+ end
114
+
115
+ CHEF_SERVER_REPORTERS = %w{chef-server chef-server-compliance chef-server-visibility chef-server-automate}.freeze
116
+ def self.chef_server_reporter?
117
+ (Array(Chef.node.attributes["audit"]["reporter"]) & CHEF_SERVER_REPORTERS).any?
118
+ end
119
+
120
+ CHEF_SERVER_FETCHERS = %w{chef-server chef-server-compliance chef-server-visibility chef-server-automate}.freeze
121
+ def self.chef_server_fetcher?
122
+ CHEF_SERVER_FETCHERS.include?(Chef.node.attributes["audit"]["fetcher"])
123
+ end
124
+
125
+ private
126
+
127
+ def chef_server_url
128
+ m = %r{^#{@config['server']}/owners/(?<owner>[^/]+)/compliance/(?<id>[^/]+)/tar$}.match(@target)
129
+ "#{m[:owner]}/#{m[:id]}"
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,201 @@
1
+ class Chef
2
+ module Compliance
3
+ module Reporter
4
+ #
5
+ # Used to send inspec reports to Chef Automate via the data_collector service
6
+ #
7
+ class Automate
8
+ def initialize(opts)
9
+ @entity_uuid = opts[:entity_uuid]
10
+ @run_id = opts[:run_id]
11
+ @node_name = opts[:node_info][:node]
12
+ @environment = opts[:node_info][:environment]
13
+ @roles = opts[:node_info][:roles]
14
+ @recipes = opts[:node_info][:recipes]
15
+ @insecure = opts[:insecure]
16
+ @chef_tags = opts[:node_info][:chef_tags]
17
+ @policy_group = opts[:node_info][:policy_group]
18
+ @policy_name = opts[:node_info][:policy_name]
19
+ @source_fqdn = opts[:node_info][:source_fqdn]
20
+ @organization_name = opts[:node_info][:organization_name]
21
+ @ipaddress = opts[:node_info][:ipaddress]
22
+ @fqdn = opts[:node_info][:fqdn]
23
+ @run_time_limit = opts[:run_time_limit]
24
+ @control_results_limit = opts[:control_results_limit]
25
+ @timestamp = opts.fetch(:timestamp) { Time.now }
26
+
27
+ @url = Chef::Config[:data_collector][:server_url]
28
+ @token = Chef::Config[:data_collector][:token]
29
+ end
30
+
31
+ # Method used in order to send the inspec report to the data_collector server
32
+ def send_report(report)
33
+ unless @entity_uuid && @run_id
34
+ Chef::Log.error "entity_uuid(#{@entity_uuid}) or run_id(#{@run_id}) can't be nil, not sending report to #{ChefUtils::Dist::Automate::PRODUCT}"
35
+ return false
36
+ end
37
+
38
+ unless @url && @token
39
+ 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"
40
+ return false
41
+ end
42
+
43
+ headers = {
44
+ "Content-Type" => "application/json",
45
+ "x-data-collector-auth" => "version=1.0",
46
+ "x-data-collector-token" => @token,
47
+ }
48
+
49
+ all_report_shas = report[:profiles].map { |p| p[:sha256] }
50
+ missing_report_shas = missing_automate_profiles(headers, all_report_shas)
51
+
52
+ full_report = truncate_controls_results(enriched_report(report), @control_results_limit)
53
+ full_report = strip_profiles_meta(full_report, missing_report_shas, @run_time_limit)
54
+ json_report = Chef::JSONCompat.to_json(full_report, validate_utf8: false)
55
+
56
+ # Automate GRPC currently has a message limit of ~4MB
57
+ # https://github.com/chef/automate/issues/1417#issuecomment-541908157
58
+ if json_report.bytesize > 4 * 1024 * 1024
59
+ Chef::Log.warn "Generated report size is #{(json_report.bytesize / (1024 * 1024.0)).round(2)} MB. #{ChefUtils::Dist::Automate::PRODUCT} has an internal 4MB limit that is not currently configurable."
60
+ end
61
+
62
+ unless json_report
63
+ Chef::Log.warn "Something went wrong, report can't be nil"
64
+ return false
65
+ end
66
+
67
+ begin
68
+ Chef::Log.info "Report to #{ChefUtils::Dist::Automate::PRODUCT}: #{@url}"
69
+ Chef::Log.debug "Compliance Report: #{json_report}"
70
+ http_client.post(nil, json_report, headers)
71
+ true
72
+ rescue => e
73
+ Chef::Log.error "send_report: POST to #{@url} returned: #{e.message}"
74
+ false
75
+ end
76
+ end
77
+
78
+ def http_client(url = @url)
79
+ if @insecure
80
+ Chef::HTTP.new(url, ssl_verify_mode: :verify_none)
81
+ else
82
+ Chef::HTTP.new(url)
83
+ end
84
+ end
85
+
86
+ def enriched_report(final_report)
87
+ final_report[:profiles].compact!
88
+
89
+ # Label this content as an inspec_report
90
+ final_report[:type] = "inspec_report"
91
+
92
+ final_report[:node_name] = @node_name
93
+ final_report[:end_time] = @timestamp.utc.strftime("%FT%TZ")
94
+ final_report[:node_uuid] = @entity_uuid
95
+ final_report[:environment] = @environment
96
+ final_report[:roles] = @roles
97
+ final_report[:recipes] = @recipes
98
+ final_report[:report_uuid] = @run_id
99
+ final_report[:source_fqdn] = @source_fqdn
100
+ final_report[:organization_name] = @organization_name
101
+ final_report[:policy_group] = @policy_group
102
+ final_report[:policy_name] = @policy_name
103
+ final_report[:chef_tags] = @chef_tags
104
+ final_report[:ipaddress] = @ipaddress
105
+ final_report[:fqdn] = @fqdn
106
+
107
+ final_report
108
+ end
109
+
110
+ CONTROL_RESULT_SORT_ORDER = %w{ failed skipped passed }.freeze
111
+
112
+ # Truncates the number of results per control in the report when they exceed max_results.
113
+ # The truncation prioritizes failed and skipped results over passed ones.
114
+ # Controls where results have been truncated will get a new object 'removed_results_counts'
115
+ # with the status counts of the truncated results
116
+ def truncate_controls_results(report, max_results)
117
+ return report unless max_results.is_a?(Integer) && max_results > 0
118
+
119
+ report.fetch(:profiles, []).each do |profile|
120
+ profile.fetch(:controls, []).each do |control|
121
+ # Only bother with truncation if the number of results exceed max_results
122
+ next unless control[:results].length > max_results
123
+
124
+ res = control[:results]
125
+ res.sort_by! { |r| CONTROL_RESULT_SORT_ORDER.index(r[:status]) }
126
+
127
+ # Count the results that will be truncated
128
+ truncated = { failed: 0, skipped: 0, passed: 0 }
129
+ (max_results..res.length - 1).each do |i|
130
+ case res[i][:status]
131
+ when "failed"
132
+ truncated[:failed] += 1
133
+ when "skipped"
134
+ truncated[:skipped] += 1
135
+ when "passed"
136
+ truncated[:passed] += 1
137
+ end
138
+ end
139
+ # Truncate the results array now
140
+ control[:results] = res[0..max_results - 1]
141
+ control[:removed_results_counts] = truncated
142
+ end
143
+ end
144
+ report
145
+ end
146
+
147
+ # Contacts the metasearch Automate API to check which of the inspec profile sha256 ids
148
+ # passed in via `report_shas` are missing from the Automate profiles metadata database.
149
+ def missing_automate_profiles(headers, report_shas)
150
+ Chef::Log.debug "Checking the #{ChefUtils::Dist::Automate::PRODUCT} profiles metadata for: #{report_shas}"
151
+ meta_url = URI(@url)
152
+ meta_url.path = "/compliance/profiles/metasearch"
153
+ response_str = http_client(meta_url.to_s).post(nil, "{\"sha256\": #{report_shas}}", headers)
154
+ missing_shas = Chef::JSONCompat.parse(response_str)["missing_sha256"]
155
+ unless missing_shas.empty?
156
+ Chef::Log.info "#{ChefUtils::Dist::Automate::PRODUCT} is missing metadata for the following profile ids: #{missing_shas}"
157
+ end
158
+ missing_shas
159
+ rescue => e
160
+ Chef::Log.error "missing_automate_profiles error: #{e.message}"
161
+ # If we get an error it's safer to assume none of the profile shas exist in Automate
162
+ report_shas
163
+ end
164
+
165
+ # Profile 'name' is a required property.
166
+ # By not sending it in the report, we make it clear to the ingestion backend that the profile metadata has been stripped from this profile in the report.
167
+ # Profile 'title' and 'version' are still kept for troubleshooting purposes in the backend.
168
+ SEEN_PROFILE_UNNECESSARY_FIELDS = %i{ copyright copyright_email groups license maintainer name summary supports}.freeze
169
+
170
+ SEEN_PROFILE_UNNECESSARY_CONTROL_FIELDS = %i{ code desc descriptions impact refs source_location tags title }.freeze
171
+
172
+ # TODO: This mutates the report and probably doesn't need to.
173
+ def strip_profiles_meta(report, missing_report_shas, run_time_limit)
174
+ report[:profiles].each do |p|
175
+ next if missing_report_shas.include?(p[:sha256])
176
+
177
+ p.delete_if { |f| SEEN_PROFILE_UNNECESSARY_FIELDS.include?(f) }
178
+
179
+ next unless p[:controls].is_a?(Array)
180
+
181
+ p[:controls].each do |c|
182
+ c.delete_if { |f| SEEN_PROFILE_UNNECESSARY_CONTROL_FIELDS.include?(f) }
183
+ c.delete(:waiver_data) if c[:waiver_data] == {}
184
+
185
+ next unless c[:results].is_a?(Array)
186
+
187
+ c[:results].each do |r|
188
+ if r[:run_time].is_a?(Float) && r[:run_time] < run_time_limit
189
+ r.delete(:start_time)
190
+ r.delete(:run_time)
191
+ end
192
+ end
193
+ end
194
+ end
195
+ report[:run_time_limit] = run_time_limit
196
+ report
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end