puppet 6.0.2-universal-darwin → 6.0.3-universal-darwin

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +11 -11
  4. data/lib/puppet/application.rb +5 -0
  5. data/lib/puppet/application/apply.rb +1 -0
  6. data/lib/puppet/application/script.rb +1 -1
  7. data/lib/puppet/application/ssl.rb +119 -49
  8. data/lib/puppet/defaults.rb +9 -27
  9. data/lib/puppet/face/node/clean.rb +0 -1
  10. data/lib/puppet/feature/base.rb +1 -1
  11. data/lib/puppet/file_serving/fileset.rb +1 -1
  12. data/lib/puppet/pops/validation/checker4_0.rb +4 -2
  13. data/lib/puppet/provider/package/windows.rb +2 -2
  14. data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
  15. data/lib/puppet/provider/service/windows.rb +11 -3
  16. data/lib/puppet/provider/user/useradd.rb +2 -10
  17. data/lib/puppet/resource/catalog.rb +1 -5
  18. data/lib/puppet/ssl/host.rb +7 -9
  19. data/lib/puppet/transaction/persistence.rb +1 -1
  20. data/lib/puppet/type/package.rb +1 -1
  21. data/lib/puppet/type/user.rb +4 -1
  22. data/lib/puppet/util.rb +7 -3
  23. data/lib/puppet/util/execution.rb +1 -0
  24. data/lib/puppet/util/logging.rb +3 -2
  25. data/lib/puppet/util/windows/process.rb +6 -2
  26. data/lib/puppet/util/windows/security.rb +14 -0
  27. data/lib/puppet/util/windows/service.rb +217 -74
  28. data/lib/puppet/util/windows/user.rb +3 -5
  29. data/lib/puppet/version.rb +1 -1
  30. data/locales/ja/puppet.po +505 -276
  31. data/locales/puppet.pot +250 -111
  32. data/man/man5/puppet.conf.5 +8 -1
  33. data/man/man8/puppet-ssl.8 +22 -2
  34. data/man/man8/puppet.8 +1 -1
  35. data/spec/integration/parser/collection_spec.rb +4 -8
  36. data/spec/integration/type/file_spec.rb +6 -6
  37. data/spec/integration/util/windows/security_spec.rb +10 -7
  38. data/spec/integration/util/windows/user_spec.rb +37 -17
  39. data/spec/lib/puppet/test_ca.rb +1 -1
  40. data/spec/unit/agent_spec.rb +2 -2
  41. data/spec/unit/application/apply_spec.rb +41 -2
  42. data/spec/unit/application/face_base_spec.rb +1 -1
  43. data/spec/unit/application/ssl_spec.rb +160 -110
  44. data/spec/unit/application_spec.rb +29 -11
  45. data/spec/unit/configurer/downloader_spec.rb +1 -1
  46. data/spec/unit/configurer_spec.rb +5 -5
  47. data/spec/unit/face/node_spec.rb +1 -3
  48. data/spec/unit/file_serving/fileset_spec.rb +11 -11
  49. data/spec/unit/network/http/connection_spec.rb +2 -2
  50. data/spec/unit/pops/validator/validator_spec.rb +24 -10
  51. data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
  52. data/spec/unit/provider/package/windows_spec.rb +4 -4
  53. data/spec/unit/provider/service/windows_spec.rb +21 -3
  54. data/spec/unit/provider/user/useradd_spec.rb +2 -2
  55. data/spec/unit/resource/catalog_spec.rb +2 -2
  56. data/spec/unit/ssl/host_spec.rb +1 -1
  57. data/spec/unit/transaction/persistence_spec.rb +4 -4
  58. data/spec/unit/util/execution_spec.rb +19 -1
  59. data/spec/unit/util/logging_spec.rb +58 -0
  60. data/spec/unit/util/windows/service_spec.rb +344 -191
  61. metadata +2 -2
@@ -868,7 +868,7 @@ The time to wait for data to be read from an HTTP connection\. If nothing is rea
868
868
  The HTTP User\-Agent string to send when making network requests\.
869
869
  .
870
870
  .IP "\(bu" 4
871
- \fIDefault\fR: Puppet/6\.0\.2 Ruby/2\.4\.1\-p111 (x86_64\-linux)
871
+ \fIDefault\fR: Puppet/6\.0\.3 Ruby/2\.4\.1\-p111 (x86_64\-linux)
872
872
  .
873
873
  .IP "" 0
874
874
  .
@@ -1056,6 +1056,13 @@ crit
1056
1056
  \fIDefault\fR: notice
1057
1057
  .
1058
1058
  .IP "" 0
1059
+ .
1060
+ .SS "logdest"
1061
+ Where to send log messages\. Choose between \'syslog\' (the POSIX syslog service), \'eventlog\' (the Windows Event Log), \'console\', or the path to a log file\.
1062
+ .
1063
+ .TP
1064
+ \fIDefault\fR:
1065
+
1059
1066
  .
1060
1067
  .SS "logdir"
1061
1068
  The directory in which to store log files
@@ -7,10 +7,26 @@
7
7
  \fBpuppet\-ssl\fR \- Manage SSL keys and certificates for puppet SSL clients
8
8
  .
