puppet 6.27.0 → 6.28.0

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.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +71 -11
  3. data/lib/puppet/agent.rb +47 -11
  4. data/lib/puppet/application/agent.rb +2 -12
  5. data/lib/puppet/http/client.rb +22 -2
  6. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  7. data/lib/puppet/provider/package/windows/exe_package.rb +30 -1
  8. data/lib/puppet/provider/package/windows/package.rb +2 -1
  9. data/lib/puppet/provider/package/windows.rb +14 -1
  10. data/lib/puppet/provider/user/directoryservice.rb +5 -0
  11. data/lib/puppet/ssl/ssl_provider.rb +65 -12
  12. data/lib/puppet/ssl/state_machine.rb +13 -17
  13. data/lib/puppet/type/user.rb +3 -0
  14. data/lib/puppet/version.rb +1 -1
  15. data/lib/puppet.rb +1 -14
  16. data/man/man5/puppet.conf.5 +2 -2
  17. data/man/man8/puppet-agent.8 +1 -1
  18. data/man/man8/puppet-apply.8 +1 -1
  19. data/man/man8/puppet-catalog.8 +1 -1
  20. data/man/man8/puppet-config.8 +1 -1
  21. data/man/man8/puppet-describe.8 +1 -1
  22. data/man/man8/puppet-device.8 +1 -1
  23. data/man/man8/puppet-doc.8 +1 -1
  24. data/man/man8/puppet-epp.8 +1 -1
  25. data/man/man8/puppet-facts.8 +1 -1
  26. data/man/man8/puppet-filebucket.8 +1 -1
  27. data/man/man8/puppet-generate.8 +1 -1
  28. data/man/man8/puppet-help.8 +1 -1
  29. data/man/man8/puppet-key.8 +1 -1
  30. data/man/man8/puppet-lookup.8 +1 -1
  31. data/man/man8/puppet-man.8 +1 -1
  32. data/man/man8/puppet-module.8 +1 -1
  33. data/man/man8/puppet-node.8 +1 -1
  34. data/man/man8/puppet-parser.8 +1 -1
  35. data/man/man8/puppet-plugin.8 +1 -1
  36. data/man/man8/puppet-report.8 +1 -1
  37. data/man/man8/puppet-resource.8 +1 -1
  38. data/man/man8/puppet-script.8 +1 -1
  39. data/man/man8/puppet-ssl.8 +1 -1
  40. data/man/man8/puppet-status.8 +1 -1
  41. data/man/man8/puppet.8 +2 -2
  42. data/spec/integration/application/agent_spec.rb +108 -0
  43. data/spec/integration/http/client_spec.rb +27 -10
  44. data/spec/lib/puppet_spec/https.rb +1 -1
  45. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  46. data/spec/unit/agent_spec.rb +28 -2
  47. data/spec/unit/application/agent_spec.rb +26 -16
  48. data/spec/unit/daemon_spec.rb +2 -11
  49. data/spec/unit/http/client_spec.rb +18 -0
  50. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  51. data/spec/unit/provider/package/windows/exe_package_spec.rb +17 -0
  52. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  53. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  54. data/tasks/generate_cert_fixtures.rake +5 -4
  55. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c79ac9c8ff480ec911b6f001137f8f089068ed0f1d9b62f26394a9ed6ea76c2
4
- data.tar.gz: a41173a9cdee61c24c191c6a719ef33360b613d112a5e1da6e199c1bffe57bc9
3
+ metadata.gz: 4a5961fa6df54cdc74777c8fbab36fad2ae62bbd11ab0bbb80cccbb2ba062a37
4
+ data.tar.gz: 3747fb6b31a88749e0ab737d599be3b0df1e89b421d9601e3ce13a803725c140
5
5
  SHA512:
6
- metadata.gz: e3a6e9fa087622bf725464304dac706311f6c678f6dc27f5fd5b135a87d2fea86a5112fc1fe204f544d1aec61f4ed4c45bfea2a3d33f424a606dfec42cb11270
7
- data.tar.gz: 967d852cbf0e1b8c8f2cc154ac7fa8316a8131044314a758295ca2f75a8cfa6537e4f69a3e99f88847b9df4cccc380e44b7b919c1e161954e484403d4b884504
6
+ metadata.gz: 465ace5e96735204aba54d8135aca487329238e36b2250cd0c0fd022306f7104a3c9f89c79370886d9c9182862f0d96d3e2621cb1b5fa1af965f47945994c671
7
+ data.tar.gz: 8e83132e776e4f6e0291c2e28d8ea01c57cbb35093234ee3d3b109e6d22af16eb33b32e3deccf888d006837542c61726c633d185c8f16717f787d2a73373fe87
data/Gemfile.lock CHANGED
@@ -1,19 +1,21 @@
1
1
  GIT
