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
@@ -0,0 +1,177 @@
1
+ require "spec_helper"
2
+
3
+ describe Chef::Compliance::Reporter::ChefServerAutomate do
4
+ before do
5
+ WebMock.disable_net_connect!
6
+
7
+ Chef::Config[:client_key] = File.expand_path("../../../data/ssl/private_key.pem", __dir__)
8
+ Chef::Config[:node_name] = "spec-node"
9
+ end
10
+
11
+ let(:reporter) { Chef::Compliance::Reporter::ChefServerAutomate.new(opts) }
12
+
13
+ let(:opts) do
14
+ {
15
+ entity_uuid: "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
16
+ run_id: "3f0536f7-3361-4bca-ae53-b45118dceb5d",
17
+ node_info: {
18
+ node: "chef-client.solo",
19
+ environment: "My Prod Env",
20
+ roles: %w{base_linux apache_linux},
21
+ recipes: ["some_cookbook::some_recipe", "some_cookbook"],
22
+ policy_name: "test_policy_name",
23
+ policy_group: "test_policy_group",
24
+ chef_tags: ["mylinux", "my.tag", "some=tag"],
25
+ organization_name: "test_org",
26
+ source_fqdn: "api.chef.io",
27
+ ipaddress: "192.168.56.33",
28
+ fqdn: "lb1.prod.example.com",
29
+ },
30
+ url: "https://chef.server/data_collector",
31
+ control_results_limit: 2,
32
+ timestamp: Time.parse("2016-07-19T19:19:19+01:00"),
33
+ }
34
+ end
35
+
36
+ let(:inspec_report) do
37
+ {
38
+ "version": "1.2.1",
39
+ "profiles":
40
+ [{ "name": "tmp_compliance_profile",
41
+ "title": "/tmp Compliance Profile",
42
+ "summary": "An Example Compliance Profile",
43
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
44
+ "version": "0.1.1",
45
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
46
+ "license": "Apache 2.0 License",
47
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
48
+ "supports": [],
49
+ "controls":
50
+ [{ "title": "A /tmp directory must exist",
51
+ "desc": "A /tmp directory must exist",
52
+ "impact": 0.3,
53
+ "refs": [],
54
+ "tags": {},
55
+ "code": "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
56
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
57
+ "id": "tmp-1.0",
58
+ "results": [
59
+ { "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" },
60
+ ],
61
+ },
62
+ { "title": "/tmp directory is owned by the root user",
63
+ "desc": "The /tmp directory must be owned by the root user",
64
+ "impact": 0.3,
65
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
66
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
67
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
68
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
69
+ "id": "tmp-1.1",
70
+ "results": [
71
+ { "status": "passed", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
72
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
73
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
74
+ ],
75
+ },
76
+ ],
77
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
78
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }] }],
79
+ "other_checks": [],
80
+ "statistics": { "duration": 0.032332 },
81
+ }
82
+ end
83
+
84
+ let(:enriched_report) do
85
+ {
86
+ "version": "1.2.1",
87
+ "profiles": [
88
+ {
89
+ "name": "tmp_compliance_profile",
90
+ "title": "/tmp Compliance Profile",
91
+ "summary": "An Example Compliance Profile",
92
+ "sha256": "7bd598e369970002fc6f2d16d5b988027d58b044ac3fa30ae5fc1b8492e215cd",
93
+ "version": "0.1.1",
94
+ "maintainer": "Nathen Harvey <nharvey@chef.io>",
95
+ "license": "Apache 2.0 License",
96
+ "copyright": "Nathen Harvey <nharvey@chef.io>",
97
+ "supports": [],
98
+ "controls": [
99
+ {
100
+ "title": "A /tmp directory must exist",
101
+ "desc": "A /tmp directory must exist",
102
+ "impact": 0.3,
103
+ "refs": [],
104
+ "tags": {},
105
+ "code":
106
+ "control 'tmp-1.0' do\n impact 0.3\n title 'A /tmp directory must exist'\n desc 'A /tmp directory must exist'\n describe file '/tmp' do\n it { should be_directory }\n end\nend\n",
107
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 3 },
108
+ "id": "tmp-1.0",
109
+ "results": [{ "status": "passed", "code_desc": "File /tmp should be directory", "run_time": 0.002312, "start_time": "2016-10-19 11:09:43 -0400" }],
110
+ },
111
+ {
112
+ "title": "/tmp directory is owned by the root user",
113
+ "desc": "The /tmp directory must be owned by the root user",
114
+ "impact": 0.3,
115
+ "refs": [{ "url": "https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf", "ref": "Compliance Whitepaper" }],
116
+ "tags": { "production": nil, "development": nil, "identifier": "value", "remediation": "https://github.com/chef-cookbooks/audit" },
117
+ "code": "control 'tmp-1.1' do\n impact 0.3\n title '/tmp directory is owned by the root user'\n desc 'The /tmp directory must be owned by the root user'\n tag 'production','development'\n tag identifier: 'value'\n tag remediation: 'https://github.com/chef-cookbooks/audit'\n ref 'Compliance Whitepaper', url: 'https://pages.chef.io/rs/255-VFB-268/images/compliance-at-velocity2015.pdf'\n describe file '/tmp' do\n it { should be_owned_by 'root' }\n end\nend\n",
118
+ "source_location": { "ref": "/Users/vjeffrey/code/delivery/insights/data_generator/chef-client/cache/cookbooks/test-cookbook/recipes/../files/default/compliance_profiles/tmp_compliance_profile/controls/tmp.rb", "line": 12 },
119
+ "id": "tmp-1.1",
120
+ "results": [
121
+ { "status": "failed", "code_desc": "File /etc/hosts is expected to be directory", "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400", "message": "expected `File /etc/hosts.directory?` to return true, got false" },
122
+ { "status": "skipped", "code_desc": 'File /tmp should be owned by "root"', "run_time": 1.228845, "start_time": "2016-10-19 11:09:43 -0400" },
123
+ ],
124
+ "removed_results_counts": { "failed": 0, "skipped": 0, "passed": 1 },
125
+ },
126
+ ],
127
+ "groups": [{ "title": "/tmp Compliance Profile", "controls": ["tmp-1.0", "tmp-1.1"], "id": "controls/tmp.rb" }],
128
+ "attributes": [{ "name": "syslog_pkg", "options": { "default": "rsyslog", "description": "syslog package..." } }],
129
+ },
130
+ ],
131
+ "other_checks": [],
132
+ "statistics": { "duration": 0.032332 },
133
+ "type": "inspec_report",
134
+ "node_name": "chef-client.solo",
135
+ "end_time": "2016-07-19T18:19:19Z",
136
+ "node_uuid": "aaaaaaaa-709a-475d-bef5-zzzzzzzzzzzz",
137
+ "environment": "My Prod Env",
138
+ "roles": %w{base_linux apache_linux},
139
+ "recipes": ["some_cookbook::some_recipe", "some_cookbook"],
140
+ "report_uuid": "3f0536f7-3361-4bca-ae53-b45118dceb5d",
141
+ "source_fqdn": "api.chef.io",
142
+ "organization_name": "test_org",
143
+ "policy_group": "test_policy_group",
144
+ "policy_name": "test_policy_name",
145
+ "chef_tags": ["mylinux", "my.tag", "some=tag"],
146
+ "ipaddress": "192.168.56.33",
147
+ "fqdn": "lb1.prod.example.com",
148
+ }
149
+ end
150
+
151
+ it "sends report successfully" do
152
+ # TODO: Had to change 'X-Ops-Server-Api-Version' from 1 to 2, is that correct?
153
+ report_stub = stub_request(:post, "https://chef.server/data_collector")
154
+ .with(
155
+ body: enriched_report,
156
+ headers: {
157
+ "X-Chef-Version" => Chef::VERSION,
158
+ "X-Ops-Authorization-1" => /.+/,
159
+ "X-Ops-Authorization-2" => /.+/,
160
+ "X-Ops-Authorization-3" => /.+/,
161
+ "X-Ops-Authorization-4" => /.+/,
162
+ "X-Ops-Authorization-5" => /.+/,
163
+ "X-Ops-Authorization-6" => /.+/,
164
+ "X-Ops-Content-Hash" => "yfck5nQDcRWta06u45Q+J463LYY=",
165
+ "X-Ops-Server-Api-Version" => "2",
166
+ "X-Ops-Sign" => "algorithm=sha1;version=1.1;",
167
+ "X-Ops-Timestamp" => /.+/,
168
+ "X-Ops-Userid" => "spec-node",
169
+ "X-Remote-Request-Id" => /.+/,
170
+ }
171
+ ).to_return(status: 200)
172
+
173
+ expect(reporter.send_report(inspec_report)).to eq(true)
174
+
175
+ expect(report_stub).to have_been_requested
176
+ end
177
+ end
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+
3
+ describe Chef::Compliance::Reporter::AuditEnforcer do
4
+ let(:reporter) { Chef::Compliance::Reporter::AuditEnforcer.new }
5
+
6
+ it "does not raise error for a successful InSpec report" do
7
+ report = {
8
+ "profiles": [
9
+ {
10
+ "controls": [
11
+ { "id": "c1", "results": [{ "status": "passed" }] },
12
+ { "id": "c2", "results": [{ "status": "passed" }] },
13
+ ],
14
+ },
15
+ ],
16
+ }
17
+
18
+ expect(reporter.send_report(report)).to eq(true)
19
+ end
20
+
21
+ it "does not raise error for an InSpec report with no controls" do
22
+ report = { "profiles": [{ "name": "empty" }] }
23
+
24
+ expect(reporter.send_report(report)).to eq(true)
25
+ end
26
+
27
+ it "does not raise error for an InSpec report with controls but no results" do
28
+ report = { "profiles": [{ "controls": [{ "id": "empty" }] }] }
29
+ expect(reporter.send_report(report)).to eq(true)
30
+ end
31
+
32
+ it "raises an error for a failed InSpec report" do
33
+ report = {
34
+ "profiles": [
35
+ {
36
+ "controls": [
37
+ { "id": "c1", "results": [{ "status": "passed" }] },
38
+ { "id": "c2", "results": [{ "status": "failed" }] },
39
+ ],
40
+ },
41
+ ],
42
+ }
43
+
44
+ expect {
45
+ reporter.send_report(report)
46
+ }.to raise_error(Chef::Compliance::Reporter::AuditEnforcer::ControlFailure, "Audit c2 has failed. Aborting chef-client run.")
47
+ end
48
+ end
@@ -0,0 +1,167 @@
1
+ require "spec_helper"
2
+
3
+ describe Chef::Compliance::Runner do
4
+ let(:logger) { double(:logger).as_null_object }
5
+ let(:node) { Chef::Node.new(logger: logger) }
6
+
7
+ let(:runner) do
8
+ described_class.new.tap do |r|
9
+ r.node = node
10
+ r.run_id = "my_run_id"
11
+ end
12
+ end
13
+
14
+ describe "#enabled?" do
15
+ it "is true if the node attributes have audit profiles and the audit cookbook is not present" do
16
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
17
+ node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
18
+
19
+ expect(runner).to be_enabled
20
+ end
21
+
22
+ it "is false if the node attributes have audit profiles and the audit cookbook is present" do
23
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
24
+ node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
25
+
26
+ expect(runner).not_to be_enabled
27
+ end
28
+
29
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is not present" do
30
+ node.normal["audit"]["profiles"] = {}
31
+ node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
32
+
33
+ expect(runner).not_to be_enabled
34
+ end
35
+
36
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is present" do
37
+ node.normal["audit"]["profiles"] = {}
38
+ node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
39
+
40
+ expect(runner).not_to be_enabled
41
+ end
42
+
43
+ it "is false if the node attributes do not have audit attributes and the audit cookbook is not present" do
44
+ node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
45
+ expect(runner).not_to be_enabled
46
+ end
47
+ end
48
+
49
+ describe "#inspec_profiles" do
50
+ it "returns an empty list with no profiles defined" do
51
+ expect(runner.inspec_profiles).to eq([])
52
+ end
53
+
54
+ it "converts from the attribute format to the format Inspec expects" do
55
+ node.normal["audit"]["profiles"]["linux-baseline"] = {
56
+ 'compliance': "user/linux-baseline",
57
+ 'version': "2.1.0",
58
+ }
59
+
60
+ node.normal["audit"]["profiles"]["ssh"] = {
61
+ 'supermarket': "hardening/ssh-hardening",
62
+ }
63
+
64
+ expected = [
65
+ {
66
+ compliance: "user/linux-baseline",
67
+ name: "linux-baseline",
68
+ version: "2.1.0",
69
+ },
70
+ {
71
+ name: "ssh",
72
+ supermarket: "hardening/ssh-hardening",
73
+ },
74
+ ]
75
+
76
+ expect(runner.inspec_profiles).to eq(expected)
77
+ end
78
+
79
+ it "raises an error when the profiles are in the old audit-cookbook format" do
80
+ node.normal["audit"]["profiles"] = [
81
+ {
82
+ name: "Windows 2019 Baseline",
83
+ compliance: "admin/windows-2019-baseline",
84
+ },
85
+ ]
86
+
87
+ expect { runner.inspec_profiles }.to raise_error(/profiles specified in an unrecognized format, expected a hash of hashes./)
88
+ end
89
+ end
90
+
91
+ describe "#warn_for_deprecated_config_values!" do
92
+ it "logs a warning when deprecated config values are present" do
93
+ node.normal["audit"]["owner"] = "my_org"
94
+ node.normal["audit"]["inspec_version"] = "90210"
95
+
96
+ expect(logger).to receive(:warn).with(/config values 'inspec_version', 'owner' are not supported/)
97
+
98
+ runner.warn_for_deprecated_config_values!
99
+ end
100
+
101
+ it "does not log a warning with no deprecated config values" do
102
+ node.normal["audit"]["profiles"]["linux-baseline"] = {
103
+ 'compliance': "user/linux-baseline",
104
+ 'version': "2.1.0",
105
+ }
106
+
107
+ expect(logger).not_to receive(:warn)
108
+
109
+ runner.warn_for_deprecated_config_values!
110
+ end
111
+ end
112
+
113
+ describe "#reporter" do
114
+ context "chef-server-automate reporter" do
115
+ it "uses the correct URL when 'server' attribute is set" do
116
+ Chef::Config[:chef_server_url] = "https://chef_config_url.example.com/my_org"
117
+ node.normal["audit"]["server"] = "https://server_attribute_url.example.com/application/sub_application"
118
+
119
+ reporter = runner.reporter("chef-server-automate")
120
+
121
+ expect(reporter).to be_kind_of(Chef::Compliance::Reporter::ChefServerAutomate)
122
+ expect(reporter.url).to eq(URI("https://server_attribute_url.example.com/application/sub_application/organizations/my_org/data-collector"))
123
+ end
124
+
125
+ it "falls back to chef_server_url for URL when 'server' attribute is not set" do
126
+ Chef::Config[:chef_server_url] = "https://chef_config_url.example.com/my_org"
127
+
128
+ reporter = runner.reporter("chef-server-automate")
129
+
130
+ expect(reporter).to be_kind_of(Chef::Compliance::Reporter::ChefServerAutomate)
131
+ expect(reporter.url).to eq(URI("https://chef_config_url.example.com/organizations/my_org/data-collector"))
132
+ end
133
+ end
134
+
135
+ it "fails with unexpected reporter value" do
136
+ expect { runner.reporter("tacos") }.to raise_error(/'tacos' is not a supported reporter for Compliance Phase/)
137
+ end
138
+ end
139
+
140
+ describe "#inspec_opts" do
141
+ it "does not include chef_node in inputs by default" do
142
+ node.normal["audit"]["attributes"] = {
143
+ "tacos" => "lunch",
144
+ "nachos" => "dinner",
145
+ }
146
+
147
+ inputs = runner.inspec_opts[:inputs]
148
+
149
+ expect(inputs["tacos"]).to eq("lunch")
150
+ expect(inputs.key?("chef_node")).to eq(false)
151
+ end
152
+
153
+ it "includes chef_node in inputs with chef_node_attribute_enabled set" do
154
+ node.normal["audit"]["chef_node_attribute_enabled"] = true
155
+ node.normal["audit"]["attributes"] = {
156
+ "tacos" => "lunch",
157
+ "nachos" => "dinner",
158
+ }
159
+
160
+ inputs = runner.inspec_opts[:inputs]
161
+
162
+ expect(inputs["tacos"]).to eq("lunch")
163
+ expect(inputs["chef_node"]["audit"]["reporter"]).to eq("json-file")
164
+ expect(inputs["chef_node"]["chef_environment"]).to eq("_default")
165
+ end
166
+ end
167
+ end
@@ -26,83 +26,86 @@ describe "HTTP SSL Policy" do
26
26
  Chef::Config[:ssl_client_key] = nil
