puppet 6.3.0-x64-mingw32 → 6.4.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CODEOWNERS +30 -0
- data/Gemfile.lock +9 -9
- data/lib/puppet.rb +13 -0
- data/lib/puppet/application/agent.rb +8 -12
- data/lib/puppet/application/device.rb +2 -3
- data/lib/puppet/application/filebucket.rb +6 -1
- data/lib/puppet/application/ssl.rb +102 -55
- data/lib/puppet/configurer.rb +8 -7
- data/lib/puppet/defaults.rb +3 -1
- data/lib/puppet/file_system.rb +24 -4
- data/lib/puppet/file_system/file_impl.rb +25 -0
- data/lib/puppet/file_system/jruby.rb +23 -0
- data/lib/puppet/file_system/windows.rb +84 -0
- data/lib/puppet/indirector/rest.rb +4 -2
- data/lib/puppet/loaders.rb +1 -0
- data/lib/puppet/network/http.rb +1 -0
- data/lib/puppet/network/http/base_pool.rb +18 -0
- data/lib/puppet/network/http/connection.rb +49 -17
- data/lib/puppet/network/http/nocache_pool.rb +9 -4
- data/lib/puppet/network/http/pool.rb +10 -11
- data/lib/puppet/network/http/session.rb +3 -2
- data/lib/puppet/network/http_pool.rb +32 -0
- data/lib/puppet/pops/loader/generic_plan_instantiator.rb +28 -0
- data/lib/puppet/pops/loader/loader_paths.rb +46 -10
- data/lib/puppet/pops/loader/module_loaders.rb +10 -3
- data/lib/puppet/provider/file/windows.rb +49 -1
- data/lib/puppet/provider/package/windows.rb +5 -1
- data/lib/puppet/reports/http.rb +2 -1
- data/lib/puppet/rest/client.rb +7 -3
- data/lib/puppet/rest/routes.rb +9 -44
- data/lib/puppet/ssl.rb +6 -0
- data/lib/puppet/ssl/error.rb +26 -0
- data/lib/puppet/ssl/host.rb +9 -92
- data/lib/puppet/ssl/ssl_context.rb +30 -0
- data/lib/puppet/ssl/ssl_provider.rb +232 -0
- data/lib/puppet/ssl/state_machine.rb +261 -0
- data/lib/puppet/ssl/validator.rb +1 -0
- data/lib/puppet/ssl/validator/default_validator.rb +1 -0
- data/lib/puppet/ssl/validator/no_validator.rb +2 -0
- data/lib/puppet/ssl/verifier.rb +134 -0
- data/lib/puppet/ssl/verifier_adapter.rb +48 -0
- data/lib/puppet/test/test_helper.rb +2 -1
- data/lib/puppet/type/exec.rb +30 -6
- data/lib/puppet/type/file/mode.rb +6 -1
- data/lib/puppet/type/file/source.rb +2 -2
- data/lib/puppet/type/filebucket.rb +12 -8
- data/lib/puppet/type/user.rb +14 -1
- data/lib/puppet/util/connection.rb +10 -5
- data/lib/puppet/util/feature.rb +11 -2
- data/lib/puppet/util/http_proxy.rb +3 -2
- data/lib/puppet/util/pidlock.rb +1 -1
- data/lib/puppet/util/ssl.rb +1 -10
- data/lib/puppet/util/windows/security.rb +29 -8
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet/x509.rb +7 -0
- data/lib/puppet/x509/cert_provider.rb +286 -0
- data/lib/puppet/x509/pem_store.rb +55 -0
- data/locales/ja/puppet.po +740 -590
- data/locales/puppet.pot +433 -208
- data/man/man5/puppet.conf.5 +6 -3
- data/man/man8/puppet-agent.8 +1 -1
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +6 -2
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-man.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-ssl.8 +5 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/ssl/127.0.0.1-key.pem +67 -0
- data/spec/fixtures/ssl/127.0.0.1.pem +48 -0
- data/spec/fixtures/ssl/bad-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/bad-int-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/ca.pem +59 -0
- data/spec/fixtures/ssl/crl.pem +30 -0
- data/spec/fixtures/ssl/encrypted-key.pem +70 -0
- data/spec/fixtures/ssl/intermediate-agent-crl.pem +31 -0
- data/spec/fixtures/ssl/intermediate-agent.pem +60 -0
- data/spec/fixtures/ssl/intermediate-crl.pem +36 -0
- data/spec/fixtures/ssl/intermediate.pem +60 -0
- data/spec/fixtures/ssl/netlock-arany-utf8.pem +23 -0
- data/spec/fixtures/ssl/pluto-key.pem +67 -0
- data/spec/fixtures/ssl/pluto.pem +44 -0
- data/spec/fixtures/ssl/request-key.pem +67 -0
- data/spec/fixtures/ssl/request.pem +39 -0
- data/spec/fixtures/ssl/revoked-key.pem +67 -0
- data/spec/fixtures/ssl/revoked.pem +44 -0
- data/spec/fixtures/ssl/signed-key.pem +67 -0
- data/spec/fixtures/ssl/signed.pem +44 -0
- data/spec/fixtures/ssl/tampered-cert.pem +44 -0
- data/spec/fixtures/ssl/tampered-csr.pem +39 -0
- data/spec/integration/network/http_pool_spec.rb +222 -0
- data/spec/integration/provider/file/windows_spec.rb +162 -0
- data/spec/integration/rest/client_spec.rb +73 -0
- data/spec/integration/type/file_spec.rb +0 -19
- data/spec/lib/puppet/test_ca.rb +87 -50
- data/spec/lib/puppet_spec/fixtures.rb +20 -0
- data/spec/lib/puppet_spec/https.rb +84 -0
- data/spec/unit/application/agent_spec.rb +29 -30
- data/spec/unit/application/device_spec.rb +12 -49
- data/spec/unit/application/ssl_spec.rb +24 -38
- data/spec/unit/configurer_spec.rb +11 -11
- data/spec/unit/file_system/uniquefile_spec.rb +6 -0
- data/spec/unit/file_system_spec.rb +214 -0
- data/spec/unit/indirector/rest_spec.rb +3 -3
- data/spec/unit/network/http/connection_spec.rb +30 -90
- data/spec/unit/network/http/factory_spec.rb +1 -0
- data/spec/unit/network/http/nocache_pool_spec.rb +8 -8
- data/spec/unit/network/http/pool_spec.rb +63 -33
- data/spec/unit/network/http/session_spec.rb +8 -1
- data/spec/unit/network/http_pool_spec.rb +36 -0
- data/spec/unit/pops/loaders/loader_spec.rb +26 -1
- data/spec/unit/provider/package/windows_spec.rb +12 -1
- data/spec/unit/reports/http_spec.rb +7 -7
- data/spec/unit/rest/client_spec.rb +4 -6
- data/spec/unit/ssl/host_spec.rb +39 -33
- data/spec/unit/ssl/ssl_provider_spec.rb +428 -0
- data/spec/unit/ssl/state_machine_spec.rb +502 -0
- data/spec/unit/ssl/verifier_spec.rb +123 -0
- data/spec/unit/type/exec_spec.rb +63 -0
- data/spec/unit/type/file/source_spec.rb +5 -5
- data/spec/unit/type/filebucket_spec.rb +8 -6
- data/spec/unit/util/feature_spec.rb +2 -2
- data/spec/unit/util/storage_spec.rb +19 -19
- data/spec/unit/x509/cert_provider_spec.rb +527 -0
- data/spec/unit/x509/pem_store_spec.rb +160 -0
- data/tasks/generate_cert_fixtures.rake +158 -0
- metadata +78 -4
- data/MAINTAINERS +0 -47
- data/lib/puppet/rest/ssl_context.rb +0 -13
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Puppet::SSL::Verifier do
|
4
|
+
let(:options) { {} }
|
5
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(options) }
|
6
|
+
let(:host) { 'example.com' }
|
7
|
+
let(:http) { Net::HTTP.new(host) }
|
8
|
+
let(:verifier) { described_class.new(host, ssl_context) }
|
9
|
+
let(:adapter) { Puppet::SSL::VerifierAdapter.new(Puppet::SSL::Validator::DefaultValidator.new) }
|
10
|
+
|
11
|
+
context '#reusable?' do
|
12
|
+
it 'Verifiers with the same ssl_context are reusable' do
|
13
|
+
expect(verifier).to be_reusable(described_class.new(host, ssl_context))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'Verifiers with different ssl_contexts are not reusable' do
|
17
|
+
expect(verifier).to_not be_reusable(described_class.new(host, Puppet::SSL::SSLContext.new))
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'Verifier is not reusable with VerifierAdapter' do
|
21
|
+
expect(verifier).to_not be_reusable(adapter)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'VerifierAdapter is not reusable with Verifier' do
|
25
|
+
expect(adapter).to_not be_reusable(verifier)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'VerifierAdapters with the same class of Validator are reusable' do
|
29
|
+
expect(
|
30
|
+
adapter
|
31
|
+
).to be_reusable(Puppet::SSL::VerifierAdapter.new(Puppet::SSL::Validator::DefaultValidator.new))
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'VerifierAdapters with different classes of Validators are not reusable' do
|
35
|
+
expect(
|
36
|
+
adapter
|
37
|
+
).to_not be_reusable(Puppet::SSL::VerifierAdapter.new(Puppet::SSL::Validator::NoValidator.new))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#setup_connection' do
|
42
|
+
it 'copies parameters from the ssl_context to the connection' do
|
43
|
+
store = stub('store')
|
44
|
+
options.merge!(store: store)
|
45
|
+
verifier.setup_connection(http)
|
46
|
+
|
47
|
+
expect(http.cert_store).to eq(store)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'defaults to VERIFY_PEER' do
|
51
|
+
http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
52
|
+
|
53
|
+
verifier.setup_connection(http)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'only uses VERIFY_NONE if explicitly disabled' do
|
57
|
+
options.merge!(verify_peer: false)
|
58
|
+
|
59
|
+
http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
60
|
+
|
61
|
+
verifier.setup_connection(http)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'registers a verify callback' do
|
65
|
+
verifier.setup_connection(http)
|
66
|
+
|
67
|
+
expect(http.verify_callback).to eq(verifier)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context '#handle_connection_error' do
|
72
|
+
let(:peer_cert) { cert_fixture('127.0.0.1.pem') }
|
73
|
+
let(:chain) { [peer_cert] }
|
74
|
+
let(:ssl_error) { OpenSSL::SSL::SSLError.new("certificate verify failed") }
|
75
|
+
|
76
|
+
it "raises a verification error for a CA cert" do
|
77
|
+
store_context = stub('store_context', current_cert: peer_cert, chain: [peer_cert], error: OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, error_string: "unable to get local issuer certificate")
|
78
|
+
verifier.call(false, store_context)
|
79
|
+
|
80
|
+
expect {
|
81
|
+
verifier.handle_connection_error(http, ssl_error)
|
82
|
+
}.to raise_error(Puppet::SSL::CertVerifyError, "certificate verify failed [unable to get local issuer certificate for /CN=127.0.0.1]")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "raises a verification error for the server cert" do
|
86
|
+
store_context = stub('store_context', current_cert: peer_cert, chain: chain, error: OpenSSL::X509::V_ERR_CERT_REJECTED, error_string: "certificate rejected")
|
87
|
+
verifier.call(false, store_context)
|
88
|
+
|
89
|
+
expect {
|
90
|
+
verifier.handle_connection_error(http, ssl_error)
|
91
|
+
}.to raise_error(Puppet::SSL::CertVerifyError, "certificate verify failed [certificate rejected for /CN=127.0.0.1]")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "raises cert mismatch error on ruby < 2.4" do
|
95
|
+
http.expects(:peer_cert).returns(peer_cert)
|
96
|
+
|
97
|
+
store_context = stub('store_context')
|
98
|
+
verifier.call(true, store_context)
|
99
|
+
|
100
|
+
ssl_error = OpenSSL::SSL::SSLError.new("hostname 'example'com' does not match the server certificate")
|
101
|
+
|
102
|
+
expect {
|
103
|
+
verifier.handle_connection_error(http, ssl_error)
|
104
|
+
}.to raise_error(Puppet::Error, "Server hostname 'example.com' did not match server certificate; expected one of 127.0.0.1, DNS:127.0.0.1, DNS:127.0.0.2")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises cert mismatch error on ruby >= 2.4" do
|
108
|
+
store_context = stub('store_context', current_cert: peer_cert, chain: chain, error: OpenSSL::X509::V_OK, error_string: "ok")
|
109
|
+
verifier.call(false, store_context)
|
110
|
+
|
111
|
+
expect {
|
112
|
+
verifier.handle_connection_error(http, ssl_error)
|
113
|
+
}.to raise_error(Puppet::Error, "Server hostname 'example.com' did not match server certificate; expected one of 127.0.0.1, DNS:127.0.0.1, DNS:127.0.0.2")
|
114
|
+
end
|
115
|
+
|
116
|
+
it 're-raises other ssl connection errors' do
|
117
|
+
err = OpenSSL::SSL::SSLError.new("This version of OpenSSL does not support FIPS mode")
|
118
|
+
expect {
|
119
|
+
verifier.handle_connection_error(http, err)
|
120
|
+
}.to raise_error(err)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/spec/unit/type/exec_spec.rb
CHANGED
@@ -31,6 +31,33 @@ describe Puppet::Type.type(:exec) do
|
|
31
31
|
return exec
|
32
32
|
end
|
33
33
|
|
34
|
+
def exec_stub(options = {})
|
35
|
+
command = options.delete(:command) || @command
|
36
|
+
#unless_val = options.delete(:unless) || :true
|
37
|
+
type_args = {
|
38
|
+
:name => command,
|
39
|
+
#:unless => unless_val,
|
40
|
+
}.merge(options)
|
41
|
+
|
42
|
+
# Chicken, meet egg:
|
43
|
+
# Provider methods have to be stubbed before resource init or checks fail
|
44
|
+
# We have to set 'unless' in resource init or it can not be marked sensitive correctly.
|
45
|
+
# So: we create a dummy ahead of time and use 'any_instance' to stub out provider methods.
|
46
|
+
dummy = Puppet::Type.type(:exec).new(:name => @command)
|
47
|
+
dummy.provider.class.any_instance.stubs(:validatecmd)
|
48
|
+
dummy.provider.class.any_instance.stubs(:checkexe).returns(true)
|
49
|
+
pass_status = stub('status', :exitstatus => 0, :split => ["pass output"])
|
50
|
+
fail_status = stub('status', :exitstatus => 1, :split => ["fail output"])
|
51
|
+
Puppet::Util::Execution.stubs(:execute).with(:true, anything).returns(pass_status)
|
52
|
+
Puppet::Util::Execution.stubs(:execute).with(:false, anything).returns(fail_status)
|
53
|
+
|
54
|
+
test = Puppet::Type.type(:exec).new(type_args)
|
55
|
+
|
56
|
+
Puppet::Util::Log.level = :debug
|
57
|
+
|
58
|
+
return test
|
59
|
+
end
|
60
|
+
|
34
61
|
before do
|
35
62
|
@command = make_absolute('/bin/true whatever')
|
36
63
|
@executable = make_absolute('/bin/true')
|
@@ -176,6 +203,26 @@ describe Puppet::Type.type(:exec) do
|
|
176
203
|
expect(@logs).to eq([])
|
177
204
|
end
|
178
205
|
|
206
|
+
describe "when checks stop execution when debugging" do
|
207
|
+
[[:unless, :true], [:onlyif, :false]].each do |check, result|
|
208
|
+
it "should log a message with the command when #{check} is #{result}" do
|
209
|
+
output = "'#{@command}' won't be executed because of failed check '#{check}'"
|
210
|
+
test = exec_stub({:command => @command, check => result})
|
211
|
+
expect(test.check_all_attributes).to eq(false)
|
212
|
+
expect(@logs).to include(an_object_having_attributes(level: :debug, message: output))
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should log a message with a redacted command and check if #{check} is sensitive" do
|
216
|
+
output1 = "Executing check '[redacted]'"
|
217
|
+
output2 = "'[command redacted]' won't be executed because of failed check '#{check}'"
|
218
|
+
test = exec_stub({:command => @command, check => result, :sensitive_parameters => [check]})
|
219
|
+
expect(test.check_all_attributes).to eq(false)
|
220
|
+
expect(@logs).to include(an_object_having_attributes(level: :debug, message: output1))
|
221
|
+
expect(@logs).to include(an_object_having_attributes(level: :debug, message: output2))
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
179
226
|
describe " when multiple tries are set," do
|
180
227
|
it "should repeat the command attempt 'tries' times on failure and produce an error" do
|
181
228
|
tries = 5
|
@@ -635,6 +682,22 @@ describe Puppet::Type.type(:exec) do
|
|
635
682
|
@test[:creates] = [@exist] * 3
|
636
683
|
end
|
637
684
|
end
|
685
|
+
|
686
|
+
context "when creates is being checked" do
|
687
|
+
it "should be logged to debug when the path does exist" do
|
688
|
+
Puppet::Util::Log.level = :debug
|
689
|
+
@test[:creates] = @exist
|
690
|
+
expect(@test.check_all_attributes).to eq(false)
|
691
|
+
expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Checking that 'creates' path '#{@exist}' exists"))
|
692
|
+
end
|
693
|
+
|
694
|
+
it "should be logged to debug when the path does not exist" do
|
695
|
+
Puppet::Util::Log.level = :debug
|
696
|
+
@test[:creates] = @unexist
|
697
|
+
expect(@test.check_all_attributes).to eq(true)
|
698
|
+
expect(@logs).to include(an_object_having_attributes(level: :debug, message: "Checking that 'creates' path '#{@unexist}' exists"))
|
699
|
+
end
|
700
|
+
end
|
638
701
|
end
|
639
702
|
|
640
703
|
{ :onlyif => { :pass => false, :fail => true },
|
@@ -594,7 +594,7 @@ describe Puppet::Type.type(:file).attrclass(:source), :uses_checksums => true do
|
|
594
594
|
it 'should use an explicit fileserver if source starts with puppet://' do
|
595
595
|
response.stubs(:code).returns('200')
|
596
596
|
source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet://somehostname/test/foo', :ftype => 'file')
|
597
|
-
Puppet::Network::HttpPool.expects(:
|
597
|
+
Puppet::Network::HttpPool.expects(:connection).with('somehostname', 8140, anything).returns(conn)
|
598
598
|
|
599
599
|
resource.write(source)
|
600
600
|
end
|
@@ -602,14 +602,14 @@ describe Puppet::Type.type(:file).attrclass(:source), :uses_checksums => true do
|
|
602
602
|
it 'should use the default fileserver if source starts with puppet:///' do
|
603
603
|
response.stubs(:code).returns('200')
|
604
604
|
source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo', :ftype => 'file')
|
605
|
-
Puppet::Network::HttpPool.expects(:
|
605
|
+
Puppet::Network::HttpPool.expects(:connection).with(Puppet[:server], 8140, anything).returns(conn)
|
606
606
|
|
607
607
|
resource.write(source)
|
608
608
|
end
|
609
609
|
|
610
610
|
it 'should percent encode reserved characters' do
|
611
611
|
response.stubs(:code).returns('200')
|
612
|
-
Puppet::Network::HttpPool.stubs(:
|
612
|
+
Puppet::Network::HttpPool.stubs(:connection).returns(conn)
|
613
613
|
source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo bar', :ftype => 'file')
|
614
614
|
|
615
615
|
conn.unstub(:request_get)
|
@@ -620,7 +620,7 @@ describe Puppet::Type.type(:file).attrclass(:source), :uses_checksums => true do
|
|
620
620
|
|
621
621
|
it 'should request binary content' do
|
622
622
|
response.stubs(:code).returns('200')
|
623
|
-
Puppet::Network::HttpPool.stubs(:
|
623
|
+
Puppet::Network::HttpPool.stubs(:connection).returns(conn)
|
624
624
|
source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo bar', :ftype => 'file')
|
625
625
|
|
626
626
|
conn.unstub(:request_get)
|
@@ -637,7 +637,7 @@ describe Puppet::Type.type(:file).attrclass(:source), :uses_checksums => true do
|
|
637
637
|
end
|
638
638
|
|
639
639
|
before(:each) do
|
640
|
-
Puppet::Network::HttpPool.stubs(:
|
640
|
+
Puppet::Network::HttpPool.stubs(:connection).returns(conn)
|
641
641
|
source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo', :ftype => 'file')
|
642
642
|
end
|
643
643
|
|
@@ -21,14 +21,14 @@ describe Puppet::Type.type(:filebucket) do
|
|
21
21
|
expect(Puppet::Type.type(:filebucket).new(:name => "main")[:path]).to eq(Puppet[:clientbucketdir])
|
22
22
|
end
|
23
23
|
|
24
|
-
it "should
|
24
|
+
it "should not have a default port" do
|
25
25
|
Puppet.settings[:masterport] = 50
|
26
|
-
expect(Puppet::Type.type(:filebucket).new(:name => "main")[:port]).to eq(
|
26
|
+
expect(Puppet::Type.type(:filebucket).new(:name => "main")[:port]).to eq(nil)
|
27
27
|
end
|
28
28
|
|
29
|
-
it "should
|
29
|
+
it "should not have a default server" do
|
30
30
|
Puppet.settings[:server] = "myserver"
|
31
|
-
expect(Puppet::Type.type(:filebucket).new(:name => "main")[:server]).to eq(
|
31
|
+
expect(Puppet::Type.type(:filebucket).new(:name => "main")[:server]).to eq(nil)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "be local by default" do
|
@@ -93,10 +93,12 @@ describe Puppet::Type.type(:filebucket) do
|
|
93
93
|
bucket.bucket
|
94
94
|
end
|
95
95
|
|
96
|
-
it "should
|
96
|
+
it "should not try to guess server or port if the path is unset and no server is provided" do
|
97
97
|
Puppet.settings[:server] = "myserv"
|
98
|
+
Puppet.settings[:server_list] = ['server_list_0', 'server_list_1']
|
99
|
+
Puppet::FileBucket::Dipper.expects(:new).with(:Server => nil, :Port => nil).returns @bucket
|
100
|
+
|
98
101
|
bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false
|
99
|
-
Puppet::FileBucket::Dipper.expects(:new).with { |args| args[:Server] == "myserv" }.returns @bucket
|
100
102
|
bucket.bucket
|
101
103
|
end
|
102
104
|
end
|
@@ -88,7 +88,7 @@ describe Puppet::Util::Feature do
|
|
88
88
|
@features.expects(:require).with("foo").raises(LoadError)
|
89
89
|
@features.stubs(:require).with("bar")
|
90
90
|
|
91
|
-
|
91
|
+
@features.expects(:debug_once)
|
92
92
|
|
93
93
|
expect(@features).not_to be_myfeature
|
94
94
|
end
|
@@ -100,7 +100,7 @@ describe Puppet::Util::Feature do
|
|
100
100
|
Puppet::Util::RubyGems::Source.stubs(:source).returns(Puppet::Util::RubyGems::Gems18Source)
|
101
101
|
Puppet::Util::RubyGems::Gems18Source.any_instance.expects(:clear_paths).times(3)
|
102
102
|
|
103
|
-
|
103
|
+
@features.expects(:debug_once)
|
104
104
|
|
105
105
|
expect(@features).not_to be_myfeature
|
106
106
|
expect(@features).to be_myfeature
|
@@ -181,6 +181,8 @@ describe Puppet::Util::Storage do
|
|
181
181
|
end
|
182
182
|
|
183
183
|
describe "when storing to the state file" do
|
184
|
+
A_SMALL_AMOUNT_OF_TIME = 0.001 #Seconds
|
185
|
+
|
184
186
|
before(:each) do
|
185
187
|
@state_file = tmpfile('storage_test')
|
186
188
|
@saved_statefile = Puppet[:statefile]
|
@@ -232,13 +234,13 @@ describe Puppet::Util::Storage do
|
|
232
234
|
stale_checked = recent_checked - (Puppet[:statettl] + 10)
|
233
235
|
Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked
|
234
236
|
Puppet::Util::Storage.cache(:stale)[:checked] = stale_checked
|
235
|
-
expect(Puppet::Util::Storage.state).to
|
237
|
+
expect(Puppet::Util::Storage.state).to match(
|
236
238
|
{
|
237
239
|
:yayness => {
|
238
|
-
:checked => recent_checked
|
240
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
239
241
|
},
|
240
242
|
:stale => {
|
241
|
-
:checked => stale_checked
|
243
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(stale_checked)
|
242
244
|
}
|
243
245
|
}
|
244
246
|
)
|
@@ -250,29 +252,28 @@ describe Puppet::Util::Storage do
|
|
250
252
|
|
251
253
|
Puppet::Util::Storage.load
|
252
254
|
|
253
|
-
expect(Puppet::Util::Storage.state).to
|
255
|
+
expect(Puppet::Util::Storage.state).to match(
|
254
256
|
{
|
255
257
|
:yayness => {
|
256
|
-
:checked => recent_checked
|
258
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
257
259
|
}
|
258
260
|
}
|
259
261
|
)
|
260
262
|
end
|
261
263
|
|
262
|
-
|
263
264
|
it "does not expire entries when statettl is 0" do
|
264
265
|
Puppet[:statettl] = '0'
|
265
266
|
recent_checked = Time.now.round
|
266
267
|
older_checked = recent_checked - 10_000_000
|
267
268
|
Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked
|
268
269
|
Puppet::Util::Storage.cache(:older)[:checked] = older_checked
|
269
|
-
expect(Puppet::Util::Storage.state).to
|
270
|
+
expect(Puppet::Util::Storage.state).to match(
|
270
271
|
{
|
271
272
|
:yayness => {
|
272
|
-
:checked => recent_checked
|
273
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
273
274
|
},
|
274
275
|
:older => {
|
275
|
-
:checked => older_checked
|
276
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)
|
276
277
|
}
|
277
278
|
}
|
278
279
|
)
|
@@ -284,32 +285,31 @@ describe Puppet::Util::Storage do
|
|
284
285
|
|
285
286
|
Puppet::Util::Storage.load
|
286
287
|
|
287
|
-
expect(Puppet::Util::Storage.state).to
|
288
|
+
expect(Puppet::Util::Storage.state).to match(
|
288
289
|
{
|
289
290
|
:yayness => {
|
290
|
-
:checked => recent_checked
|
291
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
291
292
|
},
|
292
293
|
:older => {
|
293
|
-
:checked => older_checked
|
294
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)
|
294
295
|
}
|
295
296
|
}
|
296
297
|
)
|
297
298
|
end
|
298
299
|
|
299
|
-
|
300
300
|
it "does not expire entries when statettl is 'unlimited'" do
|
301
301
|
Puppet[:statettl] = 'unlimited'
|
302
302
|
recent_checked = Time.now
|
303
303
|
older_checked = Time.now - 10_000_000
|
304
304
|
Puppet::Util::Storage.cache(:yayness)[:checked] = recent_checked
|
305
305
|
Puppet::Util::Storage.cache(:older)[:checked] = older_checked
|
306
|
-
expect(Puppet::Util::Storage.state).to
|
306
|
+
expect(Puppet::Util::Storage.state).to match(
|
307
307
|
{
|
308
308
|
:yayness => {
|
309
|
-
:checked => recent_checked
|
309
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
310
310
|
},
|
311
311
|
:older => {
|
312
|
-
:checked => older_checked
|
312
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)
|
313
313
|
}
|
314
314
|
}
|
315
315
|
)
|
@@ -321,13 +321,13 @@ describe Puppet::Util::Storage do
|
|
321
321
|
|
322
322
|
Puppet::Util::Storage.load
|
323
323
|
|
324
|
-
expect(Puppet::Util::Storage.state).to
|
324
|
+
expect(Puppet::Util::Storage.state).to match(
|
325
325
|
{
|
326
326
|
:yayness => {
|
327
|
-
:checked => recent_checked
|
327
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(recent_checked)
|
328
328
|
},
|
329
329
|
:older => {
|
330
|
-
:checked => older_checked
|
330
|
+
:checked => a_value_within(A_SMALL_AMOUNT_OF_TIME).of(older_checked)
|
331
331
|
}
|
332
332
|
}
|
333
333
|
)
|
@@ -0,0 +1,527 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'puppet/x509'
|
3
|
+
|
4
|
+
describe Puppet::X509::CertProvider do
|
5
|
+
include PuppetSpec::Files
|
6
|
+
|
7
|
+
def create_provider(options)
|
8
|
+
described_class.new(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def as_pem_file(pem)
|
12
|
+
path = tmpfile('cert_provider_pem')
|
13
|
+
File.write(path, pem)
|
14
|
+
path
|
15
|
+
end
|
16
|
+
|
17
|
+
def expects_public_file(path)
|
18
|
+
if Puppet::Util::Platform.windows?
|
19
|
+
current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)
|
20
|
+
sd = Puppet::Util::Windows::Security.get_security_descriptor(path)
|
21
|
+
expect(sd.dacl).to contain_exactly(
|
22
|
+
an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),
|
23
|
+
an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),
|
24
|
+
an_object_having_attributes(sid: current_sid, mask: 0x1f01ff),
|
25
|
+
an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinUsers, mask: 0x120089)
|
26
|
+
)
|
27
|
+
else
|
28
|
+
expect(File.stat(path).mode & 07777).to eq(0644)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def expects_private_file(path)
|
33
|
+
if Puppet::Util::Platform.windows?
|
34
|
+
current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)
|
35
|
+
sd = Puppet::Util::Windows::Security.get_security_descriptor(path)
|
36
|
+
expect(sd.dacl).to contain_exactly(
|
37
|
+
an_object_having_attributes(sid: Puppet::Util::Windows::SID::LocalSystem, mask: 0x1f01ff),
|
38
|
+
an_object_having_attributes(sid: Puppet::Util::Windows::SID::BuiltinAdministrators, mask: 0x1f01ff),
|
39
|
+
an_object_having_attributes(sid: current_sid, mask: 0x1f01ff)
|
40
|
+
)
|
41
|
+
else
|
42
|
+
expect(File.stat(path).mode & 07777).to eq(0640)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:fixture_dir) { File.join(PuppetSpec::FIXTURE_DIR, 'ssl') }
|
47
|
+
|
48
|
+
context 'when loading' do
|
49
|
+
context 'cacerts' do
|
50
|
+
it 'returns nil if it does not exist' do
|
51
|
+
provider = create_provider(capath: '/does/not/exist')
|
52
|
+
|
53
|
+
expect(provider.load_cacerts).to be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'raises if cacerts are required' do
|
57
|
+
provider = create_provider(capath: '/does/not/exist')
|
58
|
+
|
59
|
+
expect {
|
60
|
+
provider.load_cacerts(required: true)
|
61
|
+
}.to raise_error(Puppet::Error, %r{The CA certificates are missing from '/does/not/exist'})
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns an array of certificates' do
|
65
|
+
subject = OpenSSL::X509::Name.new([['CN', 'Test CA']])
|
66
|
+
certs = create_provider(capath: File.join(fixture_dir, 'ca.pem')).load_cacerts
|
67
|
+
expect(certs).to contain_exactly(an_object_having_attributes(subject: subject))
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'and input is invalid' do
|
71
|
+
it 'raises when invalid input is inside BEGIN-END block' do
|
72
|
+
ca_path = as_pem_file(<<~END)
|
73
|
+
-----BEGIN CERTIFICATE-----
|
74
|
+
whoops
|
75
|
+
-----END CERTIFICATE-----
|
76
|
+
END
|
77
|
+
|
78
|
+
expect {
|
79
|
+
create_provider(capath: ca_path).load_cacerts
|
80
|
+
}.to raise_error(OpenSSL::X509::CertificateError)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'raises if the input is empty' do
|
84
|
+
expect {
|
85
|
+
create_provider(capath: as_pem_file('')).load_cacerts
|
86
|
+
}.to raise_error(OpenSSL::X509::CertificateError)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'raises if the input is malformed' do
|
90
|
+
ca_path = as_pem_file(<<~END)
|
91
|
+
-----BEGIN CERTIFICATE-----
|
92
|
+
MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0
|
93
|
+
END
|
94
|
+
|
95
|
+
expect {
|
96
|
+
create_provider(capath: ca_path).load_cacerts
|
97
|
+
}.to raise_error(OpenSSL::X509::CertificateError)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'raises if the cacerts are unreadable' do
|
102
|
+
capath = File.join(fixture_dir, 'ca.pem')
|
103
|
+
provider = create_provider(capath: capath)
|
104
|
+
provider.stubs(:load_pem).raises(Errno::EACCES, 'Permission denied')
|
105
|
+
|
106
|
+
expect {
|
107
|
+
provider.load_cacerts
|
108
|
+
}.to raise_error(Puppet::Error, "Failed to load CA certificates from '#{capath}'")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'crls' do
|
113
|
+
it 'returns nil if it does not exist' do
|
114
|
+
provider = create_provider(crlpath: '/does/not/exist')
|
115
|
+
expect(provider.load_crls).to be_nil
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'raises if CRLs are required' do
|
119
|
+
provider = create_provider(crlpath: '/does/not/exist')
|
120
|
+
|
121
|
+
expect {
|
122
|
+
provider.load_crls(required: true)
|
123
|
+
}.to raise_error(Puppet::Error, %r{The CRL is missing from '/does/not/exist'})
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'returns an array of CRLs' do
|
127
|
+
issuer = OpenSSL::X509::Name.new([['CN', 'Test CA']])
|
128
|
+
crls = create_provider(crlpath: File.join(fixture_dir, 'crl.pem')).load_crls
|
129
|
+
expect(crls).to contain_exactly(an_object_having_attributes(issuer: issuer))
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'and input is invalid' do
|
133
|
+
it 'raises when invalid input is inside BEGIN-END block' do
|
134
|
+
pending('jruby bug: https://github.com/jruby/jruby/issues/5619') if Puppet::Util::Platform.jruby?
|
135
|
+
|
136
|
+
crl_path = as_pem_file(<<~END)
|
137
|
+
-----BEGIN X509 CRL-----
|
138
|
+
whoops
|
139
|
+
-----END X509 CRL-----
|
140
|
+
END
|
141
|
+
|
142
|
+
expect {
|
143
|
+
create_provider(crlpath: crl_path).load_crls
|
144
|
+
}.to raise_error(OpenSSL::X509::CRLError, 'nested asn1 error')
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'raises if the input is empty' do
|
148
|
+
expect {
|
149
|
+
create_provider(crlpath: as_pem_file('')).load_crls
|
150
|
+
}.to raise_error(OpenSSL::X509::CRLError, 'Failed to parse CRLs as PEM')
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'raises if the input is malformed' do
|
154
|
+
crl_path = as_pem_file(<<~END)
|
155
|
+
-----BEGIN X509 CRL-----
|
156
|
+
MIIBCjB1AgEBMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EXDTcw
|
157
|
+
END
|
158
|
+
|
159
|
+
expect {
|
160
|
+
create_provider(crlpath: crl_path).load_crls
|
161
|
+
}.to raise_error(OpenSSL::X509::CRLError, 'Failed to parse CRLs as PEM')
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'raises if the CRLs are unreadable' do
|
166
|
+
crlpath = File.join(fixture_dir, 'crl.pem')
|
167
|
+
provider = create_provider(crlpath: crlpath)
|
168
|
+
provider.stubs(:load_pem).raises(Errno::EACCES, 'Permission denied')
|
169
|
+
|
170
|
+
expect {
|
171
|
+
provider.load_crls
|
172
|
+
}.to raise_error(Puppet::Error, "Failed to load CRLs from '#{crlpath}'")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'when saving' do
|
178
|
+
context 'cacerts' do
|
179
|
+
let(:ca_path) { tmpfile('pem_cacerts') }
|
180
|
+
let(:ca_cert) { cert_fixture('ca.pem') }
|
181
|
+
|
182
|
+
it 'writes PEM encoded certs' do
|
183
|
+
create_provider(capath: ca_path).save_cacerts([ca_cert])
|
184
|
+
|
185
|
+
expect(File.read(ca_path)).to match(/\A-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----\Z/m)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'sets mode to 644' do
|
189
|
+
create_provider(capath: ca_path).save_cacerts([ca_cert])
|
190
|
+
|
191
|
+
expects_public_file(ca_path)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'raises if the CA certs are unwritable' do
|
195
|
+
provider = create_provider(capath: ca_path)
|
196
|
+
provider.stubs(:save_pem).raises(Errno::EACCES, 'Permission denied')
|
197
|
+
|
198
|
+
expect {
|
199
|
+
provider.save_cacerts([ca_cert])
|
200
|
+
}.to raise_error(Puppet::Error, "Failed to save CA certificates to '#{ca_path}'")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'crls' do
|
205
|
+
let(:crl_path) { tmpfile('pem_crls') }
|
206
|
+
let(:ca_crl) { crl_fixture('crl.pem') }
|
207
|
+
|
208
|
+
it 'writes PEM encoded CRLs' do
|
209
|
+
create_provider(crlpath: crl_path).save_crls([ca_crl])
|
210
|
+
|
211
|
+
expect(File.read(crl_path)).to match(/\A-----BEGIN X509 CRL-----.*?-----END X509 CRL-----\Z/m)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'sets mode to 644' do
|
215
|
+
create_provider(crlpath: crl_path).save_crls([ca_crl])
|
216
|
+
|
217
|
+
expects_public_file(crl_path)
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'raises if the CRLs are unwritable' do
|
221
|
+
provider = create_provider(crlpath: crl_path)
|
222
|
+
provider.stubs(:save_pem).raises(Errno::EACCES, 'Permission denied')
|
223
|
+
|
224
|
+
expect {
|
225
|
+
provider.save_crls([ca_crl])
|
226
|
+
}.to raise_error(Puppet::Error, "Failed to save CRLs to '#{crl_path}'")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'when loading' do
|
232
|
+
context 'private keys' do
|
233
|
+
let(:provider) { create_provider(privatekeydir: fixture_dir) }
|
234
|
+
|
235
|
+
it 'returns nil if it does not exist' do
|
236
|
+
provider = create_provider(privatekeydir: '/does/not/exist')
|
237
|
+
|
238
|
+
expect(provider.load_private_key('whatever')).to be_nil
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'raises if it is required' do
|
242
|
+
provider = create_provider(privatekeydir: '/does/not/exist')
|
243
|
+
|
244
|
+
expect {
|
245
|
+
provider.load_private_key('whatever', required: true)
|
246
|
+
}.to raise_error(Puppet::Error, %r{The private key is missing from '/does/not/exist/whatever.pem'})
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'returns an RSA key' do
|
250
|
+
expect(provider.load_private_key('signed-key')).to be_a(OpenSSL::PKey::RSA)
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'downcases name' do
|
254
|
+
expect(provider.load_private_key('SIGNED-KEY')).to be_a(OpenSSL::PKey::RSA)
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'raises if name is invalid' do
|
258
|
+
expect {
|
259
|
+
provider.load_private_key('signed/../key')
|
260
|
+
}.to raise_error(RuntimeError, 'Certname "signed/../key" must not contain unprintable or non-ASCII characters')
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'returns nil if `hostprivkey` is overridden' do
|
264
|
+
Puppet[:certname] = 'foo'
|
265
|
+
Puppet[:hostprivkey] = File.join(fixture_dir, "signed-key.pem")
|
266
|
+
|
267
|
+
expect(provider.load_private_key('foo')).to be_nil
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'raises if the private key is unreadable' do
|
271
|
+
provider.stubs(:load_pem).raises(Errno::EACCES, 'Permission denied')
|
272
|
+
|
273
|
+
expect {
|
274
|
+
provider.load_private_key('signed')
|
275
|
+
}.to raise_error(Puppet::Error, "Failed to load private key for 'signed'")
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'that are encrypted' do
|
279
|
+
it 'raises without a passphrase' do
|
280
|
+
# password is 74695716c8b6
|
281
|
+
expect {
|
282
|
+
provider.load_private_key('encrypted-key')
|
283
|
+
}.to raise_error(OpenSSL::PKey::RSAError, /Neither PUB key nor PRIV key/)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context 'certs' do
|
289
|
+
let(:provider) { create_provider(certdir: fixture_dir) }
|
290
|
+
|
291
|
+
it 'returns nil if it does not exist' do
|
292
|
+
provider = create_provider(certdir: '/does/not/exist')
|
293
|
+
|
294
|
+
expect(provider.load_client_cert('nonexistent')).to be_nil
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'raises if it is required' do
|
298
|
+
provider = create_provider(certdir: '/does/not/exist')
|
299
|
+
|
300
|
+
expect {
|
301
|
+
provider.load_client_cert('nonexistent', required: true)
|
302
|
+
}.to raise_error(Puppet::Error, %r{The client certificate is missing from '/does/not/exist/nonexistent.pem'})
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'returns a certificate' do
|
306
|
+
cert = provider.load_client_cert('signed')
|
307
|
+
expect(cert.subject.to_s).to eq('/CN=signed')
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'downcases name' do
|
311
|
+
cert = provider.load_client_cert('SIGNED')
|
312
|
+
expect(cert.subject.to_s).to eq('/CN=signed')
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'raises if name is invalid' do
|
316
|
+
expect {
|
317
|
+
provider.load_client_cert('tom/../key')
|
318
|
+
}.to raise_error(RuntimeError, 'Certname "tom/../key" must not contain unprintable or non-ASCII characters')
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'returns nil if `hostcert` is overridden' do
|
322
|
+
Puppet[:certname] = 'foo'
|
323
|
+
Puppet[:hostcert] = File.join(fixture_dir, "signed.pem")
|
324
|
+
|
325
|
+
expect(provider.load_client_cert('foo')).to be_nil
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'raises if the certificate is unreadable' do
|
329
|
+
provider.stubs(:load_pem).raises(Errno::EACCES, 'Permission denied')
|
330
|
+
|
331
|
+
expect {
|
332
|
+
provider.load_client_cert('signed')
|
333
|
+
}.to raise_error(Puppet::Error, "Failed to load client certificate for 'signed'")
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'requests' do
|
338
|
+
let(:request) { request_fixture('request.pem') }
|
339
|
+
let(:provider) { create_provider(requestdir: fixture_dir) }
|
340
|
+
|
341
|
+
it 'returns nil if it does not exist' do
|
342
|
+
expect(provider.load_request('whatever')).to be_nil
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'returns a request' do
|
346
|
+
expect(provider.load_request('request')).to be_a(OpenSSL::X509::Request)
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'downcases name' do
|
350
|
+
csr = provider.load_request('REQUEST')
|
351
|
+
expect(csr.subject.to_s).to eq('/CN=pending')
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'raises if name is invalid' do
|
355
|
+
expect {
|
356
|
+
provider.load_request('tom/../key')
|
357
|
+
}.to raise_error(RuntimeError, 'Certname "tom/../key" must not contain unprintable or non-ASCII characters')
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'ignores `hostcsr`' do
|
361
|
+
Puppet[:hostcsr] = File.join(fixture_dir, "doesnotexist.pem")
|
362
|
+
|
363
|
+
expect(provider.load_request('request')).to be_a(OpenSSL::X509::Request)
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'raises if the certificate is unreadable' do
|
367
|
+
provider.stubs(:load_pem).raises(Errno::EACCES, 'Permission denied')
|
368
|
+
|
369
|
+
expect {
|
370
|
+
provider.load_request('pending')
|
371
|
+
}.to raise_error(Puppet::Error, "Failed to load certificate request for 'pending'")
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
context 'when saving' do
|
377
|
+
let(:name) { 'tom' }
|
378
|
+
|
379
|
+
context 'private keys' do
|
380
|
+
let(:privatekeydir) { tmpdir('privatekeydir') }
|
381
|
+
let(:private_key) { key_fixture('signed-key.pem') }
|
382
|
+
let(:path) { File.join(privatekeydir, 'tom.pem') }
|
383
|
+
let(:provider) { create_provider(privatekeydir: privatekeydir) }
|
384
|
+
|
385
|
+
it 'writes PEM encoded private key' do
|
386
|
+
provider.save_private_key(name, private_key)
|
387
|
+
|
388
|
+
expect(File.read(path)).to match(/\A-----BEGIN RSA PRIVATE KEY-----.*?-----END RSA PRIVATE KEY-----\Z/m)
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'sets mode to 640' do
|
392
|
+
provider.save_private_key(name, private_key)
|
393
|
+
|
394
|
+
expects_private_file(path)
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'downcases name' do
|
398
|
+
provider.save_private_key('TOM', private_key)
|
399
|
+
|
400
|
+
expect(File).to be_exist(path)
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'raises if name is invalid' do
|
404
|
+
expect {
|
405
|
+
provider.save_private_key('tom/../key', private_key)
|
406
|
+
}.to raise_error(RuntimeError, 'Certname "tom/../key" must not contain unprintable or non-ASCII characters')
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'raises if the private key is unwritable' do
|
410
|
+
provider.stubs(:save_pem).raises(Errno::EACCES, 'Permission denied')
|
411
|
+
|
412
|
+
expect {
|
413
|
+
provider.save_private_key(name, private_key)
|
414
|
+
}.to raise_error(Puppet::Error, "Failed to save private key for '#{name}'")
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
context 'certs' do
|
419
|
+
let(:certdir) { tmpdir('certdir') }
|
420
|
+
let(:client_cert) { cert_fixture('signed.pem') }
|
421
|
+
let(:path) { File.join(certdir, 'tom.pem') }
|
422
|
+
let(:provider) { create_provider(certdir: certdir) }
|
423
|
+
|
424
|
+
it 'writes PEM encoded cert' do
|
425
|
+
provider.save_client_cert(name, client_cert)
|
426
|
+
|
427
|
+
expect(File.read(path)).to match(/\A-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----\Z/m)
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'sets mode to 644' do
|
431
|
+
provider.save_client_cert(name, client_cert)
|
432
|
+
|
433
|
+
expects_public_file(path)
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'downcases name' do
|
437
|
+
provider.save_client_cert('TOM', client_cert)
|
438
|
+
|
439
|
+
expect(File).to be_exist(path)
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'raises if name is invalid' do
|
443
|
+
expect {
|
444
|
+
provider.save_client_cert('tom/../key', client_cert)
|
445
|
+
}.to raise_error(RuntimeError, 'Certname "tom/../key" must not contain unprintable or non-ASCII characters')
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'raises if the cert is unwritable' do
|
449
|
+
provider.stubs(:save_pem).raises(Errno::EACCES, 'Permission denied')
|
450
|
+
|
451
|
+
expect {
|
452
|
+
provider.save_client_cert(name, client_cert)
|
453
|
+
}.to raise_error(Puppet::Error, "Failed to save client certificate for '#{name}'")
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context 'requests' do
|
458
|
+
let(:requestdir) { tmpdir('requestdir') }
|
459
|
+
let(:csr) { request_fixture('request.pem') }
|
460
|
+
let(:path) { File.join(requestdir, 'tom.pem') }
|
461
|
+
let(:provider) { create_provider(requestdir: requestdir) }
|
462
|
+
|
463
|
+
it 'writes PEM encoded request' do
|
464
|
+
provider.save_request(name, csr)
|
465
|
+
|
466
|
+
expect(File.read(path)).to match(/\A-----BEGIN CERTIFICATE REQUEST-----.*?-----END CERTIFICATE REQUEST-----\Z/m)
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'sets mode to 644' do
|
470
|
+
provider.save_request(name, csr)
|
471
|
+
|
472
|
+
expects_public_file(path)
|
473
|
+
end
|
474
|
+
|
475
|
+
it 'downcases name' do
|
476
|
+
provider.save_request('TOM', csr)
|
477
|
+
|
478
|
+
expect(File).to be_exist(path)
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'raises if name is invalid' do
|
482
|
+
expect {
|
483
|
+
provider.save_request('tom/../key', csr)
|
484
|
+
}.to raise_error(RuntimeError, 'Certname "tom/../key" must not contain unprintable or non-ASCII characters')
|
485
|
+
end
|
486
|
+
|
487
|
+
it 'raises if the request is unwritable' do
|
488
|
+
provider.stubs(:save_pem).raises(Errno::EACCES, 'Permission denied')
|
489
|
+
|
490
|
+
expect {
|
491
|
+
provider.save_request(name, csr)
|
492
|
+
}.to raise_error(Puppet::Error, "Failed to save certificate request for '#{name}'")
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context 'when deleting' do
|
498
|
+
context 'requests' do
|
499
|
+
let(:name) { 'jerry' }
|
500
|
+
let(:requestdir) { tmpdir('cert_provider') }
|
501
|
+
let(:provider) { create_provider(requestdir: requestdir) }
|
502
|
+
|
503
|
+
it 'returns true if request was deleted' do
|
504
|
+
path = File.join(requestdir, "#{name}.pem")
|
505
|
+
File.write(path, "PEM")
|
506
|
+
|
507
|
+
expect(provider.delete_request(name)).to eq(true)
|
508
|
+
expect(File).not_to be_exist(path)
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'returns false if the request is non-existent' do
|
512
|
+
path = File.join(requestdir, "#{name}.pem")
|
513
|
+
|
514
|
+
expect(provider.delete_request(name)).to eq(false)
|
515
|
+
expect(File).to_not be_exist(path)
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'raises if the file is undeletable' do
|
519
|
+
provider.stubs(:delete_pem).raises(Errno::EACCES, 'Permission denied')
|
520
|
+
|
521
|
+
expect {
|
522
|
+
provider.delete_request(name)
|
523
|
+
}.to raise_error(Puppet::Error, "Failed to delete certificate request for '#{name}'")
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|