puppet 8.0.1-universal-darwin → 8.2.0-universal-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODEOWNERS +5 -5
- data/Gemfile.lock +47 -39
- data/ext/project_data.yaml +1 -1
- data/lib/puppet/defaults.rb +37 -7
- data/lib/puppet/http/client.rb +12 -5
- data/lib/puppet/http/service/ca.rb +32 -2
- data/lib/puppet/node/environment.rb +6 -4
- data/lib/puppet/pops/evaluator/deferred_resolver.rb +20 -3
- data/lib/puppet/ssl/oids.rb +2 -0
- data/lib/puppet/ssl/ssl_provider.rb +1 -1
- data/lib/puppet/ssl/state_machine.rb +143 -14
- data/lib/puppet/thread_local.rb +1 -4
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet/x509/cert_provider.rb +29 -0
- data/locales/puppet.pot +2346 -2310
- data/man/man5/puppet.conf.5 +31 -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 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-lookup.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 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/ssl/127.0.0.1-key.pem +107 -107
- data/spec/fixtures/ssl/127.0.0.1.pem +52 -51
- data/spec/fixtures/ssl/bad-basic-constraints.pem +56 -56
- data/spec/fixtures/ssl/bad-int-basic-constraints.pem +53 -53
- data/spec/fixtures/ssl/ca.pem +54 -54
- data/spec/fixtures/ssl/crl.pem +26 -26
- data/spec/fixtures/ssl/ec-key.pem +11 -11
- data/spec/fixtures/ssl/ec.pem +33 -32
- data/spec/fixtures/ssl/encrypted-ec-key.pem +12 -12
- data/spec/fixtures/ssl/encrypted-key.pem +108 -108
- data/spec/fixtures/ssl/intermediate-agent-crl.pem +26 -26
- data/spec/fixtures/ssl/intermediate-agent.pem +56 -56
- data/spec/fixtures/ssl/intermediate-crl.pem +29 -29
- data/spec/fixtures/ssl/intermediate.pem +53 -53
- data/spec/fixtures/ssl/oid-key.pem +107 -107
- data/spec/fixtures/ssl/oid.pem +51 -50
- data/spec/fixtures/ssl/pluto-key.pem +107 -107
- data/spec/fixtures/ssl/pluto.pem +52 -51
- data/spec/fixtures/ssl/renewed.pem +67 -0
- data/spec/fixtures/ssl/request-key.pem +107 -107
- data/spec/fixtures/ssl/request.pem +50 -48
- data/spec/fixtures/ssl/revoked-key.pem +107 -107
- data/spec/fixtures/ssl/revoked.pem +51 -50
- data/spec/fixtures/ssl/signed-key.pem +107 -107
- data/spec/fixtures/ssl/signed.pem +49 -48
- data/spec/fixtures/ssl/tampered-cert.pem +51 -50
- data/spec/fixtures/ssl/tampered-csr.pem +50 -48
- data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +107 -107
- data/spec/fixtures/ssl/unknown-127.0.0.1.pem +50 -49
- data/spec/fixtures/ssl/unknown-ca-key.pem +107 -107
- data/spec/fixtures/ssl/unknown-ca.pem +54 -54
- data/spec/integration/application/agent_spec.rb +63 -13
- data/spec/integration/application/apply_spec.rb +14 -0
- data/spec/integration/http/client_spec.rb +16 -0
- data/spec/lib/puppet/test_ca.rb +3 -10
- data/spec/unit/application/lookup_spec.rb +1 -0
- data/spec/unit/defaults_spec.rb +2 -40
- data/spec/unit/file_system/path_pattern_spec.rb +15 -0
- data/spec/unit/http/service/ca_spec.rb +83 -0
- data/spec/unit/ssl/ssl_provider_spec.rb +20 -0
- data/spec/unit/ssl/state_machine_spec.rb +143 -3
- data/spec/unit/x509/cert_provider_spec.rb +49 -0
- data/tasks/generate_cert_fixtures.rake +4 -0
- metadata +5 -9
@@ -50,14 +50,25 @@ class Puppet::SSL::StateMachine
|
|
50
50
|
def next_state
|
51
51
|
Puppet.debug("Loading CA certs")
|
52
52
|
|
53
|
+
force_crl_refresh = false
|
54
|
+
|
53
55
|
cacerts = @cert_provider.load_cacerts
|
54
56
|
if cacerts
|
55
57
|
next_ctx = @ssl_provider.create_root_context(cacerts: cacerts, revocation: false)
|
58
|
+
|
59
|
+
now = Time.now
|
60
|
+
last_update = @cert_provider.ca_last_update
|
61
|
+
if needs_refresh?(now, last_update)
|
62
|
+
# If we refresh the CA, then we need to force the CRL to be refreshed too,
|
63
|
+
# since if there is a new CA in the chain, then we need its CRL to check
|
64
|
+
# the full chain for revocation status.
|
65
|
+
next_ctx, force_crl_refresh = refresh_ca(next_ctx, last_update)
|
66
|
+
end
|
56
67
|
else
|
57
68
|
route = @machine.session.route_to(:ca, ssl_context: @ssl_context)
|
58
69
|
_, pem = route.get_certificate(Puppet::SSL::CA_NAME, ssl_context: @ssl_context)
|
59
70
|
if @machine.ca_fingerprint
|
60
|
-
actual_digest =
|
71
|
+
actual_digest = @machine.digest_as_hex(pem)
|
61
72
|
expected_digest = @machine.ca_fingerprint.scan(/../).join(':').upcase
|
62
73
|
if actual_digest == expected_digest
|
63
74
|
Puppet.info(_("Verified CA bundle with digest (%{digest_type}) %{actual_digest}") %
|
@@ -74,7 +85,7 @@ class Puppet::SSL::StateMachine
|
|
74
85
|
@cert_provider.save_cacerts(cacerts)
|
75
86
|
end
|
76
87
|
|
77
|
-
NeedCRLs.new(@machine, next_ctx)
|
88
|
+
NeedCRLs.new(@machine, next_ctx, force_crl_refresh)
|
78
89
|
rescue OpenSSL::X509::CertificateError => e
|
79
90
|
Error.new(@machine, e.message, e)
|
80
91
|
rescue Puppet::HTTP::ResponseError => e
|
@@ -84,6 +95,56 @@ class Puppet::SSL::StateMachine
|
|
84
95
|
to_error(_('Could not download CA certificate: %{message}') % { message: e.message }, e)
|
85
96
|
end
|
86
97
|
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def needs_refresh?(now, last_update)
|
102
|
+
return true if last_update.nil?
|
103
|
+
|
104
|
+
ca_ttl = Puppet[:ca_refresh_interval]
|
105
|
+
return false unless ca_ttl
|
106
|
+
|
107
|
+
now.to_i > last_update.to_i + ca_ttl
|
108
|
+
end
|
109
|
+
|
110
|
+
def refresh_ca(ssl_ctx, last_update)
|
111
|
+
Puppet.info(_("Refreshing CA certificate"))
|
112
|
+
|
113
|
+
# return the next_ctx containing the updated ca
|
114
|
+
next_ctx = [download_ca(ssl_ctx, last_update), true]
|
115
|
+
|
116
|
+
# After a successful refresh, update ca_last_update
|
117
|
+
@cert_provider.ca_last_update = Time.now
|
118
|
+
|
119
|
+
next_ctx
|
120
|
+
rescue Puppet::HTTP::ResponseError => e
|
121
|
+
if e.response.code == 304
|
122
|
+
Puppet.info(_("CA certificate is unmodified, using existing CA certificate"))
|
123
|
+
else
|
124
|
+
Puppet.info(_("Failed to refresh CA certificate, using existing CA certificate: %{message}") % {message: e.message})
|
125
|
+
end
|
126
|
+
|
127
|
+
# return the original ssl_ctx
|
128
|
+
[ssl_ctx, false]
|
129
|
+
rescue Puppet::HTTP::HTTPError => e
|
130
|
+
Puppet.warning(_("Failed to refresh CA certificate, using existing CA certificate: %{message}") % {message: e.message})
|
131
|
+
|
132
|
+
# return the original ssl_ctx
|
133
|
+
[ssl_ctx, false]
|
134
|
+
end
|
135
|
+
|
136
|
+
def download_ca(ssl_ctx, last_update)
|
137
|
+
route = @machine.session.route_to(:ca, ssl_context: ssl_ctx)
|
138
|
+
_, pem = route.get_certificate(Puppet::SSL::CA_NAME, if_modified_since: last_update, ssl_context: ssl_ctx)
|
139
|
+
cacerts = @cert_provider.load_cacerts_from_pem(pem)
|
140
|
+
# verify cacerts before saving
|
141
|
+
next_ctx = @ssl_provider.create_root_context(cacerts: cacerts, revocation: false)
|
142
|
+
@cert_provider.save_cacerts(cacerts)
|
143
|
+
|
144
|
+
Puppet.info("Refreshed CA certificate: #{@machine.digest_as_hex(pem)}")
|
145
|
+
|
146
|
+
next_ctx
|
147
|
+
end
|
87
148
|
end
|
88
149
|
|
89
150
|
# If revocation is enabled, load CRLs or download them, using the CA bundle
|
@@ -93,6 +154,13 @@ class Puppet::SSL::StateMachine
|
|
93
154
|
# for which we don't have a CRL
|
94
155
|
#
|
95
156
|
class NeedCRLs < SSLState
|
157
|
+
attr_reader :force_crl_refresh
|
158
|
+
|
159
|
+
def initialize(machine, ssl_context, force_crl_refresh = false)
|
160
|
+
super(machine, ssl_context)
|
161
|
+
@force_crl_refresh = force_crl_refresh
|
162
|
+
end
|
163
|
+
|
96
164
|
def next_state
|
97
165
|
Puppet.debug("Loading CRLs")
|
98
166
|
|
@@ -102,15 +170,10 @@ class Puppet::SSL::StateMachine
|
|
102
170
|
if crls
|
103
171
|
next_ctx = @ssl_provider.create_root_context(cacerts: ssl_context[:cacerts], crls: crls)
|
104
172
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
if last_update.nil? || now.to_i > last_update.to_i + crl_ttl
|
110
|
-
# set last updated time first, then make a best effort to refresh
|
111
|
-
@cert_provider.crl_last_update = now
|
112
|
-
next_ctx = refresh_crl(next_ctx, last_update)
|
113
|
-
end
|
173
|
+
now = Time.now
|
174
|
+
last_update = @cert_provider.crl_last_update
|
175
|
+
if needs_refresh?(now, last_update)
|
176
|
+
next_ctx = refresh_crl(next_ctx, last_update)
|
114
177
|
end
|
115
178
|
else
|
116
179
|
next_ctx = download_crl(@ssl_context, nil)
|
@@ -133,11 +196,25 @@ class Puppet::SSL::StateMachine
|
|
133
196
|
|
134
197
|
private
|
135
198
|
|
199
|
+
def needs_refresh?(now, last_update)
|
200
|
+
return true if @force_crl_refresh || last_update.nil?
|
201
|
+
|
202
|
+
crl_ttl = Puppet[:crl_refresh_interval]
|
203
|
+
return false unless crl_ttl
|
204
|
+
|
205
|
+
now.to_i > last_update.to_i + crl_ttl
|
206
|
+
end
|
207
|
+
|
136
208
|
def refresh_crl(ssl_ctx, last_update)
|
137
209
|
Puppet.info(_("Refreshing CRL"))
|
138
210
|
|
139
211
|
# return the next_ctx containing the updated crl
|
140
|
-
download_crl(ssl_ctx, last_update)
|
212
|
+
next_ctx = download_crl(ssl_ctx, last_update)
|
213
|
+
|
214
|
+
# After a successful refresh, update crl_last_update
|
215
|
+
@cert_provider.crl_last_update = Time.now
|
216
|
+
|
217
|
+
next_ctx
|
141
218
|
rescue Puppet::HTTP::ResponseError => e
|
142
219
|
if e.response.code == 304
|
143
220
|
Puppet.info(_("CRL is unmodified, using existing CRL"))
|
@@ -162,6 +239,8 @@ class Puppet::SSL::StateMachine
|
|
162
239
|
next_ctx = @ssl_provider.create_root_context(cacerts: ssl_ctx[:cacerts], crls: crls)
|
163
240
|
@cert_provider.save_crls(crls)
|
164
241
|
|
242
|
+
Puppet.info("Refreshed CRL: #{@machine.digest_as_hex(pem)}")
|
243
|
+
|
165
244
|
next_ctx
|
166
245
|
end
|
167
246
|
end
|
@@ -183,7 +262,11 @@ class Puppet::SSL::StateMachine
|
|
183
262
|
next_ctx = @ssl_provider.create_context(
|
184
263
|
cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: key, client_cert: cert
|
185
264
|
)
|
186
|
-
|
265
|
+
if needs_refresh?(cert)
|
266
|
+
return NeedRenewedCert.new(@machine, next_ctx, key)
|
267
|
+
else
|
268
|
+
return Done.new(@machine, next_ctx)
|
269
|
+
end
|
187
270
|
end
|
188
271
|
else
|
189
272
|
if Puppet[:key_type] == 'ec'
|
@@ -199,6 +282,15 @@ class Puppet::SSL::StateMachine
|
|
199
282
|
|
200
283
|
NeedSubmitCSR.new(@machine, @ssl_context, key)
|
201
284
|
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def needs_refresh?(cert)
|
289
|
+
cert_ttl = Puppet[:hostcert_renewal_interval]
|
290
|
+
return false unless cert_ttl
|
291
|
+
|
292
|
+
Time.now.to_i >= (cert.not_after.to_i - cert_ttl)
|
293
|
+
end
|
202
294
|
end
|
203
295
|
|
204
296
|
# Base class for states with a private key.
|
@@ -270,6 +362,39 @@ class Puppet::SSL::StateMachine
|
|
270
362
|
end
|
271
363
|
end
|
272
364
|
|
365
|
+
# Class to renew a client/host certificate automatically.
|
366
|
+
#
|
367
|
+
class NeedRenewedCert < KeySSLState
|
368
|
+
def next_state
|
369
|
+
Puppet.debug(_("Renewing client certificate"))
|
370
|
+
|
371
|
+
route = @machine.session.route_to(:ca, ssl_context: @ssl_context)
|
372
|
+
cert = OpenSSL::X509::Certificate.new(
|
373
|
+
route.post_certificate_renewal(@ssl_context)[1]
|
374
|
+
)
|
375
|
+
|
376
|
+
# verify client cert before saving
|
377
|
+
next_ctx = @ssl_provider.create_context(
|
378
|
+
cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: @private_key, client_cert: cert
|
379
|
+
)
|
380
|
+
@cert_provider.save_client_cert(Puppet[:certname], cert)
|
381
|
+
|
382
|
+
Puppet.info(_("Renewed client certificate: %{cert_digest}, not before '%{not_before}', not after '%{not_after}'") % { cert_digest: @machine.digest_as_hex(cert.to_pem), not_before: cert.not_before, not_after: cert.not_after })
|
383
|
+
|
384
|
+
Done.new(@machine, next_ctx)
|
385
|
+
rescue Puppet::HTTP::ResponseError => e
|
386
|
+
if e.response.code == 404
|
387
|
+
Puppet.info(_("Certificate autorenewal has not been enabled on the server."))
|
388
|
+
else
|
389
|
+
Puppet.warning(_("Failed to automatically renew certificate: %{code} %{reason}") % { code: e.response.code, reason: e.response.reason })
|
390
|
+
end
|
391
|
+
Done.new(@machine, @ssl_context)
|
392
|
+
rescue => e
|
393
|
+
Puppet.warning(_("Unable to automatically renew certificate: %{message}") % { message: e })
|
394
|
+
Done.new(@machine, @ssl_context)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
273
398
|
# We cannot make progress, so wait if allowed to do so, or exit.
|
274
399
|
#
|
275
400
|
class Wait < SSLState
|
@@ -421,7 +546,7 @@ class Puppet::SSL::StateMachine
|
|
421
546
|
final_state.ssl_context
|
422
547
|
end
|
423
548
|
|
424
|
-
# Run the state machine for
|
549
|
+
# Run the state machine for client certs.
|
425
550
|
#
|
426
551
|
# @return [Puppet::SSL::SSLContext] initialized SSLContext
|
427
552
|
# @raise [Puppet::Error] If we fail to generate an SSLContext
|
@@ -441,6 +566,10 @@ class Puppet::SSL::StateMachine
|
|
441
566
|
@lockfile.unlock
|
442
567
|
end
|
443
568
|
|
569
|
+
def digest_as_hex(str)
|
570
|
+
Puppet::SSL::Digest.new(digest, str).to_hex
|
571
|
+
end
|
572
|
+
|
444
573
|
private
|
445
574
|
|
446
575
|
def run_machine(state, stop)
|
data/lib/puppet/thread_local.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'concurrent'
|
3
3
|
|
4
|
-
|
5
|
-
# implementation of ThreadLocal, we end up leaking references to JRuby instances
|
6
|
-
# and preventing them from being garbage collected.
|
7
|
-
class Puppet::ThreadLocal < Concurrent::RubyThreadLocalVar
|
4
|
+
class Puppet::ThreadLocal < Concurrent::ThreadLocalVar
|
8
5
|
end
|
data/lib/puppet/version.rb
CHANGED
@@ -147,6 +147,28 @@ class Puppet::X509::CertProvider
|
|
147
147
|
Puppet::FileSystem.touch(@crlpath, mtime: time)
|
148
148
|
end
|
149
149
|
|
150
|
+
# Return the time when the CA bundle was last updated.
|
151
|
+
#
|
152
|
+
# @return [Time, nil] Time when the CA bundle was last updated, or nil if we don't
|
153
|
+
# have a CA bundle
|
154
|
+
#
|
155
|
+
# @api private
|
156
|
+
def ca_last_update
|
157
|
+
stat = Puppet::FileSystem.stat(@capath)
|
158
|
+
Time.at(stat.mtime)
|
159
|
+
rescue Errno::ENOENT
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# Set the CA bundle last updated time.
|
164
|
+
#
|
165
|
+
# @param time [Time] The last updated time
|
166
|
+
#
|
167
|
+
# @api private
|
168
|
+
def ca_last_update=(time)
|
169
|
+
Puppet::FileSystem.touch(@capath, mtime: time)
|
170
|
+
end
|
171
|
+
|
150
172
|
# Save named private key in the configured `privatekeydir`. For
|
151
173
|
# historical reasons, names are case insensitive.
|
152
174
|
#
|
@@ -289,6 +311,13 @@ class Puppet::X509::CertProvider
|
|
289
311
|
options[:extension_requests] = csr_attributes.extension_requests
|
290
312
|
end
|
291
313
|
|
314
|
+
# Adds auto-renew attribute to CSR if the agent supports auto-renewal of
|
315
|
+
# certificates
|
316
|
+
if Puppet[:hostcert_renewal_interval] && Puppet[:hostcert_renewal_interval] > 0
|
317
|
+
options[:csr_attributes] ||= {}
|
318
|
+
options[:csr_attributes].merge!({'1.3.6.1.4.1.34380.1.3.2' => 'true'})
|
319
|
+
end
|
320
|
+
|
292
321
|
csr = Puppet::SSL::CertificateRequest.new(name)
|
293
322
|
csr.generate(private_key, options)
|
294
323
|
end
|