chef 16.7.61 → 16.9.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -5
- data/README.md +2 -2
- data/chef.gemspec +12 -2
- 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)
|