puppet 8.1.0-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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +20 -20
  3. data/lib/puppet/defaults.rb +17 -5
  4. data/lib/puppet/http/client.rb +12 -5
  5. data/lib/puppet/http/service/ca.rb +25 -0
  6. data/lib/puppet/node/environment.rb +6 -4
  7. data/lib/puppet/pops/evaluator/deferred_resolver.rb +20 -3
  8. data/lib/puppet/ssl/oids.rb +2 -0
  9. data/lib/puppet/ssl/ssl_provider.rb +1 -1
  10. data/lib/puppet/ssl/state_machine.rb +60 -9
  11. data/lib/puppet/version.rb +1 -1
  12. data/lib/puppet/x509/cert_provider.rb +7 -0
  13. data/locales/puppet.pot +71 -47
  14. data/man/man5/puppet.conf.5 +16 -2
  15. data/man/man8/puppet-agent.8 +1 -1
  16. data/man/man8/puppet-apply.8 +1 -1
  17. data/man/man8/puppet-catalog.8 +1 -1
  18. data/man/man8/puppet-config.8 +1 -1
  19. data/man/man8/puppet-describe.8 +1 -1
  20. data/man/man8/puppet-device.8 +1 -1
  21. data/man/man8/puppet-doc.8 +1 -1
  22. data/man/man8/puppet-epp.8 +1 -1
  23. data/man/man8/puppet-facts.8 +1 -1
  24. data/man/man8/puppet-filebucket.8 +1 -1
  25. data/man/man8/puppet-generate.8 +1 -1
  26. data/man/man8/puppet-help.8 +1 -1
  27. data/man/man8/puppet-lookup.8 +1 -1
  28. data/man/man8/puppet-module.8 +1 -1
  29. data/man/man8/puppet-node.8 +1 -1
  30. data/man/man8/puppet-parser.8 +1 -1
  31. data/man/man8/puppet-plugin.8 +1 -1
  32. data/man/man8/puppet-report.8 +1 -1
  33. data/man/man8/puppet-resource.8 +1 -1
  34. data/man/man8/puppet-script.8 +1 -1
  35. data/man/man8/puppet-ssl.8 +1 -1
  36. data/man/man8/puppet.8 +2 -2
  37. data/spec/fixtures/ssl/127.0.0.1-key.pem +107 -107
  38. data/spec/fixtures/ssl/127.0.0.1.pem +52 -51
  39. data/spec/fixtures/ssl/bad-basic-constraints.pem +56 -56
  40. data/spec/fixtures/ssl/bad-int-basic-constraints.pem +53 -53
  41. data/spec/fixtures/ssl/ca.pem +54 -54
  42. data/spec/fixtures/ssl/crl.pem +26 -26
  43. data/spec/fixtures/ssl/ec-key.pem +11 -11
  44. data/spec/fixtures/ssl/ec.pem +33 -32
  45. data/spec/fixtures/ssl/encrypted-ec-key.pem +12 -12
  46. data/spec/fixtures/ssl/encrypted-key.pem +108 -108
  47. data/spec/fixtures/ssl/intermediate-agent-crl.pem +26 -26
  48. data/spec/fixtures/ssl/intermediate-agent.pem +56 -56
  49. data/spec/fixtures/ssl/intermediate-crl.pem +29 -29
  50. data/spec/fixtures/ssl/intermediate.pem +53 -53
  51. data/spec/fixtures/ssl/oid-key.pem +107 -107
  52. data/spec/fixtures/ssl/oid.pem +51 -50
  53. data/spec/fixtures/ssl/pluto-key.pem +107 -107
  54. data/spec/fixtures/ssl/pluto.pem +52 -51
  55. data/spec/fixtures/ssl/renewed.pem +67 -0
  56. data/spec/fixtures/ssl/request-key.pem +107 -107
  57. data/spec/fixtures/ssl/request.pem +50 -48
  58. data/spec/fixtures/ssl/revoked-key.pem +107 -107
  59. data/spec/fixtures/ssl/revoked.pem +51 -50
  60. data/spec/fixtures/ssl/signed-key.pem +107 -107
  61. data/spec/fixtures/ssl/signed.pem +49 -48
  62. data/spec/fixtures/ssl/tampered-cert.pem +51 -50
  63. data/spec/fixtures/ssl/tampered-csr.pem +50 -48
  64. data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +107 -107
  65. data/spec/fixtures/ssl/unknown-127.0.0.1.pem +50 -49
  66. data/spec/fixtures/ssl/unknown-ca-key.pem +107 -107
  67. data/spec/fixtures/ssl/unknown-ca.pem +54 -54
  68. data/spec/integration/application/agent_spec.rb +27 -27
  69. data/spec/integration/application/apply_spec.rb +14 -0
  70. data/spec/integration/http/client_spec.rb +16 -0
  71. data/spec/lib/puppet/test_ca.rb +3 -10
  72. data/spec/unit/defaults_spec.rb +2 -40
  73. data/spec/unit/file_system/path_pattern_spec.rb +15 -0
  74. data/spec/unit/http/service/ca_spec.rb +71 -0
  75. data/spec/unit/ssl/ssl_provider_spec.rb +20 -0
  76. data/spec/unit/ssl/state_machine_spec.rb +75 -3
  77. data/spec/unit/x509/cert_provider_spec.rb +23 -0
  78. data/tasks/generate_cert_fixtures.rake +4 -0
  79. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7eea5fdf57cadd5d893dc4324a16144079bfb11686d4fe50ba34b0c8b9206ffd