2
2
  remote: https://github.com/puppetlabs/packaging
3
- revision: 6f7b1ff00ab557f6a47f3f553cc87ec15d718470
3
+ revision: 6edc2f8e4ebe3cbea96c3af9c294bcd6e2953648
4
4
  branch: 1.0.x
5
5
  specs:
6
- packaging (0.106.0.27.g6f7b1ff)
6
+ packaging (0.107.0.9.g6edc2f8)
7
7
  apt_stage_artifacts
8
8
  artifactory (~> 3)
9
9
  csv (= 3.1.5)
10
+ google-cloud-storage
11
+ googleauth
10
12
  rake (>= 12.3)
11
13
  release-metrics
12
14
 
13
15
  PATH
14
16
  remote: .
15
17
  specs:
16
- puppet (6.27.0)
18
+ puppet (6.28.0)
17
19
  CFPropertyList (~> 2.2)
18
20
  concurrent-ruby (~> 1.0)
19
21
  deep_merge (~> 1.0)
@@ -31,7 +33,7 @@ GEM
31
33
  CFPropertyList (2.3.6)
32
34
  addressable (2.8.0)
33
35
  public_suffix (>= 2.0.2, < 5.0)
34
- apt_stage_artifacts (0.10.1)
36
+ apt_stage_artifacts (0.11.0)
35
37
  docopt
36
38
  artifactory (3.0.15)
37
39
  ast (2.4.2)
@@ -40,12 +42,19 @@ GEM
40
42
  crack (0.4.5)
41
43
  rexml
42
44
  csv (3.1.5)
45
+ declarative (0.0.20)
43
46
  deep_merge (1.2.2)
44
47
  diff-lcs (1.5.0)
48
+ digest-crc (0.6.4)
49
+ rake (>= 12.0.0, < 14.0.0)
45
50
  docopt (0.6.1)
46
- facter (4.2.9)
51
+ facter (4.2.10)
47
52
  hocon (~> 1.3)
48
53
  thor (>= 1.0.1, < 2.0)
54
+ faraday (2.3.0)
55
+ faraday-net_http (~> 2.0)
56
+ ruby2_keywords (>= 0.0.4)
57
+ faraday-net_http (2.0.3)
49
58
  fast_gettext (1.1.2)
50
59
  ffi (1.15.5)
51
60
  gettext (3.2.9)
@@ -55,9 +64,43 @@ GEM
55
64
  fast_gettext (~> 1.1.0)
56
65
  gettext (>= 3.0.2, < 3.3.0)
57
66
  locale
67
+ google-apis-core (0.7.0)
68
+ addressable (~> 2.5, >= 2.5.1)
69
+ googleauth (>= 0.16.2, < 2.a)
70
+ httpclient (>= 2.8.1, < 3.a)
71
+ mini_mime (~> 1.0)
72
+ representable (~> 3.0)
73
+ retriable (>= 2.0, < 4.a)
74
+ rexml
75
+ webrick
76
+ google-apis-iamcredentials_v1 (0.13.0)
77
+ google-apis-core (>= 0.7, < 2.a)
78
+ google-apis-storage_v1 (0.18.0)
79
+ google-apis-core (>= 0.7, < 2.a)
80
+ google-cloud-core (1.6.0)
81
+ google-cloud-env (~> 1.0)
82
+ google-cloud-errors (~> 1.0)
83
+ google-cloud-env (1.6.0)
84
+ faraday (>= 0.17.3, < 3.0)
85
+ google-cloud-errors (1.2.0)
86
+ google-cloud-storage (1.37.0)
87
+ addressable (~> 2.8)
88
+ digest-crc (~> 0.4)
89
+ google-apis-iamcredentials_v1 (~> 0.1)
90
+ google-apis-storage_v1 (~> 0.1)
91
+ google-cloud-core (~> 1.6)
92
+ googleauth (>= 0.16.2, < 2.a)
93
+ mini_mime (~> 1.0)
94
+ googleauth (1.2.0)
95
+ faraday (>= 0.17.3, < 3.a)
96
+ jwt (>= 1.4, < 3.0)
97
+ memoist (~> 0.16)
98
+ multi_json (~> 1.11)
99
+ os (>= 0.9, < 2.0)
100
+ signet (>= 0.16, < 2.a)
58
101
  hashdiff (1.0.1)