27
27
  Chef::Config[:ssl_ca_path] = nil
28
28
  Chef::Config[:ssl_ca_file] = nil
29
+ ENV["SSL_CERT_FILE"] = nil
29
30
  end
30
31
 
31
- let(:unconfigured_http_client) { Net::HTTP.new("example.com", 443) }
32
32
  let(:http_client) do
33
- unconfigured_http_client.use_ssl = true
34
- ssl_policy.apply
35
- unconfigured_http_client
33
+ ssl_policy_class.apply_to(Net::HTTP.new("example.com"))
36
34
  end
37
35
 
38
36
  describe Chef::HTTP::DefaultSSLPolicy do
39
37
 
40
- let(:ssl_policy) { Chef::HTTP::DefaultSSLPolicy.new(unconfigured_http_client) }
38
+ let(:ssl_policy_class) { Chef::HTTP::DefaultSSLPolicy }
41
39
 
42
- describe "when configured with :ssl_verify_mode set to :verify peer" do
43
- before do
44
- Chef::Config[:ssl_verify_mode] = :verify_peer
45
- end
46
-
47
- it "configures the HTTP client to use SSL when given a URL with the https protocol" do
48
- expect(http_client.use_ssl?).to be_truthy
49
- end
40
+ it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do
41
+ Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here"
42
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
43
+ end
50
44
 