4
- data.tar.gz: 1186f83bde61e09ef1bbbfca662481e474896fe61550df9bdb9546fac8d9f524
3
+ metadata.gz: a007559506837db8fff3be2557fe949b928b52c8d13e5d8e12957391263e4efe
4
+ data.tar.gz: ae6866634e1e346ef8a28a964cfa1552c02f8afe21ca4894682cc850656afb1c
5
5
  SHA512:
6
- metadata.gz: 3ec9291458052ad337ff5d91808694a842152be78fef074fa50b9345dab18bfefc557d8b879295ad0d6f063b970f48e8bae0726950e6e5a9b8beaf068d2f3fb5
7
- data.tar.gz: 58f214114d5d8d0e7f4bace9a31618a5c9b44da188a4ab8f753295c743287f35c4f28baa82abd8c858a01f0cdb7a175fa019e4b1459bb8e88a1b3d394944f4a9
6
+ metadata.gz: 80c8d4b7774956f7fe5ba2a4aa0fcbffffab695d3517f91473bbde7cac509dc8b8f67317f499182cbd3f9b7d8f86d2f33de0363d936e74f012493f100dd0d199
7
+ data.tar.gz: d826de6788fbe4fb802c2ff1be076783669a6c257ba2332ceca5d3e2bae64a576a4a5f8bcccab1f23f983b5b0bd4b2624106de27e6f35199d2beff79e5c6e94d
data/Gemfile.lock CHANGED
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: https://github.com/puppetlabs/packaging
3
- revision: 87a3396077f06e2341ad19e6fcd15f7c14ec02f9
3
+ revision: affecba5dfacc5862fc7199895ccf11b69153570
4
4
  branch: 1.0.x
5
5
  specs:
6
6
  packaging (0)
@@ -15,7 +15,7 @@ GIT
15
15
  PATH
16
16
  remote: .
17
17
  specs:
18
- puppet (8.1.0)
18
+ puppet (8.2.0)
19
19
  CFPropertyList (~> 2.2)
20
20
  concurrent-ruby (~> 1.0)
21
21
  deep_merge (~> 1.0)
@@ -31,7 +31,7 @@ GEM
31
31
  remote: https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/
32
32
  specs:
33
33
  CFPropertyList (2.3.6)
34
- addressable (2.8.4)
34
+ addressable (2.8.5)
35
35
  public_suffix (>= 2.0.2, < 6.0)
36
36
  apt_stage_artifacts (0.11.0)
37
37
  docopt
@@ -41,25 +41,25 @@ GEM
41
41
  concurrent-ruby (1.2.2)
42
42
  crack (0.4.5)
43
43
  rexml
44
- csv (3.2.6)
44
+ csv (3.2.7)
45
45
  declarative (0.0.20)
46
46
  deep_merge (1.2.2)
47
47
  diff-lcs (1.5.0)
48
- digest-crc (0.6.4)
48
+ digest-crc (0.6.5)
49
49
  rake (>= 12.0.0, < 14.0.0)
50
50
  docopt (0.6.1)
51
51
  erubi (1.12.0)
