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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +5 -5
  3. data/Gemfile.lock +47 -39
  4. data/ext/project_data.yaml +1 -1
  5. data/lib/puppet/defaults.rb +37 -7
  6. data/lib/puppet/http/client.rb +12 -5
  7. data/lib/puppet/http/service/ca.rb +32 -2
  8. data/lib/puppet/node/environment.rb +6 -4
  9. data/lib/puppet/pops/evaluator/deferred_resolver.rb +20 -3
  10. data/lib/puppet/ssl/oids.rb +2 -0
  11. data/lib/puppet/ssl/ssl_provider.rb +1 -1
  12. data/lib/puppet/ssl/state_machine.rb +143 -14
  13. data/lib/puppet/thread_local.rb +1 -4
  14. data/lib/puppet/version.rb +1 -1
  15. data/lib/puppet/x509/cert_provider.rb +29 -0
  16. data/locales/puppet.pot +2346 -2310
  17. data/man/man5/puppet.conf.5 +31 -3
  18. data/man/man8/puppet-agent.8 +1 -1
  19. data/man/man8/puppet-apply.8 +1 -1
  20. data/man/man8/puppet-catalog.8 +1 -1
  21. data/man/man8/puppet-config.8 +1 -1
  22. data/man/man8/puppet-describe.8 +1 -1
  23. data/man/man8/puppet-device.8 +1 -1
  24. data/man/man8/puppet-doc.8 +1 -1
  25. data/man/man8/puppet-epp.8 +1 -1
  26. data/man/man8/puppet-facts.8 +1 -1
  27. data/man/man8/puppet-filebucket.8 +1 -1
  28. data/man/man8/puppet-generate.8 +1 -1
  29. data/man/man8/puppet-help.8 +1 -1
  30. data/man/man8/puppet-lookup.8 +1 -1
  31. data/man/man8/puppet-module.8 +1 -1
  32. data/man/man8/puppet-node.8 +1 -1
  33. data/man/man8/puppet-parser.8 +1 -1
  34. data/man/man8/puppet-plugin.8 +1 -1
  35. data/man/man8/puppet-report.8 +1 -1
  36. data/man/man8/puppet-resource.8 +1 -1
  37. data/man/man8/puppet-script.8 +1 -1
  38. data/man/man8/puppet-ssl.8 +1 -1
  39. data/man/man8/puppet.8 +2 -2
  40. data/spec/fixtures/ssl/127.0.0.1-key.pem +107 -107
  41. data/spec/fixtures/ssl/127.0.0.1.pem +52 -51
  42. data/spec/fixtures/ssl/bad-basic-constraints.pem +56 -56
  43. data/spec/fixtures/ssl/bad-int-basic-constraints.pem +53 -53
  44. data/spec/fixtures/ssl/ca.pem +54 -54
  45. data/spec/fixtures/ssl/crl.pem +26 -26
  46. data/spec/fixtures/ssl/ec-key.pem +11 -11
  47. data/spec/fixtures/ssl/ec.pem +33 -32
  48. data/spec/fixtures/ssl/encrypted-ec-key.pem +12 -12
  49. data/spec/fixtures/ssl/encrypted-key.pem +108 -108
  50. data/spec/fixtures/ssl/intermediate-agent-crl.pem +26 -26
  51. data/spec/fixtures/ssl/intermediate-agent.pem +56 -56
  52. data/spec/fixtures/ssl/intermediate-crl.pem +29 -29
  53. data/spec/fixtures/ssl/intermediate.pem +53 -53
  54. data/spec/fixtures/ssl/oid-key.pem +107 -107
  55. data/spec/fixtures/ssl/oid.pem +51 -50
  56. data/spec/fixtures/ssl/pluto-key.pem +107 -107
  57. data/spec/fixtures/ssl/pluto.pem +52 -51
  58. data/spec/fixtures/ssl/renewed.pem +67 -0
  59. data/spec/fixtures/ssl/request-key.pem +107 -107
  60. data/spec/fixtures/ssl/request.pem +50 -48
  61. data/spec/fixtures/ssl/revoked-key.pem +107 -107
  62. data/spec/fixtures/ssl/revoked.pem +51 -50
  63. data/spec/fixtures/ssl/signed-key.pem +107 -107
  64. data/spec/fixtures/ssl/signed.pem +49 -48
  65. data/spec/fixtures/ssl/tampered-cert.pem +51 -50
  66. data/spec/fixtures/ssl/tampered-csr.pem +50 -48
  67. data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +107 -107
  68. data/spec/fixtures/ssl/unknown-127.0.0.1.pem +50 -49
  69. data/spec/fixtures/ssl/unknown-ca-key.pem +107 -107
  70. data/spec/fixtures/ssl/unknown-ca.pem +54 -54
  71. data/spec/integration/application/agent_spec.rb +63 -13
  72. data/spec/integration/application/apply_spec.rb +14 -0
  73. data/spec/integration/http/client_spec.rb +16 -0
  74. data/spec/lib/puppet/test_ca.rb +3 -10
  75. data/spec/unit/application/lookup_spec.rb +1 -0
  76. data/spec/unit/defaults_spec.rb +2 -40
  77. data/spec/unit/file_system/path_pattern_spec.rb +15 -0
  78. data/spec/unit/http/service/ca_spec.rb +83 -0
  79. data/spec/unit/ssl/ssl_provider_spec.rb +20 -0
  80. data/spec/unit/ssl/state_machine_spec.rb +143 -3
  81. data/spec/unit/x509/cert_provider_spec.rb +49 -0
  82. data/tasks/generate_cert_fixtures.rake +4 -0
  83. 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 = Puppet::SSL::Digest.new(@machine.digest, pem).to_hex
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
- crl_ttl = Puppet[:crl_refresh_interval]
106
- if crl_ttl
107
- last_update = @cert_provider.crl_last_update
108
- now = Time.now
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
- return Done.new(@machine, next_ctx)
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 CA certs and CRLs.
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)
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require 'concurrent'
3
3
 
4
- # We want to use the pure Ruby implementation even on JRuby. If we use the Java
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
@@ -7,7 +7,7 @@
7
7
  # Raketasks and such to set the version based on the output of `git describe`
8
8
 
9
9
  module Puppet
10
- PUPPETVERSION = '8.0.1'
10
+ PUPPETVERSION = '8.2.0'
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and
@@ -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