51
- it "sets the OpenSSL verify mode to verify_peer" do
52
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
53
- end
45
+ it "should set the CA path if that is set in the configuration" do
46
+ Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl")
47
+ expect(http_client.ca_path).to eq(File.join(CHEF_SPEC_DATA, "ssl"))
48
+ end
54
49
 
55
- it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do
56
- Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here"
57
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
58
- end
50
+ it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do
51
+ Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here"
52
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
53
+ end
59
54
 
60
- it "should set the CA path if that is set in the configuration" do
61
- Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl")
62
- expect(http_client.ca_path).to eq(File.join(CHEF_SPEC_DATA, "ssl"))
63
- end
55
+ it "should set the CA file if that is set in the configuration" do
56
+ Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + "/ssl/5e707473.0"
57
+ expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/ssl/5e707473.0")
58
+ end
64
59
 
65
- it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do
66
- Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here"
67
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
68
- end
60
+ it "should set the custom CA file if SSL_CERT_FILE environment variable is set" do
61
+ ENV["SSL_CERT_FILE"] = CHEF_SPEC_DATA + "/trusted_certs/intermediate.pem"
62
+ expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/trusted_certs/intermediate.pem")
63
+ end
69
64
 
70
- it "should set the CA file if that is set in the configuration" do
71
- Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + "/ssl/5e707473.0"
72
- expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + "/ssl/5e707473.0")
73
- end
65
+ it "raises a ConfigurationError if SSL_CERT_FILE environment variable is set to a file that does not exist" do
66
+ ENV["SSL_CERT_FILE"] = "/dev/null/nothing_here"
67
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
74
68
  end