52
- facter (4.4.0)
52
+ facter (4.4.2)
53
53
  hocon (~> 1.3)
54
54
  thor (>= 1.0.1, < 2.0)
55
- faraday (2.7.6)
55
+ faraday (2.7.10)
56
56
  faraday-net_http (>= 2.0, < 3.1)
57
57
  ruby2_keywords (>= 0.0.4)
58
58
  faraday-net_http (3.0.2)
59
59
  fast_gettext (2.3.0)
60
60
  ffi (1.15.5)
61
61
  forwardable (1.3.3)
62
- gettext (3.4.4)
62
+ gettext (3.4.7)
63
63
  erubi
64
64
  locale (>= 2.0.5)
65
65
  prime
@@ -69,7 +69,7 @@ GEM
69
69
  fast_gettext (~> 2.1)
70
70
  gettext (~> 3.4)
71
71
  locale
72
- google-apis-core (0.11.0)
72
+ google-apis-core (0.11.1)
73
73
  addressable (~> 2.5, >= 2.5.1)
74
74
  googleauth (>= 0.16.2, < 2.a)
75
75
  httpclient (>= 2.8.1, < 3.a)
@@ -96,7 +96,7 @@ GEM
96
96
  google-cloud-core (~> 1.6)
97
97
  googleauth (>= 0.16.2, < 2.a)
98
98
  mini_mime (~> 1.0)
99
- googleauth (1.5.2)
99
+ googleauth (1.7.0)
100
100
  faraday (>= 0.17.3, < 3.a)
101
101
  jwt (>= 1.4, < 3.0)
102
102
  memoist (~> 0.16)
@@ -118,12 +118,12 @@ GEM
118
118
  memoist (0.16.2)
119
119
  memory_profiler (1.0.1)
120
120
  method_source (1.0.0)
121
- mini_mime (1.1.2)
121
+ mini_mime (1.1.5)
122
122
  minitar (0.9)
123
- msgpack (1.7.1)
123
+ msgpack (1.7.2)
124
124
  multi_json (1.15.0)
125
125
  mustache (1.1.1)
126
- optimist (3.0.1)
126
+ optimist (3.1.0)
127
127
  os (1.1.4)
128
128
  parallel (1.23.0)
129
129
  parser (3.2.2.3)
@@ -135,15 +135,15 @@ GEM
135
135
  pry (0.14.2)
136
136
  coderay (~> 1.1)
137
137
  method_source (~> 1.0)
138
- public_suffix (5.0.1)
139
- puppet-resource_api (1.8.14)
138
+ public_suffix (5.0.3)
139
+ puppet-resource_api (1.9.0)
140
140
  hocon (>= 1.0)
141
141
  puppetserver-ca (2.6.0)
142
142
  facter (>= 2.0.1, < 5)
143
143
  racc (1.5.2)
144
144
  rainbow (3.1.1)
145
145
  rake (13.0.6)
146
- rdiscount (2.2.7)
146
+ rdiscount (2.2.7.1)
147
147
  rdoc (6.3.3)
148
148
  regexp_parser (2.8.1)
149
149
  release-metrics (1.1.0)
@@ -154,7 +154,7 @@ GEM
154
154
  trailblazer-option (>= 0.1.1, < 0.2.0)
155
155
  uber (< 0.2.0)
156
156
  retriable (3.1.2)
157
- rexml (3.2.5)
157
+ rexml (3.2.6)
158
158
  ronn (0.7.3)
159
159
  hpricot (>= 0.8.2)
160
160
  mustache (>= 0.7.0)
@@ -171,10 +171,10 @@ GEM
171
171
  rspec-its (1.3.0)
172
172
  rspec-core (>= 3.0.0)
173
173
  rspec-expectations (>= 3.0.0)
174
- rspec-mocks (3.12.5)
174
+ rspec-mocks (3.12.6)
175
175
  diff-lcs (>= 1.2.0, < 2.0)
176
176
  rspec-support (~> 3.12.0)
177
- rspec-support (3.12.0)
177
+ rspec-support (3.12.1)
178
178
  rubocop (1.28.0)
179
179
  parallel (~> 1.10)
180
180
  parser (>= 3.1.0.0)
@@ -204,7 +204,7 @@ GEM
204
204
  trailblazer-option (0.1.2)