9
9
  .SH "SYNOPSIS"
10
- Manage SSL keys and certificates for an SSL clients needed to communicate with a puppet infrastructure\.
10
+ Manage SSL keys and certificates for SSL clients needing to communicate with a puppet infrastructure\.
11
11
  .
12
12
  .SH "USAGE"
13
- puppet ssl \fIaction\fR [\-\-certname \fINAME\fR]
13
+ puppet ssl \fIaction\fR [\-h|\-\-help] [\-v|\-\-verbose] [\-d|\-\-debug] [\-\-localca]
14
+ .
15
+ .SH "OPTIONS"
16
+ .
17
+ .IP "\(bu" 4
18
+ \-\-help: Print this help messsge\.
19
+ .
20
+ .IP "\(bu" 4
21
+ \-\-verbose: Print extra information\.
22
+ .
23
+ .IP "\(bu" 4
24
+ \-\-debug: Enable full debugging\.
25
+ .
26
+ .IP "\(bu" 4
27
+ \-\-localca Also clean the local CA certificate and CRL\.
28
+ .
29
+ .IP "" 0
14
30
  .
15
31
  .SH "ACTIONS"
16
32
  .
@@ -25,4 +41,8 @@ Download a certificate for this host\. If the current private key matches the do
25
41
  .TP
26
42
  verify
27
43
  Verify the private key and certificate are present and match, verify the certificate is issued by a trusted CA, and check revocation status\.
44
+ .
45
+ .TP
46
+ clean
47
+ Remove the private key and certificate related files for this host\. If \fB\-\-localca\fR is specified, then also remove this host\'s local copy of the CA certificate(s) and CRL bundle\.
28
48
 
@@ -25,4 +25,4 @@ Specialized:
25
25
  catalog Compile, save, view, and convert catalogs\. describe Display help about resource types device Manage remote network devices doc Generate Puppet references epp Interact directly with the EPP template parser/renderer\. facts Retrieve and store facts\. filebucket Store and retrieve files in a filebucket generate Generates Puppet code from Ruby definitions\. node View and manage node definitions\. parser Interact directly with the parser\. script Run a puppet manifests as a script without compiling a catalog ssl Manage SSL keys and certificates for puppet SSL clients
26
26
  .
27
27
  .P
28
- See \'puppet help \fIsubcommand\fR \fIaction\fR\' for help on a specific subcommand action\. See \'puppet help \fIsubcommand\fR\' for help on a specific subcommand\. Puppet v6\.0\.2
28
+ See \'puppet help \fIsubcommand\fR \fIaction\fR\' for help on a specific subcommand action\. See \'puppet help \fIsubcommand\fR\' for help on a specific subcommand\. Puppet v6\.0\.3
@@ -12,6 +12,10 @@ describe 'collectors' do
12
12
  expect(messages).to include(*expected_messages)
13
13
  end
14
14
 
15
+ def warnings
16
+ @logs.select { |log| log.level == :warning }.map { |log| log.message }
17
+ end
18
+
15
19
  context "virtual resource collection" do
16
20
  it "matches everything when no query given" do
17
21
  expect_the_message_to_be(["the other message", "the message"], <<-MANIFEST)
@@ -313,8 +317,6 @@ describe 'collectors' do
313
317
  end
314
318
 
315
319
  context 'when overriding an already evaluated resource' do
316
- let(:logs) { [] }
317
- let(:warnings) { logs.select { |log| log.level == :warning }.map { |log| log.message } }
318
320
  let(:manifest) { <<-MANIFEST }