75
69
 
76
- describe "when configured with :ssl_verify_mode set to :verify peer" do
77
- before do
78
- @url = URI.parse("https://chef.example.com:4443/")
79
- Chef::Config[:ssl_verify_mode] = :verify_none
80
- end
70
+ it "sets the OpenSSL verify mode to verify_peer when configured with :ssl_verify_mode set to :verify_peer" do
71
+ Chef::Config[:ssl_verify_mode] = :verify_peer
72
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
73
+ end
81
74
 
82
- it "sets the OpenSSL verify mode to :verify_none" do
83
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
84
- end
75
+ it "sets the OpenSSL verify mode to :verify_none when configured with :ssl_verify_mode set to :verify_none" do
76
+ Chef::Config[:ssl_verify_mode] = :verify_none
77
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
85
78
  end
86
79
 
87
80
  describe "when configured with a client certificate" do
88
- before { @url = URI.parse("https://chef.example.com:4443/") }
89
-
90
81
  it "raises ConfigurationError if the certificate file doesn't exist" do
91
82
  Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
92
83
  Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
93
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
84
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /ssl_client_cert .* does not exist/)
94
85
  end
95
86
 
96
- it "raises ConfigurationError if the certificate file doesn't exist" do
87
+ it "raises ConfigurationError if the private key file doesn't exist" do
97
88
  Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