205
205
  uber (0.1.0)
206
206
  unicode-display_width (2.4.2)
207
- vcr (6.1.0)
207
+ vcr (6.2.0)
208
208
  webmock (3.18.1)
209
209
  addressable (>= 2.8.0)
210
210
  crack (>= 0.3.2)
@@ -4,11 +4,7 @@ require_relative '../puppet/util/platform'
4
4
  module Puppet
5
5
 
6
6
  def self.default_diffargs
7
- if (Puppet.runtime[:facter].value(:kernel) == "AIX" && Puppet.runtime[:facter].value(:kernelmajversion) == "5300")
8
- ""
9
- else
10
- "-u"
11
- end
7
+ '-u'
12
8
  end
13
9
 
14
10
  def self.default_digest_algorithm
@@ -1248,6 +1244,22 @@ EOT
1248
1244
  unchanged on the server, then the agent run will continue using the
1249
1245
  local CRL it already has.#{AS_DURATION}",
1250
1246
  },
1247
+ :hostcert_renewal_interval => {
1248
+ :default => "30d",
1249
+ :type => :duration,
1250
+ :desc => "How often the Puppet agent refreshes its client certificate.
1251
+ By default the client certificate is refreshed once every 30 days. If
1252
+ a different duration is specified, then the agent will refresh its
1253
+ client certificate whenever it next runs and the elapsed time since the
1254
+ client certificate was last refreshed exceeds the duration.
1255
+
1256
+ In general, the duration should be greater than the `runinterval`.
1257
+ Setting it to 0 will disable automatic renewal.
1258
+
1259
+ If the agent downloads a new certificate, the agent will use it for subsequent
1260
+ network requests. If the refresh request fails, then the agent run will continue using the
1261
+ certificate it already has. #{AS_DURATION}",
1262
+ },
1251
1263
  :keylength => {
1252
1264
  :default => 4096,
1253
1265
  :type => :integer,
@@ -368,6 +368,7 @@ class Puppet::HTTP::Client
368
368
  apply_auth(request, basic_auth) if redirects.zero?
369
369
 
370
370
  # don't call return within the `request` block
371
+ close_and_sleep = nil
371
372
  http.request(request) do |nethttp|
372
373
  response = Puppet::HTTP::ResponseNetHTTP.new(request.uri, nethttp)
373
374
  begin
@@ -381,12 +382,14 @@ class Puppet::HTTP::Client
381
382
  interval = @retry_after_handler.retry_after_interval(request, response, retries)
382
383
  retries += 1
383
384
  if interval
384
- if http.started?
385
- Puppet.debug("Closing connection for #{Puppet::HTTP::Site.from_uri(request.uri)}")
386
- http.finish
385
+ close_and_sleep = proc do
386
+ if http.started?
387
+ Puppet.debug("Closing connection for #{Puppet::HTTP::Site.from_uri(request.uri)}")
388
+ http.finish
389
+ end
390
+ Puppet.warning(_("Sleeping for %{interval} seconds before retrying the request") % { interval: interval })
391
+ ::Kernel.sleep(interval)
387
392
  end
388
- Puppet.warning(_("Sleeping for %{interval} seconds before retrying the request") % { interval: interval })
389
- ::Kernel.sleep(interval)
390
393
  next
391
394
  end
392
395
  end
@@ -405,6 +408,10 @@ class Puppet::HTTP::Client
405
408
 
406
409
  done = true
407
410
  end
411
+ ensure
412
+ # If a server responded with a retry, make sure the connection is closed and then
413
+ # sleep the specified time.
414
+ close_and_sleep.call if close_and_sleep
408
415
  end
409
416
  end
410
417
 
@@ -104,4 +104,29 @@ class Puppet::HTTP::Service::Ca < Puppet::HTTP::Service
104
104
 
105
105
  response
106
106
  end
107
+
108
+ # Submit a POST request to send a certificate renewal request to the server
109
+ #
110
+ # @param [Puppet::SSL::SSLContext] ssl_context
111
+ #
112
+ # @return [Array<Puppet::HTTP::Response, String>] The request response
113
+ #
114
+ # @api public
115
+ def post_certificate_renewal(ssl_context)
116
+ headers = add_puppet_headers(HEADERS)
117
+ headers['Content-Type'] = 'text/plain'
118
+
119
+ response = @client.post(
120
+ with_base_url('/certificate_renewal'),
121
+ '', # Puppet::HTTP::Client.post requires a body, the API endpoint does not
122
+ headers: headers,
123
+ options: {ssl_context: ssl_context}
124
+ )
125
+
126
+ raise ArgumentError.new(_('SSL context must contain a client certificate.')) unless ssl_context.client_cert
127
+
128
+ process_response(response)
129
+
130
+ [response, response.body.to_s]
131
+ end
107
132
  end
@@ -592,10 +592,12 @@ class Puppet::Node::Environment
592
592
  if file == NO_MANIFEST
593
593
  empty_parse_result
594
594
  elsif File.directory?(file)
595
- parse_results = Puppet::FileSystem::PathPattern.absolute(File.join(file, '**/*.pp')).glob.sort.map do | file_to_parse |
596
- parser.file = file_to_parse
597
- parser.parse
598
- end
595
+ # JRuby does not properly perform Dir.glob operations with wildcards, (see PUP-11788 and https://github.com/jruby/jruby/issues/7836).
596
+ # We sort the results because Dir.glob order is inconsistent in Ruby < 3 (see PUP-10115).
597
+ parse_results = Puppet::FileSystem::PathPattern.absolute(File.join(file, '**/*')).glob.select {|globbed_file| globbed_file.end_with?('.pp')}.sort.map do | file_to_parse |
598
+ parser.file = file_to_parse
599
+ parser.parse
600
+ end
599
601
  # Use a parser type specific merger to concatenate the results
600
602
  Puppet::Parser::AST::Hostclass.new('', :code => Puppet::Parser::ParserFactory.code_merger.concatenate(parse_results))
601
603
  else
@@ -10,7 +10,13 @@ class DeferredValue
10
10
  end
11
11
 
12
12
  def resolve
13
- @proc.call
13
+ val = @proc.call
14
+ # Deferred sensitive values will be marked as such in resolve_futures()
15
+ if val.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
16
+ val.unwrap
17
+ else
18
+ val
19
+ end
14
20
  end
15
21
  end
16
22
 
@@ -88,8 +94,12 @@ class DeferredResolver
88
94
  #
89
95
  if resolved.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
90
96
  resolved = resolved.unwrap
91
- unless r.sensitive_parameters.include?(k.to_sym)
92
- r.sensitive_parameters = (r.sensitive_parameters + [k.to_sym]).freeze
97
+ mark_sensitive_parameters(r, k)
98
+ # If the value is a DeferredValue and it has an argument of type PSensitiveType, mark it as sensitive
99
+ # The DeferredValue.resolve method will unwrap it during catalog application
100
+ elsif resolved.is_a?(Puppet::Pops::Evaluator::DeferredValue)
101
+ if v.arguments.any? {|arg| arg.is_a?(Puppet::Pops::Types::PSensitiveType)}
102
+ mark_sensitive_parameters(r, k)
93
103
  end
94
104
  end
95
105
  overrides[ k ] = resolved
@@ -98,6 +108,13 @@ class DeferredResolver
98
108
  end
99
109
  end
100
110
 
111
+ def mark_sensitive_parameters(r, k)
112
+ unless r.sensitive_parameters.include?(k.to_sym)
113
+ r.sensitive_parameters = (r.sensitive_parameters + [k.to_sym]).freeze
114
+ end
115
+ end
116
+ private :mark_sensitive_parameters
117
+
101
118
  def resolve(x)
102
119
  if x.class == @deferred_class
103
120
  resolve_future(x)
@@ -71,7 +71,9 @@ module Puppet::SSL::Oids
71
71
  ["1.3.6.1.4.1.34380.1.3", 'ppAuthCertExt', 'Puppet Certificate Authorization Extension'],
72
72
 
73
73
  ["1.3.6.1.4.1.34380.1.3.1", 'pp_authorization', 'Certificate Extension Authorization'],
74
+ ["1.3.6.1.4.1.34380.1.3.2", 'pp_auth_auto_renew', 'Auto-Renew Certificate Attribute'],
74
75
  ["1.3.6.1.4.1.34380.1.3.13", 'pp_auth_role', 'Puppet Node Role Name for Authorization'],
76
+ ["1.3.6.1.4.1.34380.1.3.39", 'pp_cli_auth', 'Puppetserver CA CLI Authorization'],
75
77
  ]