319
321
  define foo($message) {
320
322
  notify { "testing": message => $message }
@@ -326,12 +328,6 @@ describe 'collectors' do
326
328
  delayed {'do it now': }
327
329
  MANIFEST
328
330
 
329
- around(:each) do |example|
330
- Puppet::Util::Log.with_destination(Puppet::Test::LogCollector.new(logs)) do
331
- example.run
332
- end
333
- end
334
-
335
331
  it 'and --strict=off, it silently skips the override' do
336
332
  Puppet[:strict] = :off
337
333
  expect_the_message_to_be(['given'], manifest)
@@ -579,7 +579,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
579
579
  if Puppet::Util::Platform.windows? && ['sha512', 'sha384'].include?(example.metadata[:digest_algorithm])
580
580
  skip "PUP-8257: Skip file bucket test on windows for #{example.metadata[:digest_algorithm]} due to long path names"
581
581
  end
582
-
582
+
583
583
  bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket"
584
584
  file = described_class.new({:path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo", :force => true}.merge(resource_options))
585
585
  catalog.add_resource file
@@ -1283,7 +1283,7 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
1283
1283
  describe "when sourcing" do
1284
1284
  it "should give a deprecation warning when the user sets source_permissions" do
1285
1285
  Puppet.expects(:puppet_deprecation_warning).with(
1286
- 'The `source_permissions` parameter is deprecated. Explicitly set `owner`, `group`, and `mode`.',
1286
+ 'The `source_permissions` parameter is deprecated. Explicitly set `owner`, `group`, and `mode`.',
1287
1287
  {:file => 'my/file.pp', :line => 5})
1288
1288
 
1289
1289
  catalog.add_resource described_class.new(:path => path, :content => 'this is content', :source_permissions => :use_when_creating)
@@ -1525,12 +1525,12 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
1525
1525
  catalog.apply
1526
1526
  end
1527
1527
 
1528
- it "should allow the user to explicitly set the mode to 4" do
1528
+ it "should not allow the user to explicitly set the mode to 4 ,and correct to 7" do
1529
1529
  system_aces = get_aces_for_path_by_sid(path, @sids[:system])
1530
1530
  expect(system_aces).not_to be_empty
1531
1531
 
1532
1532
  system_aces.each do |ace|
1533
- expect(ace.mask).to eq(Puppet::Util::Windows::File::FILE_GENERIC_READ)
1533
+ expect(ace.mask).to eq(Puppet::Util::Windows::File::FILE_ALL_ACCESS)
1534
1534
  end
1535
1535
  end
1536
1536
 
@@ -1612,13 +1612,13 @@ describe Puppet::Type.type(:file), :uses_checksums => true do
1612
1612
  catalog.apply
1613
1613
  end
1614
1614
 
1615
- it "should allow the user to explicitly set the mode to 4" do
1615
+ it "should not allow the user to explicitly set the mode to 4, and correct to 7" do
1616
1616
  system_aces = get_aces_for_path_by_sid(dir, @sids[:system])
1617
1617
  expect(system_aces).not_to be_empty
1618
1618
 
1619
1619
  system_aces.each do |ace|
1620
1620
  # unlike files, Puppet sets execute bit on directories that are readable
1621
- expect(ace.mask).to eq(Puppet::Util::Windows::File::FILE_GENERIC_READ | Puppet::Util::Windows::File::FILE_GENERIC_EXECUTE)
1621
+ expect(ace.mask).to eq(Puppet::Util::Windows::File::FILE_ALL_ACCESS)
1622
1622
  end
1623
1623
  end
1624
1624
 
@@ -285,10 +285,11 @@ describe "Puppet::Util::Windows::Security", :if => Puppet::Util::Platform.window
285
285
  # access mask, and back to mode without loss of information
286
286
  # (provided the owner and group are not the same)
287
287
  next if ((u & g) != g) or ((g & o) != o)
288
-
289
- mode = (s << 9 | u << 6 | g << 3 | o << 0)
290
- winsec.set_mode(mode, path)
291
- expect(winsec.get_mode(path).to_s(8)).to eq(mode.to_s(8))
288
+ applied_mode = (s << 9 | u << 6 | g << 3 | o << 0)
289
+ # SYSTEM must always be Full Control (7)
290
+ expected_mode = (s << 9 | u << 6 | 7 << 3 | o << 0)
291
+ winsec.set_mode(applied_mode, path)
292
+ expect(winsec.get_mode(path).to_s(8)).to eq(expected_mode.to_s(8))
292
293
  end
293
294
  end
294
295
  end
@@ -634,9 +635,11 @@ describe "Puppet::Util::Windows::Security", :if => Puppet::Util::Platform.window
634
635
  # access mask, and back to mode without loss of information
635
636
  # (provided the owner and group are the same)
636
637
  next if ((ug & o) != o)
637
- mode = (s << 9 | ug << 6 | ug << 3 | o << 0)
638
- winsec.set_mode(mode, path)
639
- expect(winsec.get_mode(path).to_s(8)).to eq(mode.to_s(8))
638
+ applied_mode = (s << 9 | ug << 6 | ug << 3 | o << 0)
639
+ # SYSTEM must always be Full Control (7)
640
+ expected_mode = (s << 9 | 7 << 6 | 7 << 3 | o << 0)
641
+ winsec.set_mode(applied_mode, path)
642
+ expect(winsec.get_mode(path).to_s(8)).to eq(expected_mode.to_s(8))
640
643
  end
641
644
  end
642
645
  end
@@ -6,54 +6,74 @@ describe "Puppet::Util::Windows::User", :if => Puppet::Util::Platform.windows? d
6
6
  describe "2003 without UAC" do
7
7
  before :each do
8
8
  Puppet::Util::Windows::Process.stubs(:windows_major_version).returns(5)
9
+ Puppet::Util::Windows::Process.stubs(:supports_elevated_security?).returns(false)
9
10
  end
10
11
 
11
12
  it "should be an admin if user's token contains the Administrators SID" do
12
13
  Puppet::Util::Windows::User.expects(:check_token_membership).returns(true)
13
- Puppet::Util::Windows::Process.expects(:elevated_security?).never
14
14
 
15
15
  expect(Puppet::Util::Windows::User).to be_admin
16
16
  end
17
17
 
18
18
  it "should not be an admin if user's token doesn't contain the Administrators SID" do
19
19
  Puppet::Util::Windows::User.expects(:check_token_membership).returns(false)
20
- Puppet::Util::Windows::Process.expects(:elevated_security?).never
21
20
 
22
21
  expect(Puppet::Util::Windows::User).not_to be_admin
23
22
  end
24
23
 
25
24
  it "should raise an exception if we can't check token membership" do
26
25
  Puppet::Util::Windows::User.expects(:check_token_membership).raises(Puppet::Util::Windows::Error, "Access denied.")
27
- Puppet::Util::Windows::Process.expects(:elevated_security?).never
28
26
 
29
27
  expect { Puppet::Util::Windows::User.admin? }.to raise_error(Puppet::Util::Windows::Error, /Access denied./)
30
28
  end
31
29
  end
32
30
 
33
- describe "2008 with UAC" do
31
+ context "2008 with UAC" do
34
32
  before :each do
35
33
  Puppet::Util::Windows::Process.stubs(:windows_major_version).returns(6)
34
+ Puppet::Util::Windows::Process.stubs(:supports_elevated_security?).returns(true)
36
35
  end
37
36
 
38
- it "should be an admin if user is running with elevated privileges" do
39
- Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(true)
40
- Puppet::Util::Windows::User.expects(:check_token_membership).never
37
+ describe "in local administrators group" do
38
+ before :each do
39
+ Puppet::Util::Windows::User.stubs(:check_token_membership).returns(true)
40
+ end
41
41
 
42
- expect(Puppet::Util::Windows::User).to be_admin
43
- end
42
+ it "should be an admin if user is running with elevated privileges" do
43
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(true)
44
44
 
45
- it "should not be an admin if user is not running with elevated privileges" do
46
- Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(false)
47
- Puppet::Util::Windows::User.expects(:check_token_membership).never
45
+ expect(Puppet::Util::Windows::User).to be_admin
46
+ end
48
47
 
49
- expect(Puppet::Util::Windows::User).not_to be_admin
48
+ it "should not be an admin if user is not running with elevated privileges" do
49
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(false)
50
+
51
+ expect(Puppet::Util::Windows::User).not_to be_admin
52
+ end
53
+
54
+ it "should raise an exception if the process fails to open the process token" do
55
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).raises(Puppet::Util::Windows::Error, "Access denied.")
56
+
57
+ expect { Puppet::Util::Windows::User.admin? }.to raise_error(Puppet::Util::Windows::Error, /Access denied./)
58
+ end
50
59
  end
51
60
 
52
- it "should raise an exception if the process fails to open the process token" do
53
- Puppet::Util::Windows::Process.stubs(:elevated_security?).raises(Puppet::Util::Windows::Error, "Access denied.")
54
- Puppet::Util::Windows::User.expects(:check_token_membership).never
61
+ describe "not in local administrators group" do
62
+ before :each do
63
+ Puppet::Util::Windows::User.stubs(:check_token_membership).returns(false)
64
+ end
65
+
66
+ it "should not be an admin if user is running with elevated privileges" do
67
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(true)
55
68
 
56
- expect { Puppet::Util::Windows::User.admin? }.to raise_error(Puppet::Util::Windows::Error, /Access denied./)
69
+ expect(Puppet::Util::Windows::User).not_to be_admin
70
+ end
71
+
72
+ it "should not be an admin if user is not running with elevated privileges" do
73
+ Puppet::Util::Windows::Process.stubs(:elevated_security?).returns(false)
74
+
75
+ expect(Puppet::Util::Windows::User).not_to be_admin
76
+ end
57
77
  end
58
78
  end
59
79
 
@@ -52,7 +52,7 @@ module Puppet
52
52
  def generate(name, opts)
53
53
  host_key = OpenSSL::PKey::RSA.new(1024)
54
54
  csr = create_csr(name, host_key)
55
- sign(csr, opts)
55
+ { private_key: host_key, csr: csr, cert: sign(csr, opts).content }
56
56
  end
57
57
 
58
58
  private
@@ -138,7 +138,7 @@ describe Puppet::Agent do
138
138
 
139
139
  it "should not fail if a client class instance cannot be created" do
140
140
  AgentTestClient.expects(:new).raises "eh"
141
- Puppet.expects(:err)
141
+ Puppet.expects(:log_exception)
142
142
  @agent.run
143
143
  end
144
144
 
@@ -146,7 +146,7 @@ describe Puppet::Agent do
146
146
  client = AgentTestClient.new
147
147
  AgentTestClient.expects(:new).returns client
148
148
  client.expects(:run).raises "eh"
149
- Puppet.expects(:err)
149
+ Puppet.expects(:log_exception)
150
150
  @agent.run
151
151
  end
152
152
 
@@ -7,6 +7,8 @@ require 'puppet/configurer'
7
7
  require 'fileutils'
8
8
 
9
9
  describe Puppet::Application::Apply do
10
+ include PuppetSpec::Files
11
+
10
12
  before :each do
11
13
  @apply = Puppet::Application[:apply]
12
14
  Puppet::Util::Log.stubs(:newdestination)
@@ -91,6 +93,13 @@ describe Puppet::Application::Apply do
91
93
  @apply.setup
92
94
  end
93
95
 
96
+ it "sets the log destination if logdest is provided via settings" do
97
+ Puppet::Log.expects(:newdestination).with("set_via_config")
98
+ Puppet[:logdest] = "set_via_config"
99
+
100
+ @apply.setup
101
+ end
102
+
94
103
  it "should set INT trap" do
95
104
  Signal.expects(:trap).with(:INT)
96
105
 
@@ -172,8 +181,6 @@ describe Puppet::Application::Apply do
172
181
  end
173
182
 
174
183
  describe "the main command" do
175
- include PuppetSpec::Files
176
-
177
184
  before :each do
178
185
  Puppet[:prerun_command] = ''
179
186
  Puppet[:postrun_command] = ''
@@ -492,6 +499,38 @@ describe Puppet::Application::Apply do
492
499
  end
493
500
  end
494
501
 
502
+ describe "when really executing" do
503
+ let(:testfile) { tmpfile('secret_file_name') }
504
+ let(:resourcefile) { tmpfile('resourcefile') }
505
+ let(:classfile) { tmpfile('classfile') }
506
+
507
+ it "should not expose sensitive data in the relationship file" do
508
+ @apply.options[:code] = <<-CODE
509
+ $secret = Sensitive('cat #{testfile}')
510
+
511
+ exec { 'do it':
512
+ command => $secret,
513
+ path => '/bin/'
514
+ }
515
+ CODE
516
+
517
+ @apply.options[:write_catalog_summary] = true
518
+
519
+ Puppet.settings[:resourcefile] = resourcefile
520
+ Puppet.settings[:classfile] = classfile
521
+
522
+ #We don't actually need the resource to do anything, we are using it's properties in other parts of the workflow.
523
+ Puppet::Util::Execution.stubs(:execute)
524
+
525
+ expect { @apply.main }.to exit_with 0
526
+
527
+ result = File.read(resourcefile)
528
+
529
+ expect(result).not_to match(/secret_file_name/)
530
+ expect(result).to match(/do it/)
531
+ end
532
+ end
533
+
495
534
  describe "apply_catalog" do
496
535
  it "should call the configurer with the catalog" do
497
536
  catalog = "I am a catalog"
@@ -416,7 +416,7 @@ EOT
416
416
  # it, but this helps us fail if that slips up and all. --daniel 2011-04-27
417
417
  Puppet::Face[:help, :current].expects(:help).never
418
418
 
419
- Puppet.expects(:err).with("Could not parse application options: I don't know how to render 'interpretive-dance'")
419
+ Puppet.expects(:send_log).with(:err, "Could not parse application options: I don't know how to render 'interpretive-dance'")
420
420
 
421
421
  expect { app.run }.to exit_with(1)
422
422
  end
@@ -2,88 +2,22 @@ require 'spec_helper'
2
2
  require 'puppet/application/ssl'
3
3
  require 'webmock/rspec'
4
4
  require 'openssl'
5
+ require 'puppet/test_ca'
5
6
 
6
7
  describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
7
- let(:ssl) { Puppet::Application[:ssl] }
8
- let(:name) { 'ssl-client' }
9
-
10
- def generate_cert(name, issuer = nil, issuer_key = nil)
11
- # generate CA key pair
12
- private_key = OpenSSL::PKey::RSA.new(512)
13
- public_key = private_key.public_key
14
-
15
- # generate CSR
16
- csr = OpenSSL::X509::Request.new
17
- csr.version = 0
18
- csr.subject = OpenSSL::X509::Name.new([["CN", name]])
19
- csr.public_key = public_key
20
- csr.sign(private_key, OpenSSL::Digest::SHA256.new)
21
-
22
- # is it self-signed?
23
- issuer ||= csr.subject
24
- issuer_key ||= private_key
25
-
26
- # issue cert
27
- cert = OpenSSL::X509::Certificate.new
28
- cert.version = 2 # X509v3
29
- cert.subject = csr.subject
30
- cert.issuer = issuer
31
- cert.public_key = csr.public_key
32
- cert.serial = 1
33
- cert.not_before = Time.now - (60*60*24)
34
- cert.not_after = Time.now + (60*60*24)
35
-
36
- # Ugly hack, but we need to make sure the CA cert has the proper
37
- # X509v3 Basic Constraints to be a valid CA certificate.
38
- if name == 'ca'
39
- extension_factory = OpenSSL::X509::ExtensionFactory.new
40
- extension_factory.subject_certificate = cert
41
- extension_factory.issuer_certificate = cert
42
-
43
- extensions = {
44
- "keyUsage" => [%w{cRLSign keyCertSign}, true],
45
- "basicConstraints" => ["CA:TRUE", true],
46
- }
47
-
48
- cert.extensions = extensions.map do |oid, (val, crit)|
49
- val = val.join(', ') unless val.is_a? String
50
-
51
- if Puppet::SSL::Oids.subtree_of?('id-ce', oid) or Puppet::SSL::Oids.subtree_of?('id-pkix', oid)
52
- # Attempt to create a X509v3 certificate extension. Standard certificate
53
- # extensions may need access to the associated subject certificate and
54
- # issuing certificate, so must be created by the OpenSSL::X509::ExtensionFactory
55
- # which provides that context.
56
- extension_factory.create_ext(oid, val, crit)
57
- else
58
- # This is not an X509v3 extension which means that the extension
59
- # factory cannot generate it. We need to generate the extension
60
- # manually.
61
- OpenSSL::X509::Extension.new(oid, OpenSSL::ASN1::UTF8String.new(val).to_der, crit)
62
- end
63
- end
64
- end
65
-
66
- cert.sign(issuer_key, OpenSSL::Digest::SHA256.new)
67
-
68
- {:private_key => private_key, :csr => csr, :cert => cert}
69
- end
70
-
71
- def generate_crl(name, issuer, issuer_key)
72
- crl = OpenSSL::X509::CRL.new
73
- crl.version = 1
74
- crl.issuer = issuer
75
- crl.last_update = Time.now - (60*60*24)
76
- crl.next_update = Time.now + (60*60*24)
77
- crl.extensions = [OpenSSL::X509::Extension.new('crlNumber', OpenSSL::ASN1::Integer(0))]
78
- crl.sign(issuer_key, OpenSSL::Digest::SHA256.new)
79
-
80
- crl
8
+ let(:ssl) do
9
+ app = Puppet::Application[:ssl]
10
+ app.options[:verbose] = true
11
+ app.setup_logs
12
+ app
81
13
  end
14
+ let(:name) { 'ssl-client' }
82
15
 
83
16
  before :all do
84
- @ca = generate_cert('ca')
85
- @crl = generate_crl('ca', @ca[:cert].subject, @ca[:private_key])
86
- @host = generate_cert('ssl-client', @ca[:cert].subject, @ca[:private_key])
17
+ @ca = Puppet::TestCa.new
18
+ @ca_cert = @ca.ca_cert
19
+ @crl = @ca.ca_crl
20
+ @host = @ca.generate('ssl-client', {})
87
21
  end
88
22
 
89
23
  before do
@@ -93,7 +27,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
93
27
  Puppet[:certname] = name
94
28
 
95
29
  # Host assumes ca cert and crl are present
96
- File.open(Puppet[:localcacert], 'w') { |f| f.write(@ca[:cert].to_pem) }
30
+ File.open(Puppet[:localcacert], 'w') { |f| f.write(@ca_cert.to_pem) }
97
31
  File.open(Puppet[:hostcrl], 'w') { |f| f.write(@crl.to_pem) }
98
32
 
99
33
  # Setup our ssl client
@@ -101,25 +35,31 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
101
35
  File.open(Puppet[:hostpubkey], 'w') { |f| f.write(@host[:private_key].public_key.to_pem) }
102
36
  end
103
37
 
104
- def expects_command_to_output(expected_message = nil, code = 0)
38
+ def expects_command_to_pass(expected_output = nil)
39
+ expect {
40
+ ssl.run_command
41
+ }.to have_printed(expected_output)
42
+ end
43
+
44
+ def expects_command_to_fail(message)
105
45
  expect {
106
46
  expect {
107
47
  ssl.run_command
108
- }.to exit_with(code)
109
- }.to output(expected_message).to_stdout
48
+ }.to raise_error(Puppet::Error, message)
49
+ }.to have_printed(/.*/) # ignore output
110
50
  end