59
- hiera (3.8.0)
60
- hiera-eyaml (3.2.2)
102
+ hiera (3.9.0)
103
+ hiera-eyaml (3.3.0)
61
104
  highline
62
105
  optimist
63
106
  highline (2.0.3)
@@ -66,14 +109,18 @@ GEM
66
109
  httpclient (2.8.3)
67
110
  json-schema (2.8.1)
68
111
  addressable (>= 2.4)
112
+ jwt (2.4.1)
69
113
  locale (2.1.3)
114
+ memoist (0.16.2)
70
115
  memory_profiler (1.0.0)
71
116
  method_source (1.0.0)
117
+ mini_mime (1.1.2)
72
118
  minitar (0.9)
73
- msgpack (1.5.0)
119
+ msgpack (1.5.3)
74
120
  multi_json (1.15.0)
75
121
  mustache (1.1.1)
76
122
  optimist (3.0.1)
123
+ os (1.1.4)
77
124
  parallel (1.22.1)
78
125
  parser (2.7.2.0)
79
126
  ast (~> 2.4.1)
@@ -81,7 +128,7 @@ GEM
81
128
  pry (0.14.1)
82
129
  coderay (~> 1.1)
83
130
  method_source (~> 1.0)
84
- public_suffix (4.0.6)
131
+ public_suffix (4.0.7)
85
132
  puppet-resource_api (1.8.14)
86
133
  hocon (>= 1.0)
87
134
  puppetserver-ca (1.11.7)
@@ -95,6 +142,11 @@ GEM
95
142
  release-metrics (1.1.0)
96
143
  csv
97
144
  docopt
145
+ representable (3.2.0)
146
+ declarative (< 0.1.0)
147
+ trailblazer-option (>= 0.1.1, < 0.2.0)
148
+ uber (< 0.2.0)
149
+ retriable (3.1.2)
98
150
  rexml (3.2.5)
99
151
  ronn (0.7.3)
100
152
  hpricot (>= 0.8.2)
@@ -127,10 +179,18 @@ GEM
127
179
  rubocop (~> 0.49.0)
128
180
  ruby-prof (1.4.3)
129
181
  ruby-progressbar (1.11.0)
182
+ ruby2_keywords (0.0.5)
130
183
  scanf (1.0.0)
131
184
  semantic_puppet (1.0.4)
185
+ signet (0.17.0)
186
+ addressable (~> 2.8)
187
+ faraday (>= 0.17.5, < 3.a)
188
+ jwt (>= 1.5, < 3.0)
189
+ multi_json (~> 1.10)
132
190
  text (1.3.1)
133
191
  thor (1.2.1)
192
+ trailblazer-option (0.1.2)
193
+ uber (0.1.0)
134
194
  unicode-display_width (1.8.0)
135
195
  vcr (5.1.0)
136
196
  webmock (3.14.0)
@@ -138,7 +198,7 @@ GEM
138
198
  crack (>= 0.3.2)
139
199
  hashdiff (>= 0.4.0, < 2.0.0)
140
200
  webrick (1.7.0)
141
- yard (0.9.27)
201
+ yard (0.9.28)
142
202
  webrick (~> 1.7.0)
143
203
 
144
204
  PLATFORMS
@@ -176,4 +236,4 @@ DEPENDENCIES
176
236
  yard
177
237
 
178
238
  BUNDLED WITH
179
- 2.3.9
239
+ 2.3.10
data/lib/puppet/agent.rb CHANGED
@@ -38,26 +38,51 @@ class Puppet::Agent
38
38
  # Perform a run with our client.
39
39
  def run(client_options = {})
40
40
  if disabled?
41
- Puppet.notice _("Skipping run of %{client_class}; administratively disabled (Reason: '%{disable_message}');\nUse 'puppet agent --enable' to re-enable.") % { client_class: client_class, disable_message: disable_message }
41
+ log_disabled_message
42
42
  return
43
43
  end
44
44
 
45
45
  result = nil
46
46
  wait_for_lock_deadline = nil
47
47
  block_run = Puppet::Application.controlled_run do