76
78
 
77
79
  @did_register_puppet_oids = false
@@ -225,7 +225,7 @@ class Puppet::SSL::SSLProvider
225
225
  ssl_context.crls.each do |crl|
226
226
  oid_values = Hash[crl.extensions.map { |ext| [ext.oid, ext.value] }]
227
227
  crlNumber = oid_values['crlNumber'] || 'unknown'
228
- authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp!
228
+ authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp
229
229
  Puppet.debug("Using CRL '#{crl.issuer.to_utf8}' authorityKeyIdentifier '#{authKeyId}' crlNumber '#{crlNumber }'")
230
230
  end
231
231
  end
@@ -59,9 +59,6 @@ class Puppet::SSL::StateMachine
59
59
  now = Time.now
60
60
  last_update = @cert_provider.ca_last_update
61
61
  if needs_refresh?(now, last_update)
62
- # set last updated time first, then make a best effort to refresh
63
- @cert_provider.ca_last_update = now
64
-
65
62
  # If we refresh the CA, then we need to force the CRL to be refreshed too,
66
63
  # since if there is a new CA in the chain, then we need its CRL to check
67
64
  # the full chain for revocation status.
@@ -114,7 +111,12 @@ class Puppet::SSL::StateMachine
114
111
  Puppet.info(_("Refreshing CA certificate"))