111
51
 
112
52
  shared_examples_for 'an ssl action' do
113
53
  it 'downloads the CA bundle first when missing' do
114
54
  File.delete(Puppet[:localcacert])
115
- stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: @ca[:cert].to_pem)
55
+ stub_request(:get, %r{puppet-ca/v1/certificate/ca}).to_return(status: 200, body: @ca.ca_cert.to_pem)
116
56
  stub_request(:get, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 404)
117
57
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
118
- stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
58
+ stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
119
59
 
120
- expects_command_to_output
60
+ expects_command_to_pass
121
61
 
122
- expect(File.read(Puppet[:localcacert])).to eq(@ca[:cert].to_pem)
62
+ expect(File.read(Puppet[:localcacert])).to eq(@ca.ca_cert.to_pem)
123
63
  end
124
64
 
125
65
  it 'downloads the CRL bundle first when missing' do
@@ -127,23 +67,29 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
127
67
  stub_request(:get, %r{puppet-ca/v1/certificate_revocation_list/ca}).to_return(status: 200, body: @crl.to_pem)
128
68
  stub_request(:get, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 404)
129
69
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
130
- stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
70
+ stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
131
71
 
132
- expects_command_to_output
72
+ expects_command_to_pass
133
73
 
134
74
  expect(File.read(Puppet[:hostcrl])).to eq(@crl.to_pem)