48
- splay client_options.fetch :splay, Puppet[:splay]
48
+ # splay may sleep for awhile when running onetime! If not onetime, then
49
+ # the job scheduler splays (only once) so that agents assign themselves a
50
+ # slot within the splay interval.
51
+ do_splay = client_options.fetch(:splay, Puppet[:splay])
52
+ if do_splay
53
+ splay(do_splay)
54
+
55
+ if disabled?
56
+ log_disabled_message
57
+ break
58
+ end
59
+ end
60
+
61
+ # waiting for certs may sleep for awhile depending on onetime, waitforcert and maxwaitforcert!
62
+ # this needs to happen before forking so that if we fail to obtain certs and try to exit, then
63
+ # we exit the main process and not the forked child.
64
+ ssl_context = wait_for_certificates(client_options)
65
+
49
66
  result = run_in_fork(should_fork) do
50
67
  with_client(client_options[:transaction_uuid], client_options[:job_id]) do |client|
51
68
  client_args = client_options.merge(:pluginsync => Puppet::Configurer.should_pluginsync?)
52
69
  begin
70
+ # lock may sleep for awhile depending on waitforlock and maxwaitforlock!
53
71
  lock do
54
- # NOTE: Timeout is pretty heinous as the location in which it
55
- # throws an error is entirely unpredictable, which means that
56
- # it can interrupt code blocks that perform cleanup or enforce
57
- # sanity. The only thing a Puppet agent should do after this
58
- # error is thrown is die with as much dignity as possible.
59
- Timeout.timeout(Puppet[:runtimeout], RunTimeoutError) do
60
- client.run(client_args)
72
+ if disabled?
73
+ log_disabled_message
74
+ nil
75
+ else
76
+ # NOTE: Timeout is pretty heinous as the location in which it
77
+ # throws an error is entirely unpredictable, which means that
78
+ # it can interrupt code blocks that perform cleanup or enforce
79
+ # sanity. The only thing a Puppet agent should do after this
80
+ # error is thrown is die with as much dignity as possible.
81
+ Timeout.timeout(Puppet[:runtimeout], RunTimeoutError) do
82
+ Puppet.override(ssl_context: ssl_context) do
83
+ client.run(client_args)
84
+ end
85
+ end
61
86
  end
62
87
  end
63
88
  rescue Puppet::LockError
@@ -78,12 +103,13 @@ class Puppet::Agent
78
103
  end
79
104
  rescue RunTimeoutError => detail
80
105
  Puppet.log_exception(detail, _("Execution of %{client_class} did not complete within %{runtimeout} seconds and was terminated.") %
81
- {client_class: client_class,
82
- runtimeout: Puppet[:runtimeout]})
106
+ {client_class: client_class, runtimeout: Puppet[:runtimeout]})
83
107
  nil
84
108
  rescue StandardError => detail
85
109
  Puppet.log_exception(detail, _("Could not run %{client_class}: %{detail}") % { client_class: client_class, detail: detail })
86
110
  nil
111
+ ensure
112
+ Puppet.runtime[:http].close
87
113
  end
88
114
  end
89
115
  end
@@ -137,4 +163,14 @@ class Puppet::Agent
137
163
  ensure
138
164
  @client = nil
139
165
  end
166
+
167
+ def wait_for_certificates(options)
168
+ waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : Puppet[:waitforcert])
169
+ sm = Puppet::SSL::StateMachine.new(waitforcert: waitforcert, onetime: Puppet[:onetime])
170
+ sm.ensure_client_certificate
171
+ end
172
+
173
+ def log_disabled_message
174
+ Puppet.notice _("Skipping run of %{client_class}; administratively disabled (Reason: '%{disable_message}');\nUse 'puppet agent --enable' to re-enable.") % { client_class: client_class, disable_message: disable_message }
175
+ end
140
176
  end
@@ -383,15 +383,11 @@ Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License
383
383
 
384
384
  log_config if Puppet[:daemonize]
385
385
 
386
- # run ssl state machine, waiting if needed
387
- ssl_context = wait_for_certificates
388
-
389
386
  # Each application is responsible for pushing loaders onto the context.
390
387
  # Use the current environment that has already been established, though
391
388
  # it may change later during the configurer run.
392
389
  env = Puppet.lookup(:current_environment)