115
112
 
116
113
  # return the next_ctx containing the updated ca
117
- [download_ca(ssl_ctx, last_update), true]
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
118
120
  rescue Puppet::HTTP::ResponseError => e
119
121
  if e.response.code == 304
120
122
  Puppet.info(_("CA certificate is unmodified, using existing CA certificate"))
@@ -171,8 +173,6 @@ class Puppet::SSL::StateMachine
171
173
  now = Time.now
172
174
  last_update = @cert_provider.crl_last_update
173
175
  if needs_refresh?(now, last_update)
174
- # set last updated time first, then make a best effort to refresh
175
- @cert_provider.crl_last_update = now
176
176
  next_ctx = refresh_crl(next_ctx, last_update)
177
177
  end
178
178
  else
@@ -209,7 +209,12 @@ class Puppet::SSL::StateMachine
209
209
  Puppet.info(_("Refreshing CRL"))
210
210
 
211
211
  # return the next_ctx containing the updated crl
212
- 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
213
218
  rescue Puppet::HTTP::ResponseError => e
214
219
  if e.response.code == 304
215
220
  Puppet.info(_("CRL is unmodified, using existing CRL"))
@@ -257,7 +262,11 @@ class Puppet::SSL::StateMachine
257
262
  next_ctx = @ssl_provider.create_context(
258
263
  cacerts: @ssl_context.cacerts, crls: @ssl_context.crls, private_key: key, client_cert: cert
259
264
  )
260
- 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
261
270
  end
262
271
  else
263
272
  if Puppet[:key_type] == 'ec'
@@ -273,6 +282,15 @@ class Puppet::SSL::StateMachine
273
282
 
274
283
  NeedSubmitCSR.new(@machine, @ssl_context, key)
275
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
276
294
  end
277
295
 
278
296
  # Base class for states with a private key.
@@ -344,6 +362,39 @@ class Puppet::SSL::StateMachine
344
362
  end
345
363
  end
346
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
+
347
398
  # We cannot make progress, so wait if allowed to do so, or exit.
348
399
  #
349
400
  class Wait < SSLState
@@ -495,7 +546,7 @@ class Puppet::SSL::StateMachine
495
546
  final_state.ssl_context
496
547
  end
497
548
 
498
- # Run the state machine for CA certs and CRLs.
549
+ # Run the state machine for client certs.
499
550
  #
500
551
  # @return [Puppet::SSL::SSLContext] initialized SSLContext
501
552
  # @raise [Puppet::Error] If we fail to generate an SSLContext
@@ -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.1.0'
10
+ PUPPETVERSION = '8.2.0'
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and
@@ -311,6 +311,13 @@ class Puppet::X509::CertProvider
311
311
  options[:extension_requests] = csr_attributes.extension_requests
312
312
  end
313
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
+
314
321
  csr = Puppet::SSL::CertificateRequest.new(name)
315
322
  csr.generate(private_key, options)
316
323
  end