135
75
  end
136
76
  end
137
77
 
78
+ it 'uses the agent run mode' do
79
+ # make sure the ssl app resolves certname, server, etc
80
+ # the same way the agent application does
81
+ expect(ssl.class.run_mode.name).to eq(:agent)
82
+ end
83
+
138
84
  context 'when generating help' do
139
- it 'prints usage when no arguments are specified' do
85
+ it 'prints a message when an unknown action is specified' do
140
86
  ssl.command_line.args << 'whoops'
141
87
 
142
- expects_command_to_output(/Unknown action 'whoops'/, 1)
88
+ expects_command_to_fail(/Unknown action 'whoops'/)
143
89
  end
144
90
 
145
- it 'rejects unknown actions' do
146
- expects_command_to_output(/^puppet-ssl.*SYNOPSIS/m, 1)
91
+ it 'prints a message requiring an action to be specified' do
92
+ expects_command_to_fail(/An action must be specified/)
147
93
  end
148
94
  end
149
95
 
@@ -161,7 +107,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
161
107
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
162
108
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
163
109
 
164
- expects_command_to_output(%r{Submitted certificate request for '#{name}' to https://.*}, 0)
110
+ expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})
165
111
 
166
112
  expect(Puppet::FileSystem).to be_exist(csr_path)
