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.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/Gemfile.lock +11 -11
- data/lib/puppet/application.rb +5 -0
- data/lib/puppet/application/apply.rb +1 -0
- data/lib/puppet/application/script.rb +1 -1
- data/lib/puppet/application/ssl.rb +119 -49
- data/lib/puppet/defaults.rb +9 -27
- data/lib/puppet/face/node/clean.rb +0 -1
- data/lib/puppet/feature/base.rb +1 -1
- data/lib/puppet/file_serving/fileset.rb +1 -1
- data/lib/puppet/pops/validation/checker4_0.rb +4 -2
- data/lib/puppet/provider/package/windows.rb +2 -2
- data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
- data/lib/puppet/provider/service/windows.rb +11 -3
- data/lib/puppet/provider/user/useradd.rb +2 -10
- data/lib/puppet/resource/catalog.rb +1 -5
- data/lib/puppet/ssl/host.rb +7 -9
- data/lib/puppet/transaction/persistence.rb +1 -1
- data/lib/puppet/type/package.rb +1 -1
- data/lib/puppet/type/user.rb +4 -1
- data/lib/puppet/util.rb +7 -3
- data/lib/puppet/util/execution.rb +1 -0
- data/lib/puppet/util/logging.rb +3 -2
- data/lib/puppet/util/windows/process.rb +6 -2
- data/lib/puppet/util/windows/security.rb +14 -0
- data/lib/puppet/util/windows/service.rb +217 -74
- data/lib/puppet/util/windows/user.rb +3 -5
- data/lib/puppet/version.rb +1 -1
- data/locales/ja/puppet.po +505 -276
- data/locales/puppet.pot +250 -111
- data/man/man5/puppet.conf.5 +8 -1
- data/man/man8/puppet-ssl.8 +22 -2
- data/man/man8/puppet.8 +1 -1
- data/spec/integration/parser/collection_spec.rb +4 -8
- data/spec/integration/type/file_spec.rb +6 -6
- data/spec/integration/util/windows/security_spec.rb +10 -7
- data/spec/integration/util/windows/user_spec.rb +37 -17
- data/spec/lib/puppet/test_ca.rb +1 -1
- data/spec/unit/agent_spec.rb +2 -2
- data/spec/unit/application/apply_spec.rb +41 -2
- data/spec/unit/application/face_base_spec.rb +1 -1
- data/spec/unit/application/ssl_spec.rb +160 -110
- data/spec/unit/application_spec.rb +29 -11
- data/spec/unit/configurer/downloader_spec.rb +1 -1
- data/spec/unit/configurer_spec.rb +5 -5
- data/spec/unit/face/node_spec.rb +1 -3
- data/spec/unit/file_serving/fileset_spec.rb +11 -11
- data/spec/unit/network/http/connection_spec.rb +2 -2
- data/spec/unit/pops/validator/validator_spec.rb +24 -10
- data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
- data/spec/unit/provider/package/windows_spec.rb +4 -4
- data/spec/unit/provider/service/windows_spec.rb +21 -3
- data/spec/unit/provider/user/useradd_spec.rb +2 -2
- data/spec/unit/resource/catalog_spec.rb +2 -2
- data/spec/unit/ssl/host_spec.rb +1 -1
- data/spec/unit/transaction/persistence_spec.rb +4 -4
- data/spec/unit/util/execution_spec.rb +19 -1
- data/spec/unit/util/logging_spec.rb +58 -0
- data/spec/unit/util/windows/service_spec.rb +344 -191
- metadata +2 -2
data/man/man5/puppet.conf.5
CHANGED
@@ -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\.
|
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
|
data/man/man8/puppet-ssl.8
CHANGED
@@ -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
|
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 [
|
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
|
|
data/man/man8/puppet.8
CHANGED
@@ -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\.
|
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::
|
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::
|
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
|
-
|
290
|
-
|
291
|
-
|
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
|
-
|
638
|
-
|
639
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
47
|
-
Puppet::Util::Windows::User.expects(:check_token_membership).never
|
45
|
+
expect(Puppet::Util::Windows::User).to be_admin
|
46
|
+
end
|
48
47
|
|
49
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
|
data/spec/lib/puppet/test_ca.rb
CHANGED
data/spec/unit/agent_spec.rb
CHANGED
@@ -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(:
|
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(:
|
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(:
|
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)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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 =
|
85
|
-
@
|
86
|
-
@
|
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(@
|
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
|
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
|
109
|
-
}.to
|
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
|
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:
|
58
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
|
119
59
|
|
120
|
-
|
60
|
+
expects_command_to_pass
|
121
61
|
|
122
|
-
expect(File.read(Puppet[:localcacert])).to eq(@ca
|
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:
|
70
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 200, body: @host[:cert].to_pem)
|
131
71
|
|
132
|
-
|
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
|
85
|
+
it 'prints a message when an unknown action is specified' do
|
140
86
|
ssl.command_line.args << 'whoops'
|
141
87
|
|
142
|
-
|
88
|
+
expects_command_to_fail(/Unknown action 'whoops'/)
|
143
89
|
end
|
144
90
|
|
145
|
-
it '
|
146
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
307
|
-
File.open(Puppet[:localcacert], 'w') { |f| f.write(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|