98
89
  Chef::Config[:ssl_client_key] = "/dev/null/nothing_here"
99
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
90
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /ssl_client_key .* does not exist/)
100
91
  end
101
92
 
102
93
  it "raises a ConfigurationError if one of :ssl_client_cert and :ssl_client_key is set but not both" do
103
94
  Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
104
95
  Chef::Config[:ssl_client_key] = nil
105
- expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError)
96
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /configure ssl_client_cert and ssl_client_key together/)
97
+ end
98
+
99
+ it "raises a ConfigurationError with a bad cert file" do
100
+ Chef::Config[:ssl_client_cert] = __FILE__
101
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
102
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading cert file '#{__FILE__}'/)
103
+ end
104
+
105
+ it "raises a ConfigurationError with a bad key file" do
106
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
107
+ Chef::Config[:ssl_client_key] = __FILE__
108
+ expect { http_client }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading key file '#{__FILE__}'/)
106
109
  end
107
110
 
108
111
  it "configures the HTTP client's cert and private key" do
@@ -111,20 +114,31 @@ describe "HTTP SSL Policy" do
111
114
  expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
112
115
  expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
113
116
  end
114
- end
115
117
 
116
- context "when additional certs are located in the trusted_certs dir" do
117
- let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt") }
118
- let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) }
118
+ it "configures the HTTP client's cert and private key with a DER encoded cert" do
119
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/binary/chef-rspec-der.cert"
120
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/chef-rspec.key"
121
+ expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
122
+ expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
123
+ end
119
124
 