167
113
  end
@@ -171,13 +117,13 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
171
117
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
172
118
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
173
119
 
174
- expects_command_to_output(%r{Submitted certificate request for '#{name}' to https://.*}, 0)
120
+ expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})
175
121
 
176
122
  stub_request(:get, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200, body: @host[:csr].to_pem)
177
123
  # we skip :put
178
124
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
179
125
 
180
- expects_command_to_output
126
+ expects_command_to_pass
181
127
  end
182
128
 
183
129
  it "warns if the local CSR doesn't match the local public key, and submits a new CSR" do
@@ -199,7 +145,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
199
145
 
200
146
  Puppet.stubs(:warning) # ignore unrelated warnings
201
147
  Puppet.expects(:warning).with("The local CSR does not match the agent's public key. Generating a new CSR.")
202
- expects_command_to_output(%r{Submitted certificate request for '#{name}' to https://.*}, 0)
148
+ expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})
203
149
  end
204
150
 
205
151
  it 'downloads the certificate when autosigning is enabled' do
@@ -207,7 +153,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
207
153
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
208
154
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
209
155
 
210
- expects_command_to_output(%r{Submitted certificate request for '#{name}' to https://.*}, 0)
156
+ expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})
211
157
 
212
158
  expect(Puppet::FileSystem).to be_exist(Puppet[:hostcert])
