chef 16.7.61-universal-mingw32 → 16.9.20-universal-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +3 -5
- data/README.md +2 -2
- data/chef.gemspec +12 -2
- data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll +0 -0
- data/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll +0 -0
- data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
- data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
- data/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
- data/distro/ruby_bin_folder/x86/Chef.PowerShell.dll +0 -0
- data/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll +0 -0
- data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll +0 -0
- data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll +0 -0
- data/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb +0 -0
- data/lib/chef/application/base.rb +1 -1
- data/lib/chef/client.rb +3 -0
- data/lib/chef/compliance/default_attributes.rb +93 -0
- data/lib/chef/compliance/fetcher/automate.rb +69 -0
- data/lib/chef/compliance/fetcher/chef_server.rb +134 -0
- data/lib/chef/compliance/reporter/automate.rb +201 -0
- data/lib/chef/compliance/reporter/chef_server_automate.rb +94 -0
- data/lib/chef/compliance/reporter/compliance_enforcer.rb +20 -0
- data/lib/chef/compliance/reporter/json_file.rb +19 -0
- data/lib/chef/compliance/runner.rb +262 -0
- data/lib/chef/cookbook_manifest.rb +1 -0
- data/lib/chef/encrypted_data_bag_item/assertions.rb +1 -1
- data/lib/chef/exceptions.rb +4 -0
- data/lib/chef/http/ssl_policies.rb +33 -14
- data/lib/chef/knife/bootstrap/train_connector.rb +1 -1
- data/lib/chef/knife/core/formatting_options.rb +49 -0
- data/lib/chef/knife/core/node_presenter.rb +0 -25
- data/lib/chef/knife/core/status_presenter.rb +1 -26
- data/lib/chef/knife/core/ui.rb +4 -1
- data/lib/chef/knife/core/windows_bootstrap_context.rb +1 -1
- data/lib/chef/knife/node_show.rb +2 -1
- data/lib/chef/knife/search.rb +2 -1
- data/lib/chef/knife/ssh.rb +3 -1
- data/lib/chef/knife/status.rb +8 -11
- data/lib/chef/mixin/powershell_exec.rb +3 -1
- data/lib/chef/platform/query_helpers.rb +4 -4
- data/lib/chef/policy_builder/policyfile.rb +1 -1
- data/lib/chef/powershell.rb +2 -0
- data/lib/chef/provider/dsc_resource.rb +12 -24
- data/lib/chef/provider/dsc_script.rb +16 -20
- data/lib/chef/provider/git.rb +5 -5
- data/lib/chef/provider/package.rb +53 -19
- data/lib/chef/provider/package/dnf.rb +39 -12
- data/lib/chef/provider/package/dnf/dnf_helper.py +18 -5
- data/lib/chef/provider/package/dnf/python_helper.rb +6 -6
- data/lib/chef/provider/package/freebsd/pkgng.rb +3 -1
- data/lib/chef/provider/yum_repository.rb +2 -2
- data/lib/chef/resource/chef_client_config.rb +1 -1
- data/lib/chef/resource/chef_gem.rb +2 -2
- data/lib/chef/resource/cron/cron_d.rb +1 -0
- data/lib/chef/resource/dsc_script.rb +8 -1
- data/lib/chef/resource/file.rb +1 -1
- data/lib/chef/resource/gem_package.rb +2 -2
- data/lib/chef/resource/homebrew_cask.rb +3 -3
- data/lib/chef/resource/hostname.rb +3 -3
- data/lib/chef/resource/http_request.rb +1 -1
- data/lib/chef/resource/locale.rb +1 -1
- data/lib/chef/resource/mdadm.rb +2 -2
- data/lib/chef/resource/osx_profile.rb +7 -7
- data/lib/chef/resource/remote_directory.rb +1 -1
- data/lib/chef/resource/ruby.rb +1 -5
- data/lib/chef/resource/ruby_block.rb +1 -1
- data/lib/chef/resource/template.rb +2 -2
- data/lib/chef/resource/user/windows_user.rb +5 -0
- data/lib/chef/resource/windows_certificate.rb +9 -13
- data/lib/chef/resource/yum_repository.rb +5 -0
- data/lib/chef/resource_collection/resource_set.rb +1 -1
- data/lib/chef/util/dsc/configuration_generator.rb +52 -11
- data/lib/chef/util/dsc/lcm_output_parser.rb +3 -4
- data/lib/chef/util/dsc/local_configuration_manager.rb +17 -14
- data/lib/chef/util/dsc/resource_store.rb +5 -11
- data/lib/chef/version.rb +1 -1
- data/lib/chef/win32/api/file.rb +4 -0
- data/spec/data/rubygems.org/latest_specs.4.8.gz +0 -0
- data/spec/data/rubygems.org/nonexistent_gem +0 -0
- data/spec/data/rubygems.org/sexp_processor +0 -0
- data/spec/data/rubygems.org/sexp_processor-4.15.1.gemspec.rz +0 -0
- data/spec/data/ssl/binary/chef-rspec-der.cert +0 -0
- data/spec/data/ssl/binary/chef-rspec-der.key +0 -0
- data/spec/functional/resource/dnf_package_spec.rb +319 -16
- data/spec/functional/resource/dsc_script_spec.rb +3 -6
- data/spec/functional/resource/windows_certificate_spec.rb +204 -384
- data/spec/integration/client/client_spec.rb +2 -1
- data/spec/integration/compliance/compliance_spec.rb +81 -0
- data/spec/integration/recipes/recipe_dsl_spec.rb +1 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/client_spec.rb +1 -0
- data/spec/unit/compliance/fetcher/automate_spec.rb +134 -0
- data/spec/unit/compliance/fetcher/chef_server_spec.rb +93 -0
- data/spec/unit/compliance/reporter/automate_spec.rb +427 -0
- data/spec/unit/compliance/reporter/chef_server_automate_spec.rb +177 -0
- data/spec/unit/compliance/reporter/compliance_enforcer_spec.rb +48 -0
- data/spec/unit/compliance/runner_spec.rb +167 -0
- data/spec/unit/http/ssl_policies_spec.rb +107 -68
- data/spec/unit/knife/bootstrap_spec.rb +5 -17
- data/spec/unit/knife/core/node_editor_spec.rb +1 -1
- data/spec/unit/knife/core/status_presenter_spec.rb +54 -0
- data/spec/unit/mixin/openssl_helper_spec.rb +0 -7
- data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
- data/spec/unit/platform/query_helpers_spec.rb +11 -12
- data/spec/unit/provider/dsc_resource_spec.rb +10 -27
- data/spec/unit/provider/dsc_script_spec.rb +1 -1
- data/spec/unit/provider/mount/windows_spec.rb +1 -0
- data/spec/unit/provider/package/freebsd/pkgng_spec.rb +1 -1
- data/spec/unit/provider/package/rubygems_spec.rb +39 -7
- data/spec/unit/provider/systemd_unit_spec.rb +1 -1
- data/spec/unit/resource/user/windows_user_spec.rb +36 -0
- data/spec/unit/resource/windows_certificate_spec.rb +12 -0
- data/spec/unit/util/dsc/configuration_generator_spec.rb +79 -0
- data/spec/unit/util/dsc/local_configuration_manager_spec.rb +27 -35
- metadata +55 -18
- data/lib/chef/util/powershell/cmdlet.rb +0 -169
- data/lib/chef/util/powershell/cmdlet_result.rb +0 -61
- data/spec/data/trusted_certs_empty/.gitkeep +0 -0
- data/spec/data/trusted_certs_empty/README.md +0 -1
- data/spec/functional/util/powershell/cmdlet_spec.rb +0 -111
- data/spec/scripts/ssl-serve.rb +0 -47
- 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
|
-
|
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(:
|
38
|
+
let(:ssl_policy_class) { Chef::HTTP::DefaultSSLPolicy }
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
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
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
121
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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(:
|
191
|
+
let(:ssl_policy_class) { Chef::HTTP::APISSLPolicy }
|
157
192
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
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(:
|
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(:
|
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)
|