puppet 6.3.0-x86-mingw32 → 6.4.0-x86-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,502 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'puppet_spec/files'
|
4
|
+
|
5
|
+
require 'puppet/ssl'
|
6
|
+
|
7
|
+
describe Puppet::SSL::StateMachine, unless: Puppet::Util::Platform.jruby? do
|
8
|
+
include PuppetSpec::Files
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
WebMock.disable_net_connect!
|
12
|
+
|
13
|
+
Net::HTTP.any_instance.stubs(:start)
|
14
|
+
Net::HTTP.any_instance.stubs(:finish)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:machine) { described_class.new }
|
18
|
+
let(:cacert_pem) { cacert.to_pem }
|
19
|
+
let(:cacert) { cert_fixture('ca.pem') }
|
20
|
+
let(:cacerts) { [cacert] }
|
21
|
+
|
22
|
+
let(:crl_pem) { crl.to_pem }
|
23
|
+
let(:crl) { crl_fixture('crl.pem') }
|
24
|
+
let(:crls) { [crl] }
|
25
|
+
|
26
|
+
let(:private_key) { key_fixture('signed-key.pem') }
|
27
|
+
let(:client_cert) { cert_fixture('signed.pem') }
|
28
|
+
|
29
|
+
before(:each) do
|
30
|
+
WebMock.disable_net_connect!
|
31
|
+
|
32
|
+
Net::HTTP.any_instance.stubs(:start)
|
33
|
+
Net::HTTP.any_instance.stubs(:finish)
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when ensuring CA certs and CRLs' do
|
37
|
+
it 'returns an SSLContext with the loaded CA certs and CRLs' do
|
38
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(cacerts)
|
39
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(crls)
|
40
|
+
|
41
|
+
ssl_context = machine.ensure_ca_certificates
|
42
|
+
|
43
|
+
expect(ssl_context[:cacerts]).to eq(cacerts)
|
44
|
+
expect(ssl_context[:crls]).to eq(crls)
|
45
|
+
expect(ssl_context[:verify_peer]).to eq(true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when ensuring a client cert' do
|
50
|
+
it 'returns an SSLContext with the loaded CA certs, CRLs, private key and client cert' do
|
51
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(cacerts)
|
52
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(crls)
|
53
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).returns(private_key)
|
54
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_client_cert).returns(client_cert)
|
55
|
+
|
56
|
+
ssl_context = machine.ensure_client_certificate
|
57
|
+
|
58
|
+
expect(ssl_context[:cacerts]).to eq(cacerts)
|
59
|
+
expect(ssl_context[:crls]).to eq(crls)
|
60
|
+
expect(ssl_context[:verify_peer]).to eq(true)
|
61
|
+
expect(ssl_context[:private_key]).to eq(private_key)
|
62
|
+
expect(ssl_context[:client_cert]).to eq(client_cert)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'NeedCACerts' do
|
67
|
+
let(:state) { Puppet::SSL::StateMachine::NeedCACerts.new(machine) }
|
68
|
+
|
69
|
+
before :each do
|
70
|
+
Puppet[:localcacert] = tmpfile('needcacerts')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'transitions to NeedCRLs state' do
|
74
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(cacerts)
|
75
|
+
|
76
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCRLs)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'loads existing CA certs' do
|
80
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(cacerts)
|
81
|
+
|
82
|
+
st = state.next_state
|
83
|
+
expect(st.ssl_context[:cacerts]).to eq(cacerts)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'fetches and saves CA certs' do
|
87
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(nil)
|
88
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: cacert_pem)
|
89
|
+
|
90
|
+
st = state.next_state
|
91
|
+
expect(st.ssl_context[:cacerts].map(&:to_pem)).to eq(cacerts.map(&:to_pem))
|
92
|
+
expect(File).to be_exist(Puppet[:localcacert])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "does not verify the server's cert if there are no local CA certs" do
|
96
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(nil)
|
97
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: cacert_pem)
|
98
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_cacerts)
|
99
|
+
|
100
|
+
Net::HTTP.any_instance.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
101
|
+
|
102
|
+
state.next_state
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'raises if the server returns 404' do
|
106
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 404)
|
107
|
+
|
108
|
+
expect {
|
109
|
+
state.next_state
|
110
|
+
}.to raise_error(Puppet::Error, /CA certificate is missing from the server/)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'raises if there is a different error' do
|
114
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: [500, 'Internal Server Error'])
|
115
|
+
|
116
|
+
expect {
|
117
|
+
state.next_state
|
118
|
+
}.to raise_error(Puppet::Error, /Could not download CA certificate: Internal Server Error/)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'raises if CA certs are invalid' do
|
122
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_cacerts).returns(nil)
|
123
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: '')
|
124
|
+
|
125
|
+
expect {
|
126
|
+
state.next_state
|
127
|
+
}.to raise_error(OpenSSL::X509::CertificateError)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does not save invalid CA certs' do
|
131
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: <<~END)
|
132
|
+
-----BEGIN CERTIFICATE-----
|
133
|
+
MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0
|
134
|
+
END
|
135
|
+
|
136
|
+
state.next_state rescue OpenSSL::X509::CertificateError
|
137
|
+
|
138
|
+
expect(File).to_not exist(Puppet[:localcacert])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'NeedCRLs' do
|
143
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts)}
|
144
|
+
let(:state) { Puppet::SSL::StateMachine::NeedCRLs.new(machine, ssl_context) }
|
145
|
+
|
146
|
+
before :each do
|
147
|
+
Puppet[:hostcrl] = tmpfile('needcrls')
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'transitions to NeedKey state' do
|
151
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(crls)
|
152
|
+
|
153
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedKey)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'loads existing CRLs' do
|
157
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(crls)
|
158
|
+
|
159
|
+
st = state.next_state
|
160
|
+
expect(st.ssl_context[:crls]).to eq(crls)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'fetches and saves CRLs' do
|
164
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(nil)
|
165
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_pem)
|
166
|
+
|
167
|
+
st = state.next_state
|
168
|
+
expect(st.ssl_context[:crls].map(&:to_pem)).to eq(crls.map(&:to_pem))
|
169
|
+
expect(File).to be_exist(Puppet[:hostcrl])
|
170
|
+
end
|
171
|
+
|
172
|
+
it "verifies the server's certificate when fetching the CRL" do
|
173
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(nil)
|
174
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: crl_pem)
|
175
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_crls)
|
176
|
+
|
177
|
+
Net::HTTP.any_instance.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
178
|
+
|
179
|
+
state.next_state
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'raises if the server returns 404' do
|
183
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 404)
|
184
|
+
|
185
|
+
expect {
|
186
|
+
state.next_state
|
187
|
+
}.to raise_error(Puppet::Error, /CRL is missing from the server/)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'raises if there is a different error' do
|
191
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: [500, 'Internal Server Error'])
|
192
|
+
|
193
|
+
expect {
|
194
|
+
state.next_state
|
195
|
+
}.to raise_error(Puppet::Error, /Could not download CRLs: Internal Server Error/)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'raises if CRLs are invalid' do
|
199
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_crls).returns(nil)
|
200
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: '')
|
201
|
+
|
202
|
+
expect {
|
203
|
+
state.next_state
|
204
|
+
}.to raise_error(OpenSSL::X509::CRLError)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'does not save invalid CRLs' do
|
208
|
+
stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: <<~END)
|
209
|
+
-----BEGIN X509 CRL-----
|
210
|
+
MIIBCjB1AgEBMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EXDTcw
|
211
|
+
END
|
212
|
+
|
213
|
+
state.next_state rescue OpenSSL::X509::CRLError
|
214
|
+
|
215
|
+
expect(File).to_not exist(Puppet[:hostcrl])
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'skips CRL download when revocation is disabled' do
|
219
|
+
Puppet[:certificate_revocation] = false
|
220
|
+
|
221
|
+
Puppet::X509::CertProvider.any_instance.expects(:load_crls).never
|
222
|
+
Puppet::Rest::Routes.expects(:get_crls).never
|
223
|
+
|
224
|
+
state.next_state
|
225
|
+
|
226
|
+
expect(File).to_not exist(Puppet[:hostcrl])
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'when ensuring a client cert' do
|
231
|
+
context 'in state NeedKey' do
|
232
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}
|
233
|
+
let(:state) { Puppet::SSL::StateMachine::NeedKey.new(machine, ssl_context) }
|
234
|
+
|
235
|
+
it 'loads an existing private key and passes it to the next state' do
|
236
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).returns(private_key)
|
237
|
+
|
238
|
+
st = state.next_state
|
239
|
+
expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)
|
240
|
+
expect(st.private_key).to eq(private_key)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'loads a matching private key and cert' do
|
244
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).returns(private_key)
|
245
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_client_cert).returns(client_cert)
|
246
|
+
|
247
|
+
st = state.next_state
|
248
|
+
expect(st).to be_instance_of(Puppet::SSL::StateMachine::Done)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'raises if the client cert is mismatched' do
|
252
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).returns(private_key)
|
253
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_client_cert).returns(cert_fixture('tampered-cert.pem'))
|
254
|
+
|
255
|
+
expect {
|
256
|
+
state.next_state
|
257
|
+
}.to raise_error(Puppet::SSL::SSLError, %r{The certificate for '/CN=signed' does not match its private key})
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'generates a new private key, saves it and passes it to the next state' do
|
261
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).returns(nil)
|
262
|
+
Puppet::X509::CertProvider.any_instance.expects(:save_private_key)
|
263
|
+
|
264
|
+
st = state.next_state
|
265
|
+
expect(st).to be_instance_of(Puppet::SSL::StateMachine::NeedSubmitCSR)
|
266
|
+
expect(st.private_key).to be_instance_of(OpenSSL::PKey::RSA)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'raises an error if it fails to load the key' do
|
270
|
+
Puppet::X509::CertProvider.any_instance.stubs(:load_private_key).raises(OpenSSL::PKey::RSAError)
|
271
|
+
|
272
|
+
expect {
|
273
|
+
state.next_state
|
274
|
+
}.to raise_error(OpenSSL::PKey::RSAError)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'in state NeedSubmitCSR' do
|
279
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}
|
280
|
+
let(:state) { Puppet::SSL::StateMachine::NeedSubmitCSR.new(machine, ssl_context, private_key) }
|
281
|
+
|
282
|
+
def write_csr_attributes(data)
|
283
|
+
csr_yaml = tmpfile('state_machine_csr')
|
284
|
+
File.write(csr_yaml, YAML.dump(data))
|
285
|
+
csr_yaml
|
286
|
+
end
|
287
|
+
|
288
|
+
before :each do
|
289
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_request)
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'submits the CSR and transitions to NeedCert' do
|
293
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)
|
294
|
+
|
295
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'saves the CSR and transitions to NeedCert' do
|
299
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)
|
300
|
+
|
301
|
+
Puppet::X509::CertProvider.any_instance.expects(:save_request).with(Puppet[:certname], instance_of(OpenSSL::X509::Request))
|
302
|
+
|
303
|
+
state.next_state
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'includes DNS alt names' do
|
307
|
+
Puppet[:dns_alt_names] = "one,IP:192.168.0.1,DNS:two.com"
|
308
|
+
|
309
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|
|
310
|
+
csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))
|
311
|
+
expect(
|
312
|
+
csr.subject_alt_names
|
313
|
+
).to contain_exactly('DNS:one', 'IP Address:192.168.0.1', 'DNS:two.com', "DNS:#{Puppet[:certname]}")
|
314
|
+
end.to_return(status: 200)
|
315
|
+
|
316
|
+
state.next_state
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'includes CSR attributes' do
|
320
|
+
Puppet[:csr_attributes] = write_csr_attributes(
|
321
|
+
'custom_attributes' => {
|
322
|
+
'1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',
|
323
|
+
'1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info'
|
324
|
+
}
|
325
|
+
)
|
326
|
+
|
327
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|
|
328
|
+
csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))
|
329
|
+
expect(
|
330
|
+
csr.custom_attributes
|
331
|
+
).to contain_exactly(
|
332
|
+
{'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'CSR specific info'},
|
333
|
+
{'oid' => '1.3.6.1.4.1.34380.1.2.2', 'value' => 'more CSR specific info'}
|
334
|
+
)
|
335
|
+
end.to_return(status: 200)
|
336
|
+
|
337
|
+
state.next_state
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'includes CSR extension requests' do
|
341
|
+
Puppet[:csr_attributes] = write_csr_attributes(
|
342
|
+
{
|
343
|
+
'extension_requests' => {
|
344
|
+
'1.3.6.1.4.1.34380.1.1.31415' => 'pi',
|
345
|
+
'1.3.6.1.4.1.34380.1.1.2718' => 'e',
|
346
|
+
}
|
347
|
+
}
|
348
|
+
)
|
349
|
+
|
350
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).with do |request|
|
351
|
+
csr = Puppet::SSL::CertificateRequest.from_instance(OpenSSL::X509::Request.new(request.body))
|
352
|
+
expect(
|
353
|
+
csr.request_extensions
|
354
|
+
).to contain_exactly(
|
355
|
+
{'oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi'},
|
356
|
+
{'oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e'}
|
357
|
+
)
|
358
|
+
end.to_return(status: 200)
|
359
|
+
|
360
|
+
state.next_state
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'transitions to NeedCert if the server has a requested certificate' do
|
364
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: "#{Puppet[:certname]} already has a requested certificate")
|
365
|
+
|
366
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'transitions to NeedCert if the server has a signed certificate' do
|
370
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: "#{Puppet[:certname]} already has a signed certificate")
|
371
|
+
|
372
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'transitions to NeedCert if the server has a revoked certificate' do
|
376
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 400, body: "#{Puppet[:certname]} already has a revoked certificate")
|
377
|
+
|
378
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCert)
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'raises if the server errors' do
|
382
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 500)
|
383
|
+
|
384
|
+
expect {
|
385
|
+
state.next_state
|
386
|
+
}.to raise_error(Puppet::SSL::SSLError, /Failed to submit the CSR, HTTP response was 500/)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "verifies the server's certificate when submitting the CSR" do
|
390
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{Puppet[:certname]}}).to_return(status: 200)
|
391
|
+
|
392
|
+
Net::HTTP.any_instance.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
393
|
+
|
394
|
+
state.next_state
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'in state NeedCert' do
|
399
|
+
let(:ca_chain) { [cert_fixture('ca.pem'), cert_fixture('intermediate.pem')] }
|
400
|
+
let(:crl_chain) { [crl_fixture('crl.pem'), crl_fixture('intermediate-crl.pem')] }
|
401
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: ca_chain, crls: crl_chain)}
|
402
|
+
let(:state) { Puppet::SSL::StateMachine::NeedCert.new(machine, ssl_context, private_key) }
|
403
|
+
|
404
|
+
it 'transitions to Done if the cert is signed and matches our private key' do
|
405
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_client_cert)
|
406
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_request)
|
407
|
+
|
408
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: client_cert.to_pem)
|
409
|
+
|
410
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::Done)
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'transitions to Wait if the cert does not match our private key' do
|
414
|
+
wrong_cert = cert_fixture('127.0.0.1.pem')
|
415
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: wrong_cert.to_pem)
|
416
|
+
|
417
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::Wait)
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'transitions to Wait if the server returns non-200' do
|
421
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 404)
|
422
|
+
|
423
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::Wait)
|
424
|
+
end
|
425
|
+
|
426
|
+
it "verifies the server's certificate when getting the client cert" do
|
427
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: client_cert.to_pem)
|
428
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_client_cert)
|
429
|
+
Puppet::X509::CertProvider.any_instance.stubs(:save_request)
|
430
|
+
|
431
|
+
Net::HTTP.any_instance.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
432
|
+
|
433
|
+
state.next_state
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'does not save an invalid client cert' do
|
437
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: <<~END)
|
438
|
+
-----BEGIN CERTIFICATE-----
|
439
|
+
MIIBpDCCAQ2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRUZXN0
|
440
|
+
END
|
441
|
+
|
442
|
+
state.next_state
|
443
|
+
|
444
|
+
expect(@logs).to include(an_object_having_attributes(message: /Failed to parse certificate: /))
|
445
|
+
expect(File).to_not exist(Puppet[:hostcert])
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'does not save a mismatched client cert' do
|
449
|
+
wrong_cert = cert_fixture('127.0.0.1.pem').to_pem
|
450
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: wrong_cert)
|
451
|
+
|
452
|
+
state.next_state
|
453
|
+
|
454
|
+
expect(@logs).to include(an_object_having_attributes(message: %r{The certificate for '/CN=127.0.0.1' does not match its private key}))
|
455
|
+
expect(File).to_not exist(Puppet[:hostcert])
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'does not save a revoked client cert' do
|
459
|
+
revoked_cert = cert_fixture('revoked.pem').to_pem
|
460
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{Puppet[:certname]}}).to_return(status: 200, body: revoked_cert)
|
461
|
+
|
462
|
+
state.next_state
|
463
|
+
|
464
|
+
expect(@logs).to include(an_object_having_attributes(message: %r{Certificate '/CN=revoked' is revoked}))
|
465
|
+
expect(File).to_not exist(Puppet[:hostcert])
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
context 'in state Wait' do
|
470
|
+
let(:ssl_context) { Puppet::SSL::SSLContext.new(cacerts: cacerts, crls: crls)}
|
471
|
+
|
472
|
+
it 'exits with 1 if only running once' do
|
473
|
+
machine = described_class.new(onetime: true)
|
474
|
+
|
475
|
+
expect {
|
476
|
+
expect {
|
477
|
+
Puppet::SSL::StateMachine::Wait.new(machine, ssl_context).next_state
|
478
|
+
}.to output("Exiting; no certificate found and waitforcert is disabled").to_stdout
|
479
|
+
}.to exit_with(1)
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'exits with 1 if waitforcert is 0' do
|
483
|
+
machine = described_class.new(waitforcert: 0)
|
484
|
+
|
485
|
+
expect {
|
486
|
+
expect {
|
487
|
+
Puppet::SSL::StateMachine::Wait.new(machine, ssl_context).next_state
|
488
|
+
}.to output("Exiting; no certificate found and waitforcert is disabled").to_stdout
|
489
|
+
}.to exit_with(1)
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'sleeps and transitions to NeedCACerts' do
|
493
|
+
machine = described_class.new(waitforcert: 15)
|
494
|
+
|
495
|
+
state = Puppet::SSL::StateMachine::Wait.new(machine, ssl_context)
|
496
|
+
state.expects(:sleep).with(15)
|
497
|
+
|
498
|
+
expect(state.next_state).to be_an_instance_of(Puppet::SSL::StateMachine::NeedCACerts)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|