393
- Puppet.override(ssl_context: ssl_context,
394
- current_environment: env,
390
+ Puppet.override(current_environment: env,
395
391
  loaders: Puppet::Pops::Loaders.new(env, true)) do
396
392
  if Puppet[:onetime]
397
393
  onetime(daemon)
@@ -434,7 +430,7 @@ Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License
434
430
 
435
431
  def onetime(daemon)
436
432
  begin
437
- exitstatus = daemon.agent.run({:job_id => options[:job_id], :start_time => options[:start_time]})
433
+ exitstatus = daemon.agent.run({:job_id => options[:job_id], :start_time => options[:start_time], :waitforcert => options[:waitforcert]})
438
434
  rescue => detail
439
435
  Puppet.log_exception(detail)
440
436
  end
@@ -524,10 +520,4 @@ Copyright (c) 2011 Puppet Inc., LLC Licensed under the Apache 2.0 License
524
520
 
525
521
  daemon
526
522
  end
527
-
528
- def wait_for_certificates
529
- waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : Puppet[:waitforcert])
530
- sm = Puppet::SSL::StateMachine.new(waitforcert: waitforcert)
531
- sm.ensure_client_certificate
532
- end
533
523
  end
@@ -25,7 +25,7 @@ class Puppet::HTTP::Client
25
25
  # used if :include_system_store is set to true
26
26
  # @param [Integer] redirect_limit default number of HTTP redirections to allow
27
27
  # in a given request. Can also be specified per-request.
28
- # @param [Integer] retry_limit number of HTTP reties allowed in a given
28
+ # @param [Integer] retry_limit number of HTTP retries allowed in a given
29
29
  # request
30
30
  #
31
31
  def initialize(pool: Puppet::Network::HTTP::Pool.new(Puppet[:http_keepalive_timeout]), ssl_context: nil, system_ssl_context: nil, redirect_limit: 10, retry_limit: 100)
@@ -272,6 +272,24 @@ class Puppet::HTTP::Client
272
272
  #
273
273
  def close
274
274
  @pool.close
275
+ @default_ssl_context = nil
276
+ @default_system_ssl_context = nil
277
+ end
278
+
279
+ def default_ssl_context
280
+ cert = Puppet::X509::CertProvider.new
281
+ password = cert.load_private_key_password
282
+
283
+ ssl = Puppet::SSL::SSLProvider.new
284
+ ctx = ssl.load_context(certname: Puppet[:certname], password: password)
285
+ ssl.print(ctx)
286
+ ctx
287
+ rescue => e
288
+ # TRANSLATORS: `message` is an already translated string of why SSL failed to initialize
289
+ Puppet.log_exception(e, _("Failed to initialize SSL: %{message}") % { message: e.message })
290
+ # TRANSLATORS: `puppet agent -t` is a command and should not be translated
291
+ Puppet.err(_("Run `puppet agent -t`"))
292
+ raise e
275
293
  end
276
294
 
277
295
  protected
@@ -408,7 +426,9 @@ class Puppet::HTTP::Client
408
426
  cacerts = cert_provider.load_cacerts || []
409
427
 
410
428
  ssl = Puppet::SSL::SSLProvider.new
411
- @default_system_ssl_context = ssl.create_system_context(cacerts: cacerts)
429
+ @default_system_ssl_context = ssl.create_system_context(cacerts: cacerts, include_client_cert: true)
430
+ ssl.print(@default_system_ssl_context)
431
+ @default_system_ssl_context
412
432
  end
413
433
 
414
434
  def apply_auth(request, basic_auth)
@@ -53,7 +53,7 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
53
53
  end
54
54
 
55
55
  if options[:local]
56
- list = execute_rubygems_list_command(gem_regex)
56
+ list = execute_rubygems_list_command(command_options)
57
57
  else
58
58
  begin
59
59
  list = puppetservercmd(command_options)
@@ -137,7 +137,7 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
137
137
  # for example: json (1.8.3 java)
138
138
  # but java platform gems should not be managed by this (or any) provider.
139
139
 
140
- def self.execute_rubygems_list_command(gem_regex)
140
+ def self.execute_rubygems_list_command(command_options)
141
141
  puppetserver_default_gem_home = '/opt/puppetlabs/server/data/puppetserver/jruby-gems'
142
142
  puppetserver_default_vendored_jruby_gems = '/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems'
143
143
  puppet_default_vendor_gems = '/opt/puppetlabs/puppet/lib/ruby/vendor_gems'
@@ -157,24 +157,15 @@ Puppet::Type.type(:package).provide :puppetserver_gem, :parent => :gem do
157
157
  gem_env['GEM_PATH'] = puppetserver_conf['jruby-puppet'].key?('gem-path') ? puppetserver_conf['jruby-puppet']['gem-path'].join(':') : puppetserver_default_gem_path
158
158
  end
159
159
  gem_env['GEM_SPEC_CACHE'] = "/tmp/#{$$}"