120
- let(:additional_pem_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "opscode.pem") }
121
- let(:additional_pem) { OpenSSL::X509::Certificate.new(File.read(additional_pem_path)) }
125
+ it "configures the HTTP client's cert and private key with a DER encoded key" do
126
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + "/ssl/chef-rspec.cert"
127
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + "/ssl/binary/chef-rspec-der.key"
128
+ expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.cert")).to_s)
129
+ expect(http_client.key.to_s).to eq(OpenSSL::PKey::RSA.new(IO.read(CHEF_SPEC_DATA + "/ssl/chef-rspec.key")).to_s)
130
+ end
131
+ end
122
132
 
133
+ context "when additional certs are located in the trusted_certs dir" do
123
134
  before do
124
135
  Chef::Config.trusted_certs_dir = File.join(CHEF_SPEC_DATA, "trusted_certs")
125
136
  end
126
137
 
127
138
  it "enables verification of self-signed certificates" do
139
+ path = File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt")
140
+ self_signed_crt = OpenSSL::X509::Certificate.new(File.binread(path))
141
+
128
142
  expect(http_client.cert_store.verify(self_signed_crt)).to be_truthy
129
143
  end
130
144
 
@@ -137,39 +151,64 @@ describe "HTTP SSL Policy" do
137
151
  # If the machine running the test doesn't have ruby SSL configured correctly,
138
152
  # then the root cert also has to be loaded for the test to succeed.
139
153
  # The system under test **SHOULD** do both of these things.
