puppet 6.3.0-universal-darwin → 6.4.0-universal-darwin
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
@@ -5,11 +5,12 @@ require 'puppet/network/http'
|
|
5
5
|
|
6
6
|
describe Puppet::Network::HTTP::Session do
|
7
7
|
let(:connection) { stub('connection') }
|
8
|
+
let(:verifier) { stub('verifier') }
|
8
9
|
|
9
10
|
def create_session(connection, expiration_time = nil)
|
10
11
|
expiration_time ||= Time.now + 60 * 60
|
11
12
|
|
12
|
-
Puppet::Network::HTTP::Session.new(connection, expiration_time)
|
13
|
+
Puppet::Network::HTTP::Session.new(connection, verifier, expiration_time)
|
13
14
|
end
|
14
15
|
|
15
16
|
it 'provides access to its connection' do
|
@@ -18,6 +19,12 @@ describe Puppet::Network::HTTP::Session do
|
|
18
19
|
expect(session.connection).to eq(connection)
|
19
20
|
end
|
20
21
|
|
22
|
+
it 'provides access to its verifier' do
|
23
|
+
session = create_session(connection)
|
24
|
+
|
25
|
+
expect(session.verifier).to eq(verifier)
|
26
|
+
end
|
27
|
+
|
21
28
|
it 'expires a connection whose expiration time is in the past' do
|
22
29
|
now = Time.now
|
23
30
|
past = now - 1
|
@@ -46,6 +46,42 @@ describe Puppet::Network::HttpPool do
|
|
46
46
|
expect(Puppet::Network::HttpPool.http_instance("me", 54321, true)).to be_use_ssl
|
47
47
|
end
|
48
48
|
|
49
|
+
it 'has an http_ssl_instance method' do
|
50
|
+
expect(Puppet::Network::HttpPool.http_ssl_instance("me", 54321)).to be_use_ssl
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when calling 'connection'" do
|
54
|
+
it 'requires an ssl_context' do
|
55
|
+
expect {
|
56
|
+
Puppet::Network::HttpPool.connection('me', 8140)
|
57
|
+
}.to raise_error(ArgumentError, "An ssl_context is required when connecting to 'https://me:8140'")
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'creates a verifier from the context' do
|
61
|
+
ssl_context = Puppet::SSL::SSLContext.new
|
62
|
+
expect(
|
63
|
+
Puppet::Network::HttpPool.connection('me', 8140, ssl_context: ssl_context).verifier
|
64
|
+
).to be_a_kind_of(Puppet::SSL::Verifier)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not use SSL when specified' do
|
68
|
+
expect(Puppet::Network::HttpPool.connection('me', 8140, use_ssl: false)).to_not be_use_ssl
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'defaults to SSL' do
|
72
|
+
ssl_context = Puppet::SSL::SSLContext.new
|
73
|
+
conn = Puppet::Network::HttpPool.connection('me', 8140, ssl_context: ssl_context)
|
74
|
+
expect(conn).to be_use_ssl
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'warns if an ssl_context is used for an http connection' do
|
78
|
+
Puppet.expects(:warning).with("An ssl_context is unnecessary when connecting to 'http://me:8140' and will be ignored")
|
79
|
+
|
80
|
+
ssl_context = Puppet::SSL::SSLContext.new
|
81
|
+
Puppet::Network::HttpPool.connection('me', 8140, use_ssl: false, ssl_context: ssl_context)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
49
85
|
describe 'peer verification' do
|
50
86
|
def setup_standard_ssl_configuration
|
51
87
|
ca_cert_file = File.expand_path('/path/to/ssl/certs/ca_cert.pem')
|
@@ -250,6 +250,11 @@ describe 'The Loader' do
|
|
250
250
|
'aplan.pp' => <<-PUPPET.unindent,
|
251
251
|
plan b::aplan() {}
|
252
252
|
PUPPET
|
253
|
+
'yamlplan.yaml' => '{}',
|
254
|
+
'conflict.yaml' => '{}',
|
255
|
+
'conflict.pp' => <<-PUPPET.unindent,
|
256
|
+
plan b::conflict() {}
|
257
|
+
PUPPET
|
253
258
|
}
|
254
259
|
}
|
255
260
|
|
@@ -368,7 +373,7 @@ describe 'The Loader' do
|
|
368
373
|
|
369
374
|
it 'private loader finds plans in all modules' do
|
370
375
|
expect(loader.private_loader.discover(:plan) { |t| t.name =~ /^.(?:::.*)?\z/ }).to(
|
371
|
-
contain_exactly(tn(:plan, 'b'), tn(:plan, 'a::aplan'), tn(:plan, 'b::aplan')))
|
376
|
+
contain_exactly(tn(:plan, 'b'), tn(:plan, 'a::aplan'), tn(:plan, 'b::aplan'), tn(:plan, 'b::conflict')))
|
372
377
|
end
|
373
378
|
|
374
379
|
it 'module loader finds plans only in itself' do
|
@@ -376,6 +381,26 @@ describe 'The Loader' do
|
|
376
381
|
contain_exactly(tn(:plan, 'a::aplan')))
|
377
382
|
end
|
378
383
|
|
384
|
+
context 'with a yaml plan instantiator defined' do
|
385
|
+
before :each do
|
386
|
+
Puppet.push_context(:yaml_plan_instantiator => mock(:create => mock('plan')))
|
387
|
+
end
|
388
|
+
|
389
|
+
after :each do
|
390
|
+
Puppet.pop_context
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'module loader finds yaml plans' do
|
394
|
+
expect(Loaders.find_loader('b').discover(:plan)).to(
|
395
|
+
include(tn(:plan, 'b::yamlplan')))
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'module loader excludes plans with both .pp and .yaml versions' do
|
399
|
+
expect(Loaders.find_loader('b').discover(:plan)).not_to(
|
400
|
+
include(tn(:plan, 'b::conflict')))
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
379
404
|
it 'private loader finds types in all modules' do
|
380
405
|
expect(loader.private_loader.discover(:type) { |t| t.name =~ /^.::.*\z/ }).to(
|
381
406
|
contain_exactly(tn(:type, 'a::atype'), tn(:type, 'b::atype'), tn(:type, 'c::atype')))
|
@@ -86,7 +86,7 @@ describe Puppet::Type.type(:package).provider(:windows), :if => Puppet.features.
|
|
86
86
|
context '#install' do
|
87
87
|
let(:command) { 'blarg.exe /S' }
|
88
88
|
let(:klass) { mock('installer', :install_command => ['blarg.exe', '/S'] ) }
|
89
|
-
let(:execute_options) do {:failonfail => false, :combine => true, :cwd =>
|
89
|
+
let(:execute_options) do {:failonfail => false, :combine => true, :cwd => nil, :suppress_window => true} end
|
90
90
|
before :each do
|
91
91
|
Puppet::Provider::Package::Windows::Package.expects(:installer_class).returns(klass)
|
92
92
|
end
|
@@ -136,6 +136,17 @@ describe Puppet::Type.type(:package).provider(:windows), :if => Puppet.features.
|
|
136
136
|
expect(error.code).to eq(5) # ERROR_ACCESS_DENIED
|
137
137
|
end
|
138
138
|
end
|
139
|
+
|
140
|
+
context 'With a real working dir' do
|
141
|
+
let(:execute_options) do {:failonfail => false, :combine => true, :cwd => 'E:\Rando\Directory', :suppress_window => true} end
|
142
|
+
|
143
|
+
it 'should not try to set the working directory' do
|
144
|
+
Puppet::FileSystem.expects(:exist?).with('E:\Rando\Directory').returns(true)
|
145
|
+
expect_execute(command, 0)
|
146
|
+
|
147
|
+
provider.install
|
148
|
+
end
|
149
|
+
end
|
139
150
|
end
|
140
151
|
|
141
152
|
context '#uninstall' do
|
@@ -18,18 +18,18 @@ describe processor do
|
|
18
18
|
it "configures the connection for ssl when using https" do
|
19
19
|
Puppet[:reporturl] = 'https://testing:8080/the/path'
|
20
20
|
|
21
|
-
Puppet::Network::HttpPool.expects(:
|
22
|
-
'testing', 8080,
|
21
|
+
Puppet::Network::HttpPool.expects(:connection).with(
|
22
|
+
'testing', 8080, has_entry(ssl_context: instance_of(Puppet::SSL::SSLContext))
|
23
23
|
).returns http
|
24
24
|
|
25
25
|
subject.process
|
26
26
|
end
|
27
27
|
|
28
|
-
it "does not configure the
|
29
|
-
Puppet[:reporturl] =
|
28
|
+
it "does not configure the connection for ssl when using http" do
|
29
|
+
Puppet[:reporturl] = 'http://testing:8080/the/path'
|
30
30
|
|
31
|
-
Puppet::Network::HttpPool.expects(:
|
32
|
-
'testing', 8080, false
|
31
|
+
Puppet::Network::HttpPool.expects(:connection).with(
|
32
|
+
'testing', 8080, use_ssl: false, ssl_context: nil
|
33
33
|
).returns http
|
34
34
|
|
35
35
|
subject.process
|
@@ -42,7 +42,7 @@ describe processor do
|
|
42
42
|
let(:options) { {:metric_id => [:puppet, :report, :http]} }
|
43
43
|
|
44
44
|
before :each do
|
45
|
-
Puppet::Network::HttpPool.expects(:
|
45
|
+
Puppet::Network::HttpPool.expects(:connection).returns(connection)
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should use the path specified by the 'reporturl' setting" do
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
require 'puppet/rest/client'
|
4
|
-
require 'puppet/rest/ssl_context'
|
5
4
|
require 'puppet_spec/validators'
|
6
5
|
require 'puppet_spec/ssl'
|
7
6
|
|
@@ -57,7 +56,6 @@ describe Puppet::Rest::Client do
|
|
57
56
|
|
58
57
|
it 'initializes itself with basic defaults' do
|
59
58
|
HTTPClient.expects(:new).returns(http)
|
60
|
-
OpenSSL::X509::Store.stubs(:new).returns(ssl_store)
|
61
59
|
# Configure connection with HTTP settings
|
62
60
|
Puppet[:http_read_timeout] = 120
|
63
61
|
Puppet[:http_connect_timeout] = 10
|
@@ -73,18 +71,18 @@ describe Puppet::Rest::Client do
|
|
73
71
|
Puppet[:hostcert] = '/fake/cert/path'
|
74
72
|
ssl_config.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
75
73
|
|
76
|
-
Puppet::Rest::Client.new(ssl_context: Puppet::
|
74
|
+
Puppet::Rest::Client.new(ssl_context: Puppet::SSL::SSLContext.new(verify_peer: false, store: ssl_store))
|
77
75
|
end
|
78
76
|
|
79
77
|
it 'uses a given client and SSL store when provided' do
|
80
78
|
ssl_config.expects(:cert_store=).with(ssl_store)
|
81
79
|
Puppet::Rest::Client.new(client: http,
|
82
|
-
ssl_context: Puppet::
|
80
|
+
ssl_context: Puppet::SSL::SSLContext.new(verify_peer: true, store: ssl_store))
|
83
81
|
end
|
84
82
|
|
85
83
|
it 'configures a receive timeout when provided' do
|
86
84
|
http.expects(:receive_timeout=).with(10)
|
87
|
-
Puppet::Rest::Client.new(ssl_context: Puppet::
|
85
|
+
Puppet::Rest::Client.new(ssl_context: Puppet::SSL::SSLContext.new(verify_peer: false),
|
88
86
|
client: http, receive_timeout: 10)
|
89
87
|
end
|
90
88
|
end
|
@@ -92,7 +90,7 @@ describe Puppet::Rest::Client do
|
|
92
90
|
context 'when making requests' do
|
93
91
|
let(:ssl_config) { stub_everything('ssl config') }
|
94
92
|
let(:http) { stub_everything('http', :ssl_config => ssl_config) }
|
95
|
-
let(:client) { Puppet::Rest::Client.new(ssl_context: Puppet::
|
93
|
+
let(:client) { Puppet::Rest::Client.new(ssl_context: Puppet::SSL::SSLContext.new(verify_peer: false), client: http) }
|
96
94
|
let(:url) { 'https://myserver.com:555/data' }
|
97
95
|
|
98
96
|
describe "#get" do
|
data/spec/unit/ssl/host_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'spec_helper'
|
3
|
+
require 'webmock/rspec'
|
3
4
|
require 'puppet/test_ca'
|
4
5
|
|
5
6
|
require 'puppet/ssl/host'
|
@@ -308,40 +309,49 @@ describe Puppet::SSL::Host, if: !Puppet::Util::Platform.jruby? do
|
|
308
309
|
@host.stubs(:validate_certificate_with_key)
|
309
310
|
@host.stubs(:http_client).returns(@http)
|
310
311
|
@host.stubs(:ssl_store).returns(mock("ssl store"))
|
312
|
+
|
313
|
+
WebMock.disable_net_connect!
|
314
|
+
Net::HTTP.any_instance.stubs(:start)
|
315
|
+
Net::HTTP.any_instance.stubs(:finish)
|
311
316
|
end
|
312
317
|
|
313
318
|
let(:ca_cert_response) { @pki[:ca_bundle] }
|
319
|
+
let(:crl_response) { @pki[:crl_chain] }
|
314
320
|
let(:host_cert_response) { @pki[:unrevoked_leaf_node_cert] }
|
315
321
|
|
316
322
|
it "should find the CA certificate and save it to disk" do
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
.with(@host.name, anything)
|
322
|
-
.raises(Puppet::Rest::ResponseError.new('no client cert',
|
323
|
-
mock('response', code: '404')))
|
323
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
324
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
325
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{@host.name}}).to_return(status: 404)
|
326
|
+
|
324
327
|
@host.certificate
|
325
328
|
actual_ca_bundle = Puppet::FileSystem.read(Puppet[:localcacert])
|
326
329
|
expect(actual_ca_bundle).to match(/BEGIN CERTIFICATE.*END CERTIFICATE.*BEGIN CERTIFICATE/m)
|
327
330
|
end
|
328
331
|
|
329
|
-
it "should
|
330
|
-
|
332
|
+
it "should raise if it cannot find a CA certificate" do
|
333
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 404)
|
334
|
+
|
331
335
|
@host.expects(:get_host_certificate).never
|
332
336
|
|
333
|
-
expect
|
337
|
+
expect {
|
338
|
+
@host.certificate
|
339
|
+
}.to raise_error(Puppet::Error, /CA certificate is missing from the server/)
|
334
340
|
end
|
335
341
|
|
336
342
|
it "should find the key if it does not have one" do
|
337
|
-
|
343
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
344
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
345
|
+
|
338
346
|
@host.expects(:get_host_certificate).returns(nil)
|
339
347
|
@host.expects(:key).returns mock("key")
|
340
348
|
@host.certificate
|
341
349
|
end
|
342
350
|
|
343
351
|
it "should generate the key if one cannot be found" do
|
344
|
-
|
352
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
353
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
354
|
+
|
345
355
|
@host.expects(:get_host_certificate).returns(nil)
|
346
356
|
@host.expects(:key).returns nil
|
347
357
|
@host.expects(:generate_key)
|
@@ -349,12 +359,10 @@ describe Puppet::SSL::Host, if: !Puppet::Util::Platform.jruby? do
|
|
349
359
|
end
|
350
360
|
|
351
361
|
it "should find the host certificate, write it to file, and return the Puppet certificate instance" do
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
.with(@host.name, anything)
|
357
|
-
.returns(host_cert_response)
|
362
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
363
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
364
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{@host.name}}).to_return(status: 200, body: host_cert_response.to_pem)
|
365
|
+
|
358
366
|
expected_cert = Puppet::SSL::Certificate.from_s(@pki[:unrevoked_leaf_node_cert])
|
359
367
|
actual_cert = @host.certificate
|
360
368
|
expect(actual_cert).to be_a(Puppet::SSL::Certificate)
|
@@ -365,7 +373,8 @@ describe Puppet::SSL::Host, if: !Puppet::Util::Platform.jruby? do
|
|
365
373
|
|
366
374
|
it "should return any previously found certificate" do
|
367
375
|
cert = mock 'cert'
|
368
|
-
|
376
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
377
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
369
378
|
@host.expects(:get_host_certificate).returns(cert).once
|
370
379
|
|
371
380
|
expect(@host.certificate).to equal(cert)
|
@@ -374,19 +383,16 @@ describe Puppet::SSL::Host, if: !Puppet::Util::Platform.jruby? do
|
|
374
383
|
|
375
384
|
context 'invalid certificates' do
|
376
385
|
it "should raise if the CA certificate downloaded from CA is invalid" do
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
expect { @host.certificate }.to raise_error(Puppet::Error, /did not contain a valid CA certificate/)
|
386
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: 'garbage')
|
387
|
+
|
388
|
+
expect { @host.certificate }.to raise_error(OpenSSL::X509::CertificateError, /Failed to parse CA certificates as PEM/)
|
381
389
|
end
|
382
390
|
|
383
391
|
it "should warn if the host certificate downloaded from CA is invalid" do
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
.with(@host.name, anything)
|
389
|
-
.returns('garbage')
|
392
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
393
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
394
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{@host.name}}).to_return(status: 200, body: 'garbage')
|
395
|
+
|
390
396
|
expect { @host.certificate }.to raise_error(Puppet::Error, /did not contain a valid certificate for #{@host.name}/)
|
391
397
|
end
|
392
398
|
|
@@ -394,13 +400,13 @@ describe Puppet::SSL::Host, if: !Puppet::Util::Platform.jruby? do
|
|
394
400
|
Puppet::FileSystem.open(Puppet[:localcacert], nil, "w:ASCII") do |f|
|
395
401
|
f.puts 'garbage'
|
396
402
|
end
|
397
|
-
expect { @host.certificate }.to raise_error(
|
403
|
+
expect { @host.certificate }.to raise_error(OpenSSL::X509::CertificateError, /Failed to parse CA certificates as PEM/)
|
398
404
|
end
|
399
405
|
|
400
406
|
it 'should warn if the host certificate loaded from disk in invalid' do
|
401
|
-
|
402
|
-
|
403
|
-
|
407
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: ca_cert_response)
|
408
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_response)
|
409
|
+
|
404
410
|
Puppet::FileSystem.open(File.join(Puppet[:certdir], "#{@host.name}.pem"), nil, "w:ASCII") do |f|
|
405
411
|
f.puts 'garbage'
|
406
412
|
end
|
@@ -0,0 +1,428 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Puppet::SSL::SSLProvider do
|
4
|
+
include PuppetSpec::Files
|
5
|
+
|
6
|
+
let(:global_cacerts) { [ cert_fixture('ca.pem'), cert_fixture('intermediate.pem') ] }
|
7
|
+
let(:global_crls) { [ crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem') ] }
|
8
|
+
let(:wrong_key) { OpenSSL::PKey::RSA.new(512) }
|
9
|
+
|
10
|
+
def as_pem_file(x509)
|
11
|
+
path = tmpfile('ssl_provider_pem')
|
12
|
+
File.write(path, x509.to_pem)
|
13
|
+
path
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when creating an insecure context' do
|
17
|
+
let(:sslctx) { subject.create_insecure_context }
|
18
|
+
|
19
|
+
it 'has an empty list of trusted certs' do
|
20
|
+
expect(sslctx.cacerts).to eq([])
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has an empty list of crls' do
|
24
|
+
expect(sslctx.crls).to eq([])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has an empty chain' do
|
28
|
+
expect(sslctx.client_chain).to eq([])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has a nil private key and cert' do
|
32
|
+
expect(sslctx.private_key).to be_nil
|
33
|
+
expect(sslctx.client_cert).to be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not authenticate the server' do
|
37
|
+
expect(sslctx.verify_peer).to eq(false)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'raises if the frozen context is modified' do
|
41
|
+
expect {
|
42
|
+
sslctx.cacerts = []
|
43
|
+
}.to raise_error(/can't modify frozen/)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when creating an root ssl context with CA certs' do
|
48
|
+
let(:config) { { cacerts: [], crls: [], revocation: false } }
|
49
|
+
|
50
|
+
it 'accepts empty list of certs and crls' do
|
51
|
+
sslctx = subject.create_root_context(config)
|
52
|
+
expect(sslctx.cacerts).to eq([])
|
53
|
+
expect(sslctx.crls).to eq([])
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'accepts valid root certs' do
|
57
|
+
certs = [cert_fixture('ca.pem')]
|
58
|
+
sslctx = subject.create_root_context(config.merge(cacerts: certs))
|
59
|
+
expect(sslctx.cacerts).to eq(certs)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'accepts valid intermediate certs' do
|
63
|
+
certs = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]
|
64
|
+
sslctx = subject.create_root_context(config.merge(cacerts: certs))
|
65
|
+
expect(sslctx.cacerts).to eq(certs)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'accepts expired CA certs' do
|
69
|
+
expired = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]
|
70
|
+
expired.each { |x509| x509.not_after = Time.at(0) }
|
71
|
+
|
72
|
+
sslctx = subject.create_root_context(config.merge(cacerts: expired))
|
73
|
+
expect(sslctx.cacerts).to eq(expired)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises if the frozen context is modified' do
|
77
|
+
sslctx = subject.create_root_context(config)
|
78
|
+
expect {
|
79
|
+
sslctx.verify_peer = false
|
80
|
+
}.to raise_error(/can't modify frozen/)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when creating an ssl context with crls' do
|
85
|
+
let(:config) { { cacerts: global_cacerts, crls: global_crls} }
|
86
|
+
|
87
|
+
it 'accepts valid CRLs' do
|
88
|
+
certs = [cert_fixture('ca.pem')]
|
89
|
+
crls = [crl_fixture('crl.pem')]
|
90
|
+
sslctx = subject.create_root_context(config.merge(cacerts: certs, crls: crls))
|
91
|
+
expect(sslctx.crls).to eq(crls)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'accepts valid CRLs for intermediate certs' do
|
95
|
+
certs = [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')]
|
96
|
+
crls = [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')]
|
97
|
+
sslctx = subject.create_root_context(config.merge(cacerts: certs, crls: crls))
|
98
|
+
expect(sslctx.crls).to eq(crls)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'accepts expired CRLs' do
|
102
|
+
expired = [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')]
|
103
|
+
expired.each { |x509| x509.last_update = Time.at(0) }
|
104
|
+
|
105
|
+
sslctx = subject.create_root_context(config.merge(crls: expired))
|
106
|
+
expect(sslctx.crls).to eq(expired)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when creating an ssl context with client certs' do
|
111
|
+
let(:client_cert) { cert_fixture('signed.pem') }
|
112
|
+
let(:private_key) { key_fixture('signed-key.pem') }
|
113
|
+
let(:config) { { cacerts: global_cacerts, crls: global_crls, client_cert: client_cert, private_key: private_key } }
|
114
|
+
|
115
|
+
it 'raises if CA certs are missing' do
|
116
|
+
expect {
|
117
|
+
subject.create_context(config.merge(cacerts: nil))
|
118
|
+
}.to raise_error(ArgumentError, /CA certs are missing/)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'raises if CRLs are are missing' do
|
122
|
+
expect {
|
123
|
+
subject.create_context(config.merge(crls: nil))
|
124
|
+
}.to raise_error(ArgumentError, /CRLs are missing/)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'raises if private key is missing' do
|
128
|
+
expect {
|
129
|
+
subject.create_context(config.merge(private_key: nil))
|
130
|
+
}.to raise_error(ArgumentError, /Private key is missing/)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises if client cert is missing' do
|
134
|
+
expect {
|
135
|
+
subject.create_context(config.merge(client_cert: nil))
|
136
|
+
}.to raise_error(ArgumentError, /Client cert is missing/)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'accepts RSA keys' do
|
140
|
+
sslctx = subject.create_context(config)
|
141
|
+
expect(sslctx.private_key).to eq(private_key)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'raises if private key is unsupported' do
|
145
|
+
ec_key = OpenSSL::PKey::EC.new
|
146
|
+
expect {
|
147
|
+
subject.create_context(config.merge(private_key: ec_key))
|
148
|
+
}.to raise_error(Puppet::SSL::SSLError, /Unsupported key 'OpenSSL::PKey::EC'/)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'resolves the client chain from leaf to root' do
|
152
|
+
sslctx = subject.create_context(config)
|
153
|
+
expect(
|
154
|
+
sslctx.client_chain.map(&:subject).map(&:to_s)
|
155
|
+
).to eq(['/CN=signed', '/CN=Test CA Subauthority', '/CN=Test CA'])
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'raises if client cert signature is invalid' do
|
159
|
+
client_cert.sign(wrong_key, OpenSSL::Digest::SHA256.new)
|
160
|
+
expect {
|
161
|
+
subject.create_context(config.merge(client_cert: client_cert))
|
162
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
163
|
+
"Invalid signature for certificate '/CN=signed'")
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'raises if client cert and private key are mismatched' do
|
167
|
+
expect {
|
168
|
+
subject.create_context(config.merge(private_key: wrong_key))
|
169
|
+
}.to raise_error(Puppet::SSL::SSLError,
|
170
|
+
"The certificate for '/CN=signed' does not match its private key")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "raises if client cert's public key has been replaced" do
|
174
|
+
expect {
|
175
|
+
subject.create_context(config.merge(client_cert: cert_fixture('tampered-cert.pem')))
|
176
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
177
|
+
"Invalid signature for certificate '/CN=signed'")
|
178
|
+
end
|
179
|
+
|
180
|
+
# This option is only available in openssl 1.1
|
181
|
+
it 'raises if root cert signature is invalid', if: defined?(OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE) do
|
182
|
+
ca = global_cacerts.first
|
183
|
+
ca.sign(wrong_key, OpenSSL::Digest::SHA256.new)
|
184
|
+
|
185
|
+
expect {
|
186
|
+
subject.create_context(config.merge(cacerts: global_cacerts))
|
187
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
188
|
+
"Invalid signature for certificate '/CN=Test CA'")
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'raises if intermediate CA signature is invalid' do
|
192
|
+
int = global_cacerts.last
|
193
|
+
int.sign(wrong_key, OpenSSL::Digest::SHA256.new)
|
194
|
+
|
195
|
+
expect {
|
196
|
+
subject.create_context(config.merge(cacerts: global_cacerts))
|
197
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
198
|
+
"Invalid signature for certificate '/CN=Test CA Subauthority'")
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'raises if CRL signature for root CA is invalid', unless: Puppet::Util::Platform.jruby? do
|
202
|
+
crl = global_crls.first
|
203
|
+
crl.sign(wrong_key, OpenSSL::Digest::SHA256.new)
|
204
|
+
|
205
|
+
expect {
|
206
|
+
subject.create_context(config.merge(crls: global_crls))
|
207
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
208
|
+
"Invalid signature for CRL issued by '/CN=Test CA'")
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'raises if CRL signature for intermediate CA is invalid', unless: Puppet::Util::Platform.jruby? do
|
212
|
+
crl = global_crls.last
|
213
|
+
crl.sign(wrong_key, OpenSSL::Digest::SHA256.new)
|
214
|
+
|
215
|
+
expect {
|
216
|
+
subject.create_context(config.merge(crls: global_crls))
|
217
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
218
|
+
"Invalid signature for CRL issued by '/CN=Test CA Subauthority'")
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'raises if client cert is revoked' do
|
222
|
+
expect {
|
223
|
+
subject.create_context(config.merge(private_key: key_fixture('revoked-key.pem'), client_cert: cert_fixture('revoked.pem')))
|
224
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
225
|
+
"Certificate '/CN=revoked' is revoked")
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'warns if intermediate issuer is missing' do
|
229
|
+
Puppet.expects(:warning).with("The issuer '/CN=Test CA Subauthority' of certificate '/CN=signed' cannot be found locally")
|
230
|
+
|
231
|
+
subject.create_context(config.merge(cacerts: [cert_fixture('ca.pem')]))
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'raises if root issuer is missing' do
|
235
|
+
expect {
|
236
|
+
subject.create_context(config.merge(cacerts: [cert_fixture('intermediate.pem')]))
|
237
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
238
|
+
"The issuer '/CN=Test CA' of certificate '/CN=Test CA Subauthority' is missing")
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'raises if cert is not valid yet', unless: Puppet::Util::Platform.jruby? do
|
242
|
+
client_cert.not_before = Time.now + (5 * 60 * 60)
|
243
|
+
expect {
|
244
|
+
subject.create_context(config.merge(client_cert: client_cert))
|
245
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
246
|
+
"The certificate '/CN=signed' is not yet valid, verify time is synchronized")
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'raises if cert is expired', unless: Puppet::Util::Platform.jruby? do
|
250
|
+
client_cert.not_after = Time.at(0)
|
251
|
+
expect {
|
252
|
+
subject.create_context(config.merge(client_cert: client_cert))
|
253
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
254
|
+
"The certificate '/CN=signed' has expired, verify time is synchronized")
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'raises if crl is not valid yet', unless: Puppet::Util::Platform.jruby? do
|
258
|
+
future_crls = global_crls
|
259
|
+
# invalidate the CRL issued by the root
|
260
|
+
future_crls.first.last_update = Time.now + (5 * 60 * 60)
|
261
|
+
|
262
|
+
expect {
|
263
|
+
subject.create_context(config.merge(crls: future_crls))
|
264
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
265
|
+
"The CRL issued by '/CN=Test CA' is not yet valid, verify time is synchronized")
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'raises if crl is expired', unless: Puppet::Util::Platform.jruby? do
|
269
|
+
past_crls = global_crls
|
270
|
+
# invalidate the CRL issued by the root
|
271
|
+
past_crls.first.next_update = Time.at(0)
|
272
|
+
|
273
|
+
expect {
|
274
|
+
subject.create_context(config.merge(crls: past_crls))
|
275
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
276
|
+
"The CRL issued by '/CN=Test CA' has expired, verify time is synchronized")
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'raises if the root CRL is missing' do
|
280
|
+
crls = [crl_fixture('intermediate-crl.pem')]
|
281
|
+
expect {
|
282
|
+
subject.create_context(config.merge(crls: crls, revocation: :chain))
|
283
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
284
|
+
"The CRL issued by '/CN=Test CA' is missing")
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'raises if the intermediate CRL is missing' do
|
288
|
+
crls = [crl_fixture('crl.pem')]
|
289
|
+
expect {
|
290
|
+
subject.create_context(config.merge(crls: crls))
|
291
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
292
|
+
"The CRL issued by '/CN=Test CA Subauthority' is missing")
|
293
|
+
end
|
294
|
+
|
295
|
+
it "doesn't raise if the root CRL is missing and we're just checking the leaf" do
|
296
|
+
crls = [crl_fixture('intermediate-crl.pem')]
|
297
|
+
subject.create_context(config.merge(crls: crls, revocation: :leaf))
|
298
|
+
end
|
299
|
+
|
300
|
+
it "doesn't raise if the intermediate CRL is missing and revocation checking is disabled" do
|
301
|
+
crls = [crl_fixture('crl.pem')]
|
302
|
+
subject.create_context(config.merge(crls: crls, revocation: false))
|
303
|
+
end
|
304
|
+
|
305
|
+
it "doesn't raise if both CRLs are missing and revocation checking is disabled" do
|
306
|
+
subject.create_context(config.merge(crls: [], revocation: false))
|
307
|
+
end
|
308
|
+
|
309
|
+
# OpenSSL < 1.1 does not verify basicConstraints
|
310
|
+
it "raises if root CA's isCA basic constraint is false", unless: Puppet::Util::Platform.jruby? || OpenSSL::OPENSSL_VERSION_NUMBER < 0x10100000 do
|
311
|
+
certs = [cert_fixture('bad-basic-constraints.pem'), cert_fixture('intermediate.pem')]
|
312
|
+
|
313
|
+
expect {
|
314
|
+
subject.create_context(config.merge(cacerts: certs, crls: [], revocation: false))
|
315
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
316
|
+
"Certificate '/CN=Test CA' failed verification (24): invalid CA certificate")
|
317
|
+
end
|
318
|
+
|
319
|
+
# OpenSSL < 1.1 does not verify basicConstraints
|
320
|
+
it "raises if intermediate CA's isCA basic constraint is false", unless: Puppet::Util::Platform.jruby? || OpenSSL::OPENSSL_VERSION_NUMBER < 0x10100000 do
|
321
|
+
certs = [cert_fixture('ca.pem'), cert_fixture('bad-int-basic-constraints.pem')]
|
322
|
+
|
323
|
+
expect {
|
324
|
+
subject.create_context(config.merge(cacerts: certs, crls: [], revocation: false))
|
325
|
+
}.to raise_error(Puppet::SSL::CertVerifyError,
|
326
|
+
"Certificate '/CN=Test CA Subauthority' failed verification (24): invalid CA certificate")
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'accepts CA certs in any order' do
|
330
|
+
sslctx = subject.create_context(config.merge(cacerts: global_cacerts.reverse))
|
331
|
+
# certs in ruby+openssl 1.0.x are not comparable, so compare subjects
|
332
|
+
expect(sslctx.client_chain.map(&:subject).map(&:to_s)).to contain_exactly('/CN=Test CA', '/CN=Test CA Subauthority', '/CN=signed')
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'accepts CRLs in any order' do
|
336
|
+
sslctx = subject.create_context(config.merge(crls: global_crls.reverse))
|
337
|
+
# certs in ruby+openssl 1.0.x are not comparable, so compare subjects
|
338
|
+
expect(sslctx.client_chain.map(&:subject).map(&:to_s)).to contain_exactly('/CN=Test CA', '/CN=Test CA Subauthority', '/CN=signed')
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'raises if the frozen context is modified' do
|
342
|
+
sslctx = subject.create_context(config)
|
343
|
+
expect {
|
344
|
+
sslctx.verify_peer = false
|
345
|
+
}.to raise_error(/can't modify frozen/)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'when loading an ssl context' do
|
350
|
+
let(:client_cert) { cert_fixture('signed.pem') }
|
351
|
+
let(:private_key) { key_fixture('signed-key.pem') }
|
352
|
+
let(:doesnt_exist) { '/does/not/exist' }
|
353
|
+
|
354
|
+
before :each do
|
355
|
+
Puppet[:localcacert] = as_pem_file(global_cacerts.first)
|
356
|
+
Puppet[:hostcrl] = as_pem_file(global_crls.first)
|
357
|
+
|
358
|
+
Puppet[:certname] = 'signed'
|
359
|
+
Puppet[:privatekeydir] = tmpdir('privatekeydir')
|
360
|
+
File.write(File.join(Puppet[:privatekeydir], 'signed.pem'), private_key.to_pem)
|
361
|
+
|
362
|
+
Puppet[:certdir] = tmpdir('privatekeydir')
|
363
|
+
File.write(File.join(Puppet[:certdir], 'signed.pem'), client_cert.to_pem)
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'raises if CA certs are missing' do
|
367
|
+
Puppet[:localcacert] = doesnt_exist
|
368
|
+
|
369
|
+
expect {
|
370
|
+
subject.load_context
|
371
|
+
}.to raise_error(Puppet::Error, /The CA certificates are missing from/)
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'raises if the CRL is missing' do
|
375
|
+
Puppet[:hostcrl] = doesnt_exist
|
376
|
+
|
377
|
+
expect {
|
378
|
+
subject.load_context
|
379
|
+
}.to raise_error(Puppet::Error, /The CRL is missing from/)
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'does not raise if the CRL is missing and revocation is disabled' do
|
383
|
+
Puppet[:hostcrl] = doesnt_exist
|
384
|
+
|
385
|
+
subject.load_context(revocation: false)
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'raises if the private key is missing' do
|
389
|
+
Puppet[:privatekeydir] = doesnt_exist
|
390
|
+
|
391
|
+
expect {
|
392
|
+
subject.load_context
|
393
|
+
}.to raise_error(Puppet::Error, /The private key is missing from/)
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'raises if the client cert is missing' do
|
397
|
+
Puppet[:certdir] = doesnt_exist
|
398
|
+
|
399
|
+
expect {
|
400
|
+
subject.load_context
|
401
|
+
}.to raise_error(Puppet::Error, /The client certificate is missing from/)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
context 'when verifying requests' do
|
406
|
+
let(:csr) { request_fixture('request.pem') }
|
407
|
+
|
408
|
+
it 'accepts valid requests' do
|
409
|
+
private_key = key_fixture('request-key.pem')
|
410
|
+
expect(subject.verify_request(csr, private_key.public_key)).to eq(csr)
|
411
|
+
end
|
412
|
+
|
413
|
+
it "raises if the CSR was signed by a private key that doesn't match public key" do
|
414
|
+
expect {
|
415
|
+
subject.verify_request(csr, wrong_key.public_key)
|
416
|
+
}.to raise_error(Puppet::SSL::SSLError,
|
417
|
+
"The CSR for host '/CN=pending' does not match the public key")
|
418
|
+
end
|
419
|
+
|
420
|
+
it "raises if the CSR was tampered with" do
|
421
|
+
csr = request_fixture('tampered-csr.pem')
|
422
|
+
expect {
|
423
|
+
subject.verify_request(csr, csr.public_key)
|
424
|
+
}.to raise_error(Puppet::SSL::SSLError,
|
425
|
+
"The CSR for host '/CN=signed' does not match the public key")
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|