160
- Gem.paths = gem_env
161
-
162
- sio_inn = StringIO.new
163
- sio_out = StringIO.new
164
- sio_err = StringIO.new
165
- stream_ui = Gem::StreamUI.new(sio_inn, sio_out, sio_err, false)
166
- gem_list_cmd = Gem::Commands::ListCommand.new
167
- gem_list_cmd.options[:domain] = :local
168
- gem_list_cmd.options[:args] = [gem_regex] if gem_regex
169
- gem_list_cmd.ui = stream_ui
170
- gem_list_cmd.execute
160
+
161
+ # Remove the 'gem' from the command_options
162
+ command_options.shift
163
+ gem_out = execute_gem_command(Puppet::Type::Package::ProviderPuppet_gem.provider_command, command_options, gem_env)
171
164
 
172
165
  # There is no method exclude default gems from the local gem list,
173
166
  # for example: psych (default: 2.2.2)
174
167
  # but default gems should not be managed by this (or any) provider.
175
- gem_list = sio_out.string.lines.reject { |gem| gem =~ / \(default\: / }
168
+ gem_list = gem_out.lines.reject { |gem| gem =~ / \(default\: / }
176
169
  gem_list.join("\n")
177
- ensure
178
- Gem.clear_paths
179
170
  end
180
171
  end
@@ -17,6 +17,11 @@ class Puppet::Provider::Package::Windows
17
17
  'WindowsInstaller',
18
18
  ]
19
19
 
20
+ def self.register(path)
21
+ Puppet::Type::Package::ProviderWindows.paths ||= []
22
+ Puppet::Type::Package::ProviderWindows.paths << path
23
+ end
24
+
20
25
  # Return an instance of the package from the registry, or nil
21
26
  def self.from_registry(name, values)
22
27
  if valid?(name, values)
@@ -55,7 +60,31 @@ class Puppet::Provider::Package::Windows
55
60
  end
56
61
 
57
62
  def self.install_command(resource)
58
- munge(resource[:source])
63
+ file_location = resource[:source]
64
+ if file_location.start_with?('http://', 'https://')
65
+ tempfile = Tempfile.new(['','.exe'])
66
+ begin
67
+ uri = URI(Puppet::Util.uri_encode(file_location))
68
+ client = Puppet.runtime[:http]
69
+ client.get(uri, options: { include_system_store: true }) do |response|
70
+ raise Puppet::HTTP::ResponseError.new(response) unless response.success?
71
+
72
+ File.open(tempfile.path, 'wb') do |file|
73
+ response.read_body do |data|
74
+ file.write(data)
75
+ end
76
+ end
77
+ end
78
+ rescue => detail
79
+ raise Puppet::Error.new(_("Error when installing %{package}: %{detail}") % { package: resource[:name] ,detail: detail.message}, detail)
80
+ ensure
81
+ self.register(tempfile.path)
82
+ tempfile.close()
83
+ file_location = tempfile.path
84
+ end
85
+ end
86
+
87
+ munge(file_location)
59
88
  end
60
89
 
61
90
  def uninstall_command
@@ -67,7 +67,8 @@ class Puppet::Provider::Package::Windows
67
67
  # REMIND: what about msp, etc
68
68
  MsiPackage
69
69
  when /\.exe"?\Z/i
70
- fail(_("The source does not exist: '%{source}'") % { source: resource[:source] }) unless Puppet::FileSystem.exist?(resource[:source])
70
+ fail(_("The source does not exist: '%{source}'") % { source: resource[:source] }) unless
71
+ Puppet::FileSystem.exist?(resource[:source]) || resource[:source].start_with?('http://', 'https://')
71
72
  ExePackage
72
73
  else
73
74
  fail(_("Don't know how to install '%{source}'") % { source: resource[:source] })
@@ -30,6 +30,19 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
30
30
  has_feature :versionable
31
31
 
32
32
  attr_accessor :package
33
+ class << self
34
+ attr_accessor :paths
35
+ end
36
+
37
+ def self.post_resource_eval
38
+ @paths.each do |path|
39
+ begin
40
+ Puppet::FileSystem.unlink(path)
41
+ rescue => detail
42
+ raise Puppet::Error.new(_("Error when unlinking %{path}: %{detail}") % { path: path ,detail: detail.message}, detail)
43
+ end
44
+ end if @paths
45
+ end
33
46
 
34
47
  # Return an array of provider instances
35
48
  def self.instances
@@ -64,7 +77,7 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
64
77
 
65
78
  command = [installer.install_command(resource), install_options].flatten.compact.join(' ')
66
79
  working_dir = File.dirname(resource[:source])
67
- if !Puppet::FileSystem.exist?(working_dir) && resource[:source] =~ /\.msi"?\Z/i
80
+ unless Puppet::FileSystem.exist?(working_dir)
68
81
  working_dir = nil
69
82
  end
70
83
  output = execute(command, :failonfail => false, :combine => true, :cwd => working_dir, :suppress_window => true)
@@ -401,6 +401,11 @@ Puppet::Type.type(:user).provide :directoryservice do
401
401
  # we have to treat the ds cache just like you would in the password=
402
402
  # method.
403
403
  def salt=(value)
404
+ if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.15') >= 0)
405
+ if value.length != 64
406
+ self.fail "macOS versions 10.15 and higher require the salt to be 32-bytes. Since Puppet's user resource requires the value to be hex encoded, the length of the salt's string must be 64. Please check your salt and try again."
407
+ end
408
+ end
404
409
  if (Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.7') > 0)
405
410
  assert_full_pbkdf2_password
406
411
 
@@ -42,15 +42,18 @@ class Puppet::SSL::SSLProvider
42
42
  # refers to the cacerts bundle in the puppet-agent package.
43
43
  #
44
44
  # Connections made from the returned context will authenticate the server,
45
- # i.e. `VERIFY_PEER`, but will not use a client certificate and will not
46
- # perform revocation checking.
45
+ # i.e. `VERIFY_PEER`, but will not use a client certificate (unless requested)
46
+ # and will not perform revocation checking.
47
47
  #
48
48
  # @param cacerts [Array<OpenSSL::X509::Certificate>] Array of trusted CA certs
49
49
  # @param path [String, nil] A file containing additional trusted CA certs.
50
+ # @param include_client_cert [true, false] If true, the client cert will be added to the context
51
+ # allowing mutual TLS authentication. The default is false. If the client cert doesn't exist
52
+ # then the option will be ignored.
50
53
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
51
54
  # @raise (see #create_context)
52
55
  # @api private
53
- def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
56
+ def create_system_context(cacerts:, path: Puppet[:ssl_trust_store], include_client_cert: false)
54
57
  store = create_x509_store(cacerts, [], false, include_system_store: true)
55
58
 
56
59
  if path
@@ -71,6 +74,29 @@ class Puppet::SSL::SSLProvider
71
74
  end
72
75
  end
73
76
 
77
+ if include_client_cert
78
+ cert_provider = Puppet::X509::CertProvider.new
79
+ private_key = cert_provider.load_private_key(Puppet[:certname], required: false)
80
+ unless private_key
81
+ Puppet.warning("Private key for '#{Puppet[:certname]}' does not exist")
82
+ end
83
+
84
+ client_cert = cert_provider.load_client_cert(Puppet[:certname], required: false)
85
+ unless client_cert
86
+ Puppet.warning("Client certificate for '#{Puppet[:certname]}' does not exist")
87
+ end
88
+
89
+ if private_key && client_cert
90
+ client_chain = resolve_client_chain(store, client_cert, private_key)
91
+
92
+ return Puppet::SSL::SSLContext.new(
93
+ store: store, cacerts: cacerts, crls: [],
94
+ private_key: private_key, client_cert: client_cert, client_chain: client_chain,
95
+ revocation: false
96
+ ).freeze
97
+ end
98
+ end
99
+
74
100
  Puppet::SSL::SSLContext.new(store: store, cacerts: cacerts, crls: [], revocation: false).freeze
75
101
  end
76
102
 
@@ -107,15 +133,7 @@ class Puppet::SSL::SSLProvider
107
133
  raise ArgumentError, _("Client cert is missing") unless client_cert
108
134
 
109
135
  store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
110
- client_chain = verify_cert_with_store(store, client_cert)
111
-
112
- if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
113
- raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
114
- end
115
-
116
- unless client_cert.check_private_key(private_key)
117
- raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
118
- end
136
+ client_chain = resolve_client_chain(store, client_cert, private_key)
119
137
 
120
138
  Puppet::SSL::SSLContext.new(
121
139
  store: store, cacerts: cacerts, crls: crls,
@@ -174,6 +192,27 @@ class Puppet::SSL::SSLProvider
174
192
  csr
175
193
  end
176
194
 
195
+ def print(ssl_context, alg = 'SHA256')
196
+ if Puppet::Util::Log.sendlevel?(:debug)
197
+ chain = ssl_context.client_chain
198
+ # print from root to client
199
+ chain.reverse.each_with_index do |cert, i|
200
+ digest = Puppet::SSL::Digest.new(alg, cert.to_der)
201
+ if i == chain.length - 1
202
+ Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
203
+ else
204
+ Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
205
+ end
206
+ end
207
+ ssl_context.crls.each do |crl|
208
+ oid_values = Hash[crl.extensions.map { |ext| [ext.oid, ext.value] }]
209
+ crlNumber = oid_values['crlNumber'] || 'unknown'
210
+ authKeyId = (oid_values['authorityKeyIdentifier'] || 'unknown').chomp!
211
+ Puppet.debug("Using CRL '#{crl.issuer.to_utf8}' authorityKeyIdentifier '#{authKeyId}' crlNumber '#{crlNumber }'")
212
+ end
213
+ end
214
+ end
215
+
177
216
  private
178
217
 
179
218
  def default_flags
@@ -220,6 +259,20 @@ class Puppet::SSL::SSLProvider
220
259
  end
221
260
  end
222
261
 
262
+ def resolve_client_chain(store, client_cert, private_key)
263
+ client_chain = verify_cert_with_store(store, client_cert)
264
+
265
+ if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
266
+ raise Puppet::SSL::SSLError, _("Unsupported key '%{type}'") % { type: private_key.class.name }
267
+ end
268
+
269
+ unless client_cert.check_private_key(private_key)
270
+ raise Puppet::SSL::SSLError, _("The certificate for '%{name}' does not match its private key") % { name: subject(client_cert) }
271
+ end
272
+
273
+ client_chain
274
+ end
275
+
223
276
  def verify_cert_with_store(store, cert)
224
277
  # StoreContext#initialize accepts a chain argument, but it's set to [] because
225
278
  # puppet requires any intermediate CA certs needed to complete the client's
@@ -27,6 +27,15 @@ class Puppet::SSL::StateMachine
27
27
  detail.set_backtrace(cause.backtrace)
28
28
  Error.new(@machine, message, detail)
29
29
  end
30
+
31
+ def log_error(message)
32
+ # When running daemonized we set stdout to /dev/null, so write to the log instead
33
+ if Puppet[:daemonize]
34
+ Puppet.err(message)
35
+ else
36
+ $stdout.puts(message)
37
+ end
38
+ end
30
39
  end
31
40
 
32
41
  # Load existing CA certs or download them. Transition to NeedCRLs.
@@ -270,15 +279,15 @@ class Puppet::SSL::StateMachine
270
279
  def next_state
271
280
  time = @machine.waitforcert
272
281
  if time < 1
273
- puts _("Exiting now because the waitforcert setting is set to 0.")
282
+ log_error(_("Exiting now because the waitforcert setting is set to 0."))
274
283
  exit(1)
275
284
  elsif Time.now.to_i > @machine.wait_deadline
276
- puts _("Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.") % {name: Puppet[:certname] }
285
+ log_error(_("Couldn't fetch certificate from CA server; you might still need to sign this agent's certificate (%{name}). Exiting now because the maxwaitforcert timeout has been exceeded.") % {name: Puppet[:certname] })
277
286
  exit(1)
278
287
  else
279
288
  Puppet.info(_("Will try again in %{time} seconds.") % {time: time})
280
289
 
281
- # close persistent connections and session state before sleeping
290
+ # close http/tls and session state before sleeping
282
291
  Puppet.runtime[:http].close
283
292
  @machine.session = Puppet.runtime[:http].create_session
284
293
 
@@ -417,20 +426,7 @@ class Puppet::SSL::StateMachine
417
426
  def ensure_client_certificate
418
427
  final_state = run_machine(NeedLock.new(self), Done)
419
428
  ssl_context = final_state.ssl_context
420
-
421
- if Puppet::Util::Log.sendlevel?(:debug)
422
- chain = ssl_context.client_chain
423
- # print from root to client
424
- chain.reverse.each_with_index do |cert, i|
425
- digest = Puppet::SSL::Digest.new(@digest, cert.to_der)
426
- if i == chain.length - 1
427
- Puppet.debug(_("Verified client certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
428
- else
429
- Puppet.debug(_("Verified CA certificate '%{subject}' fingerprint %{digest}") % {subject: cert.subject.to_utf8, digest: digest})
430
- end
431
- end
432
- end
433
-
429
+ @ssl_provider.print(ssl_context, @digest)
434
430
  ssl_context
435
431
  end
436
432