154
+ path = File.join(CHEF_SPEC_DATA, "trusted_certs", "opscode.pem")
155
+ additional_pem = OpenSSL::X509::Certificate.new(File.binread(path))
156
+
140
157
  expect(http_client.cert_store.verify(additional_pem)).to be_truthy
141
158
  end
142
159
 
143
- context "and some certs are duplicates" do
144
- it "skips duplicate certs" do
145
- # For whatever reason, OpenSSL errors out when adding a
146
- # cert you already have to the certificate store.
147
- ssl_policy.set_custom_certs
148
- ssl_policy.set_custom_certs # should not raise an error
160
+ it "skips duplicate certs" do
161
+ # For whatever reason, OpenSSL errors out when adding a
162
+ # cert you already have to the certificate store.
163
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
164
+ ssl_policy.set_custom_certs
165
+ ssl_policy.set_custom_certs # should not raise an error
166
+ end
167
+
168
+ it "raises ConfigurationError with a bad cert file in the trusted_certs dir" do
169
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
170
+
171
+ Dir.mktmpdir do |dir|
172
+ bad_cert_file = File.join(dir, "bad_cert_file.crt")
173
+ File.write(bad_cert_file, File.read(__FILE__))
174
+
175
+ Chef::Config.trusted_certs_dir = dir
176
+ expect { ssl_policy.set_custom_certs }.to raise_error(Chef::Exceptions::ConfigurationError, /Error reading cert file/)
149
177
  end
150
178
  end
179
+
180
+ it "works with binary certs" do
181
+ Chef::Config.trusted_certs_dir = File.join(CHEF_SPEC_DATA, "ssl", "binary")
182
+
183
+ ssl_policy = ssl_policy_class.new(Net::HTTP.new("example.com"))
184
+ ssl_policy.set_custom_certs
185
+ end
151
186
  end
152
187
  end
153
188
 
154
189
  describe Chef::HTTP::APISSLPolicy do
155
190
 
156
- let(:ssl_policy) { Chef::HTTP::APISSLPolicy.new(unconfigured_http_client) }
191
+ let(:ssl_policy_class) { Chef::HTTP::APISSLPolicy }
157
192
 
158
- context "when verify_api_cert is set" do
159
- before do
160
- Chef::Config[:verify_api_cert] = true
161
- end
193
+ it "sets the OpenSSL verify mode to verify_peer when configured with :ssl_verify_mode set to :verify_peer" do
194
+ Chef::Config[:ssl_verify_mode] = :verify_peer
195
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
196
+ end
162
197
 
163
- it "sets the OpenSSL verify mode to verify_peer" do
164
- expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
165
- end
198
+ it "sets the OpenSSL verify mode to :verify_none when configured with :ssl_verify_mode set to :verify_none" do
199
+ Chef::Config[:ssl_verify_mode] = :verify_none
200
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
166
201
  end
167
202
 
203
+ it "sets the OpenSSL verify mode to verify_peer when verify_api_cert is set" do
204
+ Chef::Config[:verify_api_cert] = true
205
+ expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
206
+ end
168
207
  end
169
208
 
170
209
  describe Chef::HTTP::VerifyPeerSSLPolicy do
171
210
 
172
- let(:ssl_policy) { Chef::HTTP::VerifyPeerSSLPolicy.new(unconfigured_http_client) }
211
+ let(:ssl_policy_class) { Chef::HTTP::VerifyPeerSSLPolicy }
173
212
 
174
213
  it "sets the OpenSSL verify mode to verify_peer" do
175
214
  expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
@@ -179,7 +218,7 @@ describe "HTTP SSL Policy" do
179
218
 
180
219
  describe Chef::HTTP::VerifyNoneSSLPolicy do
181
220
 
182
- let(:ssl_policy) { Chef::HTTP::VerifyNoneSSLPolicy.new(unconfigured_http_client) }
221
+ let(:ssl_policy_class) { Chef::HTTP::VerifyNoneSSLPolicy }
183
222
 
184
223
  it "sets the OpenSSL verify mode to verify_peer" do
185
224
  expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)