puppet 8.0.1-universal-darwin → 8.2.0-universal-darwin
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|