213
159
  end
@@ -219,7 +165,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
219
165
  stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
220
166
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
221
167
 
222
- expects_command_to_output
168
+ expects_command_to_pass
223
169
 
224
170
  csr = Puppet::SSL::CertificateRequest.new(name)
225
171
  csr.read(csr_path)
@@ -237,7 +183,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
237
183
  it 'downloads a new cert' do
238
184
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
239
185
 
240
- expects_command_to_output(%r{Downloaded certificate '#{name}' with fingerprint .*})
186
+ expects_command_to_pass(%r{Downloaded certificate '#{name}' with fingerprint .*})
241
187
 
242
188
  expect(File.read(Puppet[:hostcert])).to eq(@host[:cert].to_pem)
243
189
  end
@@ -247,7 +193,7 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
247
193
 
248
194
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
249
195
 
250
- expects_command_to_output(%r{Downloaded certificate '#{name}' with fingerprint .*})
196
+ expects_command_to_pass(%r{Downloaded certificate '#{name}' with fingerprint .*})
251
197
 
252
198
  expect(File.read(Puppet[:hostcert])).to eq(@host[:cert].to_pem)
253
199
  end
@@ -262,13 +208,15 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
262
208
 
263
209
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
264
210
 
265
- expects_command_to_output(%r{^Failed to download certificate: The certificate retrieved from the master does not match the agent's private key. Did you forget to run as root?}, 1)
211
+ expects_command_to_fail(
212
+ %r{^Failed to download certificate: The certificate retrieved from the master does not match the agent's private key. Did you forget to run as root?}
213
+ )
266
214
  end
267
215
 
268
216
  it "prints a message if there isn't a cert to download" do
269
217
  stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
270
218
 
271
- expects_command_to_output(/No certificate for '#{name}' on CA/)
219
+ expects_command_to_fail(/The certificate for '#{name}' has not yet been signed/)
272
220
  end
273
221
  end
274
222
 
@@ -282,13 +230,13 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
282
230
  it 'reports if the key is missing' do
283
231
  File.delete(Puppet[:hostprivkey])
284
232
 
285
- expects_command_to_output(/The host's private key is missing/, 1)
233
+ expects_command_to_fail(/The host's private key is missing/)
286
234
  end
287
235
 
288
236
  it 'reports if the cert is missing' do
289
237
  File.delete(Puppet[:hostcert])
290
238
 
291
- expects_command_to_output(/The host's certificate is missing/, 1)
239
+ expects_command_to_fail(/The host's certificate is missing/)
292
240
  end
293
241
 
294
242
  it 'reports if the key and cert are mismatched' do
@@ -298,25 +246,127 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
298
246
  File.open(Puppet[:hostprivkey], 'w') { |f| f.write(private_key.to_pem) }
299
247
  File.open(Puppet[:hostpubkey], 'w') { |f| f.write(public_key.to_pem) }
300
248
 
301
- expects_command_to_output(/The host's key does not match the certificate/, 1)
249
+ expects_command_to_fail(/The host's key does not match the certificate/)
302
250
  end
303
251
 
304
252
  it 'reports if the cert verification fails' do
305
253
  # generate a new CA to force an error
306
- ca = generate_cert('ca')
307
- File.open(Puppet[:localcacert], 'w') { |f| f.write(ca[:cert].to_pem) }
254
+ new_ca = Puppet::TestCa.new
255
+ File.open(Puppet[:localcacert], 'w') { |f| f.write(new_ca.ca_cert.to_pem) }
308
256
 
309
257
  # and CRL for that CA
310
- crl = generate_crl('ca', ca[:cert].subject, ca[:private_key])
311
- File.open(Puppet[:hostcrl], 'w') { |f| f.write(crl.to_pem) }
258
+ File.open(Puppet[:hostcrl], 'w') { |f| f.write(new_ca.ca_crl.to_pem) }
312
259
 
313
- expects_command_to_output(/Failed to verify certificate '#{name}': certificate signature failure \(7\)/, 1)
260
+ expects_command_to_fail(
261
+ /Failed to verify certificate '#{name}': certificate signature failure \(7\)/
262
+ )
314
263
  end
315
264
 
316
265
  it 'reports when verification succeeds' do
317
266
  OpenSSL::X509::Store.any_instance.stubs(:verify).returns(true)
318
267
 
319
- expects_command_to_output(/Verified certificate '#{name}'/, 0)
268
+ expects_command_to_pass(/Verified certificate '#{name}'/)
269
+ end
270
+ end
271
+
272
+ context 'when cleaning' do
273
+ before do
274
+ ssl.command_line.args << 'clean'
275
+ end
276
+
277
+ it 'deletes the hostcert' do
278
+ File.open(Puppet[:hostcert], 'w') { |f| f.write(@host[:cert].to_pem) }
279
+
280
+ expects_command_to_pass(%r{Removed certificate #{Puppet[:cert]}})
281
+ end
282
+
283
+ it 'deletes the private key' do
284
+ File.open(Puppet[:hostprivkey], 'w') { |f| f.write(@host[:private_key].to_pem) }
285
+
286
+ expects_command_to_pass(%r{Removed private key #{Puppet[:hostprivkey]}})
287
+ end
288
+
289
+ it 'deletes the public key' do
290
+ File.open(Puppet[:hostpubkey], 'w') { |f| f.write(@host[:private_key].public_key.to_pem) }
291
+
292
+ expects_command_to_pass(%r{Removed public key #{Puppet[:hostpubkey]}})
293
+ end
294
+
295
+ it 'deletes the request' do
296
+ File.open(Puppet[:hostcsr], 'w') { |f| f.write(@host[:csr].to_pem) }
297
+
298
+ expects_command_to_pass(%r{Removed certificate request #{Puppet[:hostcsr]}})
299
+ end
300
+
301
+ it 'deletes the passfile' do
302
+ File.open(Puppet[:passfile], 'w') { |_| }
303
+
304
+ expects_command_to_pass(%r{Removed private key password file #{Puppet[:passfile]}})
305
+ end
306
+
307
+ it 'skips files that do not exist' do
308
+ File.delete(Puppet[:hostprivkey])
309
+
310
+ expect {
311
+ ssl.run_command
312
+ }.to_not output(%r{Removed private key #{Puppet[:hostprivkey]}}).to_stdout
313
+ end
314
+
315
+ it "raises if we fail to retrieve server's cert that we're about to clean" do
316
+ Puppet[:certname] = name
317
+ Puppet[:server] = name
318
+
319
+ stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_raise(Errno::ECONNREFUSED)
320
+
321
+ expects_command_to_fail(%r{Failed to connect to the CA to determine if certificate #{name} has been cleaned})
322
+ end
323
+
324
+ context 'when deleting local CA' do
325
+ before do
326
+ ssl.command_line.args << '--localca'
327
+ ssl.parse_options
328
+ end
329
+
330
+ it 'deletes the local CA cert' do
331
+ File.open(Puppet[:localcacert], 'w') { |f| f.write(@ca_cert.to_pem) }
332
+
333
+ expects_command_to_pass(%r{Removed local CA certificate #{Puppet[:localcacert]}})
334
+ end
335
+
336
+ it 'deletes the local CRL' do
337
+ File.open(Puppet[:hostcrl], 'w') { |f| f.write(@crl.to_pem) }
338
+
339
+ expects_command_to_pass(%r{Removed local CRL #{Puppet[:hostcrl]}})
340
+ end
341
+ end
342
+
343
+ context 'on the puppetserver host' do
344
+ before :each do
345
+ Puppet[:certname] = 'puppetserver'
346
+ Puppet[:server] = 'puppetserver'
347
+ end
348
+
349
+ it "prints an error when the CA is local and the CA has not cleaned its cert" do
350
+ stub_request(:get, %r{puppet-ca/v1/certificate/puppetserver}).to_return(status: 200, body: @host[:cert].to_pem)
351
+
352
+ expects_command_to_fail(%r{The certificate puppetserver must be cleaned from the CA first})
353
+ end
354
+
355
+ it "cleans the cert when the CA is local and the CA has already cleaned its cert" do
356
+ File.open(Puppet[:hostcert], 'w') { |f| f.write(@host[:cert].to_pem) }
357
+
358
+ stub_request(:get, %r{puppet-ca/v1/certificate/puppetserver}).to_return(status: 404)
359
+
360
+ expects_command_to_pass(%r{Removed certificate .*puppetserver.pem})
361
+ end
362
+
363
+ it "cleans the cert when run on a puppetserver that isn't the CA" do
364
+ File.open(Puppet[:hostcert], 'w') { |f| f.write(@host[:cert].to_pem) }
365
+
366
+ Puppet[:ca_server] = 'caserver'
367
+
368
+ expects_command_to_pass(%r{Removed certificate .*puppetserver.pem})
369
+ end
320
370
  end
321
371
  end
322
372
  end