puppet 6.3.0-x86-mingw32 → 6.4.0-x86-mingw32
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/CODEOWNERS +30 -0
- data/Gemfile.lock +9 -9
- data/lib/puppet.rb +13 -0
- data/lib/puppet/application/agent.rb +8 -12
- data/lib/puppet/application/device.rb +2 -3
- data/lib/puppet/application/filebucket.rb +6 -1
- data/lib/puppet/application/ssl.rb +102 -55
- data/lib/puppet/configurer.rb +8 -7
- data/lib/puppet/defaults.rb +3 -1
- data/lib/puppet/file_system.rb +24 -4
- data/lib/puppet/file_system/file_impl.rb +25 -0
- data/lib/puppet/file_system/jruby.rb +23 -0
- data/lib/puppet/file_system/windows.rb +84 -0
- data/lib/puppet/indirector/rest.rb +4 -2
- data/lib/puppet/loaders.rb +1 -0
- data/lib/puppet/network/http.rb +1 -0
- data/lib/puppet/network/http/base_pool.rb +18 -0
- data/lib/puppet/network/http/connection.rb +49 -17
- data/lib/puppet/network/http/nocache_pool.rb +9 -4
- data/lib/puppet/network/http/pool.rb +10 -11
- data/lib/puppet/network/http/session.rb +3 -2
- data/lib/puppet/network/http_pool.rb +32 -0
- data/lib/puppet/pops/loader/generic_plan_instantiator.rb +28 -0
- data/lib/puppet/pops/loader/loader_paths.rb +46 -10
- data/lib/puppet/pops/loader/module_loaders.rb +10 -3
- data/lib/puppet/provider/file/windows.rb +49 -1
- data/lib/puppet/provider/package/windows.rb +5 -1
- data/lib/puppet/reports/http.rb +2 -1
- data/lib/puppet/rest/client.rb +7 -3
- data/lib/puppet/rest/routes.rb +9 -44
- data/lib/puppet/ssl.rb +6 -0
- data/lib/puppet/ssl/error.rb +26 -0
- data/lib/puppet/ssl/host.rb +9 -92
- data/lib/puppet/ssl/ssl_context.rb +30 -0
- data/lib/puppet/ssl/ssl_provider.rb +232 -0
- data/lib/puppet/ssl/state_machine.rb +261 -0
- data/lib/puppet/ssl/validator.rb +1 -0
- data/lib/puppet/ssl/validator/default_validator.rb +1 -0
- data/lib/puppet/ssl/validator/no_validator.rb +2 -0
- data/lib/puppet/ssl/verifier.rb +134 -0
- data/lib/puppet/ssl/verifier_adapter.rb +48 -0
- data/lib/puppet/test/test_helper.rb +2 -1
- data/lib/puppet/type/exec.rb +30 -6
- data/lib/puppet/type/file/mode.rb +6 -1
- data/lib/puppet/type/file/source.rb +2 -2
- data/lib/puppet/type/filebucket.rb +12 -8
- data/lib/puppet/type/user.rb +14 -1
- data/lib/puppet/util/connection.rb +10 -5
- data/lib/puppet/util/feature.rb +11 -2
- data/lib/puppet/util/http_proxy.rb +3 -2
- data/lib/puppet/util/pidlock.rb +1 -1
- data/lib/puppet/util/ssl.rb +1 -10
- data/lib/puppet/util/windows/security.rb +29 -8
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet/x509.rb +7 -0
- data/lib/puppet/x509/cert_provider.rb +286 -0
- data/lib/puppet/x509/pem_store.rb +55 -0
- data/locales/ja/puppet.po +740 -590
- data/locales/puppet.pot +433 -208
- data/man/man5/puppet.conf.5 +6 -3
- data/man/man8/puppet-agent.8 +1 -1
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +6 -2
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-man.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-ssl.8 +5 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/ssl/127.0.0.1-key.pem +67 -0
- data/spec/fixtures/ssl/127.0.0.1.pem +48 -0
- data/spec/fixtures/ssl/bad-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/bad-int-basic-constraints.pem +59 -0
- data/spec/fixtures/ssl/ca.pem +59 -0
- data/spec/fixtures/ssl/crl.pem +30 -0
- data/spec/fixtures/ssl/encrypted-key.pem +70 -0
- data/spec/fixtures/ssl/intermediate-agent-crl.pem +31 -0
- data/spec/fixtures/ssl/intermediate-agent.pem +60 -0
- data/spec/fixtures/ssl/intermediate-crl.pem +36 -0
- data/spec/fixtures/ssl/intermediate.pem +60 -0
- data/spec/fixtures/ssl/netlock-arany-utf8.pem +23 -0
- data/spec/fixtures/ssl/pluto-key.pem +67 -0
- data/spec/fixtures/ssl/pluto.pem +44 -0
- data/spec/fixtures/ssl/request-key.pem +67 -0
- data/spec/fixtures/ssl/request.pem +39 -0
- data/spec/fixtures/ssl/revoked-key.pem +67 -0
- data/spec/fixtures/ssl/revoked.pem +44 -0
- data/spec/fixtures/ssl/signed-key.pem +67 -0
- data/spec/fixtures/ssl/signed.pem +44 -0
- data/spec/fixtures/ssl/tampered-cert.pem +44 -0
- data/spec/fixtures/ssl/tampered-csr.pem +39 -0
- data/spec/integration/network/http_pool_spec.rb +222 -0
- data/spec/integration/provider/file/windows_spec.rb +162 -0
- data/spec/integration/rest/client_spec.rb +73 -0
- data/spec/integration/type/file_spec.rb +0 -19
- data/spec/lib/puppet/test_ca.rb +87 -50
- data/spec/lib/puppet_spec/fixtures.rb +20 -0
- data/spec/lib/puppet_spec/https.rb +84 -0
- data/spec/unit/application/agent_spec.rb +29 -30
- data/spec/unit/application/device_spec.rb +12 -49
- data/spec/unit/application/ssl_spec.rb +24 -38
- data/spec/unit/configurer_spec.rb +11 -11
- data/spec/unit/file_system/uniquefile_spec.rb +6 -0
- data/spec/unit/file_system_spec.rb +214 -0
- data/spec/unit/indirector/rest_spec.rb +3 -3
- data/spec/unit/network/http/connection_spec.rb +30 -90
- data/spec/unit/network/http/factory_spec.rb +1 -0
- data/spec/unit/network/http/nocache_pool_spec.rb +8 -8
- data/spec/unit/network/http/pool_spec.rb +63 -33
- data/spec/unit/network/http/session_spec.rb +8 -1
- data/spec/unit/network/http_pool_spec.rb +36 -0
- data/spec/unit/pops/loaders/loader_spec.rb +26 -1
- data/spec/unit/provider/package/windows_spec.rb +12 -1
- data/spec/unit/reports/http_spec.rb +7 -7
- data/spec/unit/rest/client_spec.rb +4 -6
- data/spec/unit/ssl/host_spec.rb +39 -33
- data/spec/unit/ssl/ssl_provider_spec.rb +428 -0
- data/spec/unit/ssl/state_machine_spec.rb +502 -0
- data/spec/unit/ssl/verifier_spec.rb +123 -0
- data/spec/unit/type/exec_spec.rb +63 -0
- data/spec/unit/type/file/source_spec.rb +5 -5
- data/spec/unit/type/filebucket_spec.rb +8 -6
- data/spec/unit/util/feature_spec.rb +2 -2
- data/spec/unit/util/storage_spec.rb +19 -19
- data/spec/unit/x509/cert_provider_spec.rb +527 -0
- data/spec/unit/x509/pem_store_spec.rb +160 -0
- data/tasks/generate_cert_fixtures.rake +158 -0
- metadata +78 -4
- data/MAINTAINERS +0 -47
- data/lib/puppet/rest/ssl_context.rb +0 -13
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'puppet_spec/https'
|
3
|
+
require 'puppet_spec/files'
|
4
|
+
require 'puppet/network/http_pool'
|
5
|
+
|
6
|
+
describe Puppet::Network::HttpPool, unless: Puppet::Util::Platform.jruby? do
|
7
|
+
include PuppetSpec::Files
|
8
|
+
|
9
|
+
before :all do
|
10
|
+
WebMock.disable!
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
WebMock.enable!
|
15
|
+
end
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
# make sure we don't take too long
|
19
|
+
Puppet[:http_connect_timeout] = '5s'
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:hostname) { '127.0.0.1' }
|
23
|
+
let(:wrong_hostname) { 'localhost' }
|
24
|
+
let(:server) { PuppetSpec::HTTPSServer.new }
|
25
|
+
|
26
|
+
context "when calling deprecated HttpPool methods" do
|
27
|
+
let(:ssl_host) {
|
28
|
+
# use server's cert/key as the client cert/key
|
29
|
+
host = Puppet::SSL::Host.new
|
30
|
+
host.key = Puppet::SSL::Key.from_instance(server.server_key, host.name)
|
31
|
+
host.certificate = Puppet::SSL::Certificate.from_instance(server.server_cert, host.name)
|
32
|
+
host
|
33
|
+
}
|
34
|
+
|
35
|
+
before(:each) do
|
36
|
+
ssldir = tmpdir('http_pool')
|
37
|
+
Puppet[:ssldir] = ssldir
|
38
|
+
Puppet.settings.use(:main, :ssl)
|
39
|
+
|
40
|
+
File.write(Puppet[:localcacert], server.ca_cert.to_pem)
|
41
|
+
File.write(Puppet[:hostcrl], server.ca_crl.to_pem)
|
42
|
+
File.write(Puppet[:hostcert], server.server_cert.to_pem)
|
43
|
+
File.write(Puppet[:hostprivkey], server.server_key.to_pem)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Can't use `around(:each)` because it will cause ssl_host to be
|
47
|
+
# created outside of any rspec example, and $confdir won't be set
|
48
|
+
before(:each) do
|
49
|
+
Puppet.push_context(ssl_host: ssl_host)
|
50
|
+
end
|
51
|
+
|
52
|
+
after (:each) do
|
53
|
+
Puppet.pop_context
|
54
|
+
end
|
55
|
+
|
56
|
+
def connection(host, port)
|
57
|
+
Puppet::Network::HttpPool.http_instance(host, port, use_ssl: true)
|
58
|
+
end
|
59
|
+
|
60
|
+
shared_examples_for 'HTTPS client' do
|
61
|
+
it "connects over SSL" do
|
62
|
+
server.start_server do |port|
|
63
|
+
http = connection(hostname, port)
|
64
|
+
res = http.get('/')
|
65
|
+
expect(res.code).to eq('200')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "raises if the server's cert doesn't match the hostname we connected to" do
|
70
|
+
server.start_server do |port|
|
71
|
+
http = connection(wrong_hostname, port)
|
72
|
+
expect {
|
73
|
+
http.get('/')
|
74
|
+
}.to raise_error { |err|
|
75
|
+
pending("PUP-8213") if RUBY_VERSION.to_f >= 2.4
|
76
|
+
|
77
|
+
expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)
|
78
|
+
expect(err.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
|
79
|
+
|
80
|
+
md = err.message.match(/expected one of (.+)/)
|
81
|
+
expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises if the server's CA is unknown" do
|
87
|
+
# File must exist and by not empty so DefaultValidator doesn't
|
88
|
+
# downgrade to VERIFY_NONE, so use a different CA that didn't
|
89
|
+
# issue the server's cert
|
90
|
+
capath = tmpfile('empty')
|
91
|
+
File.write(capath, cert_fixture('netlock-arany-utf8.pem'))
|
92
|
+
Puppet[:localcacert] = capath
|
93
|
+
Puppet[:certificate_revocation] = false
|
94
|
+
|
95
|
+
server.start_server do |port|
|
96
|
+
http = connection(hostname, port)
|
97
|
+
expect {
|
98
|
+
http.get('/')
|
99
|
+
}.to raise_error(Puppet::Error,
|
100
|
+
%r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
context "when using single use HTTPS connections" do
|
107
|
+
include_examples 'HTTPS client'
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when using persistent HTTPS connections" do
|
111
|
+
around :each do |example|
|
112
|
+
pool = Puppet::Network::HTTP::Pool.new
|
113
|
+
Puppet.override(:http_pool => pool) do
|
114
|
+
example.run
|
115
|
+
end
|
116
|
+
pool.close
|
117
|
+
end
|
118
|
+
|
119
|
+
include_examples 'HTTPS client'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when calling HttpPool.connection method" do
|
124
|
+
let(:ssl) { Puppet::SSL::SSLProvider.new }
|
125
|
+
let(:ssl_context) { ssl.create_root_context(cacerts: [server.ca_cert], crls: [server.ca_crl]) }
|
126
|
+
|
127
|
+
def connection(host, port, ssl_context:)
|
128
|
+
Puppet::Network::HttpPool.connection(host, port, ssl_context: ssl_context)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Configure the server's SSLContext to require a client certificate. The `client_ca`
|
132
|
+
# setting allows the server to advertise which client CAs it will accept.
|
133
|
+
def require_client_certs(ctx)
|
134
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
135
|
+
ctx.client_ca = [cert_fixture('ca.pem')]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "connects over SSL" do
|
139
|
+
server.start_server do |port|
|
140
|
+
http = connection(hostname, port, ssl_context: ssl_context)
|
141
|
+
res = http.get('/')
|
142
|
+
expect(res.code).to eq('200')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "raises if the server's cert doesn't match the hostname we connected to" do
|
147
|
+
server.start_server do |port|
|
148
|
+
http = connection(wrong_hostname, port, ssl_context: ssl_context)
|
149
|
+
expect {
|
150
|
+
http.get('/')
|
151
|
+
}.to raise_error { |err|
|
152
|
+
expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)
|
153
|
+
expect(err.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
|
154
|
+
|
155
|
+
md = err.message.match(/expected one of (.+)/)
|
156
|
+
expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
|
157
|
+
}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it "raises if the server's CA is unknown" do
|
162
|
+
server.start_server do |port|
|
163
|
+
ssl_context = ssl.create_root_context(cacerts: [cert_fixture('netlock-arany-utf8.pem')],
|
164
|
+
crls: [server.ca_crl])
|
165
|
+
http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
|
166
|
+
expect {
|
167
|
+
http.get('/')
|
168
|
+
}.to raise_error(Puppet::Error,
|
169
|
+
%r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "warns when client has an incomplete client cert chain" do
|
174
|
+
Puppet.expects(:warning).with("The issuer '/CN=Test CA Agent Subauthority' of certificate '/CN=pluto' cannot be found locally")
|
175
|
+
|
176
|
+
pluto = cert_fixture('pluto.pem')
|
177
|
+
|
178
|
+
ssl_context = ssl.create_context(
|
179
|
+
cacerts: [server.ca_cert], crls: [server.ca_crl],
|
180
|
+
client_cert: pluto, private_key: key_fixture('pluto-key.pem')
|
181
|
+
)
|
182
|
+
|
183
|
+
# verify client has incomplete chain
|
184
|
+
expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto.to_der])
|
185
|
+
|
186
|
+
# force server to require (not request) client certs
|
187
|
+
ctx_proc = -> (ctx) {
|
188
|
+
require_client_certs(ctx)
|
189
|
+
|
190
|
+
# server needs to trust the client's intermediate CA to complete the client's chain
|
191
|
+
ctx.cert_store.add_cert(cert_fixture('intermediate-agent.pem'))
|
192
|
+
}
|
193
|
+
|
194
|
+
server.start_server(ctx_proc: ctx_proc) do |port|
|
195
|
+
http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
|
196
|
+
res = http.get('/')
|
197
|
+
expect(res.code).to eq('200')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it "sends a complete client cert chain" do
|
202
|
+
pluto = cert_fixture('pluto.pem')
|
203
|
+
client_ca = cert_fixture('intermediate-agent.pem')
|
204
|
+
|
205
|
+
ssl_context = ssl.create_context(
|
206
|
+
cacerts: [server.ca_cert, client_ca],
|
207
|
+
crls: [server.ca_crl, crl_fixture('intermediate-agent-crl.pem')],
|
208
|
+
client_cert: pluto,
|
209
|
+
private_key: key_fixture('pluto-key.pem')
|
210
|
+
)
|
211
|
+
|
212
|
+
# verify client has complete chain from leaf to root
|
213
|
+
expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto, client_ca, server.ca_cert].map(&:to_der))
|
214
|
+
|
215
|
+
server.start_server(ctx_proc: method(:require_client_certs)) do |port|
|
216
|
+
http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
|
217
|
+
res = http.get('/')
|
218
|
+
expect(res.code).to eq('200')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'puppet_spec/compiler'
|
3
|
+
require 'puppet_spec/files'
|
4
|
+
|
5
|
+
# For some reason the provider test will not filter out on windows when using the
|
6
|
+
# :if => Puppet.features.microsoft_windows? method of filtering the tests.
|
7
|
+
if Puppet.features.microsoft_windows?
|
8
|
+
require 'puppet/util/windows'
|
9
|
+
describe Puppet::Type.type(:file).provider(:windows), '(integration)' do
|
10
|
+
include PuppetSpec::Compiler
|
11
|
+
include PuppetSpec::Files
|
12
|
+
|
13
|
+
def create_temp_file(owner_sid, group_sid, initial_mode)
|
14
|
+
tmp_file = tmpfile('filewindowsprovider')
|
15
|
+
File.delete(tmp_file) if File.exist?(tmp_file)
|
16
|
+
File.open(tmp_file, 'w') { |file| file.write("rspec test") }
|
17
|
+
|
18
|
+
# There are other tests to ensure that these methods do indeed
|
19
|
+
# set the owner and group. Therefore it's ok to depend on them
|
20
|
+
# here
|
21
|
+
Puppet::Util::Windows::Security.set_owner(owner_sid, tmp_file) unless owner_sid.nil?
|
22
|
+
Puppet::Util::Windows::Security.set_group(group_sid, tmp_file) unless group_sid.nil?
|
23
|
+
# Pretend we are managing the owner and group to FORCE this mode, even if it's "bad"
|
24
|
+
Puppet::Util::Windows::Security.set_mode(initial_mode.to_i(8), tmp_file, true, true, true) unless initial_mode.nil?
|
25
|
+
|
26
|
+
tmp_file
|
27
|
+
end
|
28
|
+
|
29
|
+
def strip_sticky(value)
|
30
|
+
# For the purposes of these tests we don't care about the extra-ace bit in modes
|
31
|
+
# This function removes it
|
32
|
+
value & ~Puppet::Util::Windows::Security::S_IEXTRA
|
33
|
+
end
|
34
|
+
|
35
|
+
sids = {
|
36
|
+
:system => Puppet::Util::Windows::SID::LocalSystem,
|
37
|
+
:administrators => Puppet::Util::Windows::SID::BuiltinAdministrators,
|
38
|
+
:users => Puppet::Util::Windows::SID::BuiltinUsers,
|
39
|
+
:power_users => Puppet::Util::Windows::SID::PowerUsers,
|
40
|
+
:none => Puppet::Util::Windows::SID::Nobody,
|
41
|
+
:everyone => Puppet::Util::Windows::SID::Everyone
|
42
|
+
}
|
43
|
+
|
44
|
+
# Testcase Hash options
|
45
|
+
# create_* : These options are used when creating the initial test file
|
46
|
+
# create_owner (Required!)
|
47
|
+
# create_group (Required!)
|
48
|
+
# create_mode
|
49
|
+
#
|
50
|
+
# manifest_* : These options are used to craft the manifest which is applied to the test file after createion
|
51
|
+
# manifest_owner,
|
52
|
+
# manifest_group,
|
53
|
+
# manifest_mode (Required!)
|
54
|
+
#
|
55
|
+
# actual_* : These options are used to check the _actual_ values as opposed to the munged values from puppet
|
56
|
+
# actual_mode (Uses manifest_mode for checks if not set)
|
57
|
+
|
58
|
+
RSpec.shared_examples "a mungable file resource" do |testcase|
|
59
|
+
|
60
|
+
before(:each) do
|
61
|
+
@tmp_file = create_temp_file(sids[testcase[:create_owner]], sids[testcase[:create_group]], testcase[:create_mode])
|
62
|
+
raise "Could not create temporary file" if @tmp_file.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
after(:each) do
|
66
|
+
File.delete(@tmp_file) if File.exist?(@tmp_file)
|
67
|
+
end
|
68
|
+
|
69
|
+
context_name = "With initial owner '#{testcase[:create_owner]}' and initial group '#{testcase[:create_owner]}'"
|
70
|
+
context_name += " and initial mode of '#{testcase[:create_mode]}'" unless testcase[:create_mode].nil?
|
71
|
+
context_name += " and a mode of '#{testcase[:manifest_mode]}' in the manifest"
|
72
|
+
context_name += " and an owner of '#{testcase[:manifest_owner]}' in the manifest" unless testcase[:manifest_owner].nil?
|
73
|
+
context_name += " and a group of '#{testcase[:manifest_group]}' in the manifest" unless testcase[:manifest_group].nil?
|
74
|
+
|
75
|
+
context context_name do
|
76
|
+
is_idempotent = testcase[:is_idempotent].nil? || testcase[:is_idempotent]
|
77
|
+
|
78
|
+
let(:manifest) do
|
79
|
+
value = <<-MANIFEST
|
80
|
+
file { 'rspec_example':
|
81
|
+
ensure => present,
|
82
|
+
path => '#{@tmp_file}',
|
83
|
+
mode => '#{testcase[:manifest_mode]}',
|
84
|
+
MANIFEST
|
85
|
+
value += " owner => '#{testcase[:manifest_owner]}',\n" unless testcase[:manifest_owner].nil?
|
86
|
+
value += " group => '#{testcase[:manifest_group]}',\n" unless testcase[:manifest_group].nil?
|
87
|
+
value + "}"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should apply with no errors and have expected ACL" do
|
91
|
+
apply_with_error_check(manifest)
|
92
|
+
new_mode = strip_sticky(Puppet::Util::Windows::Security.get_mode(@tmp_file))
|
93
|
+
expect(new_mode.to_s(8)).to eq (testcase[:actual_mode].nil? ? testcase[:manifest_mode] : testcase[:actual_mode])
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be idempotent", :if => is_idempotent do
|
97
|
+
result = apply_with_error_check(manifest)
|
98
|
+
result = apply_with_error_check(manifest)
|
99
|
+
# Idempotent. Should be no changed resources
|
100
|
+
expect(result.changed?.count).to eq 0
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should NOT be idempotent", :unless => is_idempotent do
|
104
|
+
result = apply_with_error_check(manifest)
|
105
|
+
result = apply_with_error_check(manifest)
|
106
|
+
result = apply_with_error_check(manifest)
|
107
|
+
result = apply_with_error_check(manifest)
|
108
|
+
# Not idempotent. Expect changed resources
|
109
|
+
expect(result.changed?.count).to be > 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# These scenarios round-trip permissions and are idempotent
|
115
|
+
[
|
116
|
+
{ :create_owner => :system, :create_group => :administrators, :manifest_mode => '760' },
|
117
|
+
{ :create_owner => :administrators, :create_group => :administrators, :manifest_mode => '660' },
|
118
|
+
{ :create_owner => :system, :create_group => :system, :manifest_mode => '770' },
|
119
|
+
].each do |testcase|
|
120
|
+
# What happens if the owner and group are not managed
|
121
|
+
it_behaves_like "a mungable file resource", testcase
|
122
|
+
# What happens if the owner is managed
|
123
|
+
it_behaves_like "a mungable file resource", testcase.merge({ :manifest_owner => testcase[:create_owner]})
|
124
|
+
# What happens if the group is managed
|
125
|
+
it_behaves_like "a mungable file resource", testcase.merge({ :manifest_group => testcase[:create_group]})
|
126
|
+
# What happens if both the owner and group are managed
|
127
|
+
it_behaves_like "a mungable file resource", testcase.merge({
|
128
|
+
:manifest_owner => testcase[:create_owner],
|
129
|
+
:manifest_group => testcase[:create_group]
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
# SYSTEM is special in that when specifying less than mode 7, the owner and/or group MUST be managed
|
134
|
+
# otherwise it's munged to 7 behind the scenes and is not idempotent
|
135
|
+
both_system_testcase = { :create_owner => :system, :create_group => :system, :manifest_mode => '660', :actual_mode => '770', :is_idempotent => false }
|
136
|
+
# What happens if the owner and group are not managed
|
137
|
+
it_behaves_like "a mungable file resource", both_system_testcase.merge({ :is_idempotent => true })
|
138
|
+
# What happens if the owner is managed
|
139
|
+
it_behaves_like "a mungable file resource", both_system_testcase.merge({ :manifest_owner => both_system_testcase[:create_owner]})
|
140
|
+
# What happens if the group is managed
|
141
|
+
it_behaves_like "a mungable file resource", both_system_testcase.merge({ :manifest_group => both_system_testcase[:create_group]})
|
142
|
+
|
143
|
+
# However when we manage SYSTEM explicitly, then the modes lower than 7 stick and the file provider
|
144
|
+
# assumes it's insync (i.e. idempotent)
|
145
|
+
it_behaves_like "a mungable file resource", both_system_testcase.merge({
|
146
|
+
:manifest_owner => both_system_testcase[:create_owner],
|
147
|
+
:manifest_group => both_system_testcase[:create_group],
|
148
|
+
:actual_mode => both_system_testcase[:manifest_mode],
|
149
|
+
:is_idempotent => true
|
150
|
+
})
|
151
|
+
|
152
|
+
# What happens if we _create_ a file that SYSTEM is a part of, and is Full Control, but the manifest says it should not be Full Control
|
153
|
+
# Behind the scenes the mode should be changed to 7 and be idempotent
|
154
|
+
[
|
155
|
+
{ :create_owner => :system, :create_group => :system, :manifest_mode => '660' },
|
156
|
+
{ :create_owner => :administrators, :create_group => :system, :manifest_mode => '760' },
|
157
|
+
{ :create_owner => :system, :create_group => :administrators, :manifest_mode => '670' },
|
158
|
+
].each do |testcase|
|
159
|
+
it_behaves_like "a mungable file resource", testcase.merge({ :create_mode => '770', :actual_mode => '770'})
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'puppet_spec/https'
|
3
|
+
require 'puppet_spec/files'
|
4
|
+
require 'puppet/rest/client'
|
5
|
+
|
6
|
+
describe Puppet::Rest::Client, unless: Puppet::Util::Platform.jruby? do
|
7
|
+
include PuppetSpec::Files
|
8
|
+
|
9
|
+
before :all do
|
10
|
+
WebMock.disable!
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
WebMock.enable!
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:hostname) { '127.0.0.1' }
|
18
|
+
let(:wrong_hostname) { 'localhost' }
|
19
|
+
let(:server) { PuppetSpec::HTTPSServer.new }
|
20
|
+
let(:ssl) { Puppet::SSL::SSLProvider.new }
|
21
|
+
let(:ssl_context) { ssl.create_root_context(cacerts: [server.ca_cert], crls: [server.ca_crl]) }
|
22
|
+
let(:client) { Puppet::Rest::Client.new(ssl_context: ssl_context) }
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
localcacert = tmpfile('rest_client')
|
26
|
+
File.write(localcacert, server.ca_cert.to_pem)
|
27
|
+
Puppet[:ssl_client_ca_auth] = localcacert
|
28
|
+
|
29
|
+
# make sure we don't take too long
|
30
|
+
Puppet[:http_connect_timeout] = '5s'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "connects over SSL" do
|
34
|
+
server.start_server do |port|
|
35
|
+
uri = URI.parse("https://#{hostname}:#{port}/blah")
|
36
|
+
expect { |b|
|
37
|
+
client.get(uri, &b)
|
38
|
+
}.to yield_with_args('OK')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'provides a meaningful error message when cert validation fails' do
|
43
|
+
ssl_context = ssl.create_root_context(
|
44
|
+
cacerts: [cert_fixture('netlock-arany-utf8.pem')]
|
45
|
+
)
|
46
|
+
client = Puppet::Rest::Client.new(ssl_context: ssl_context)
|
47
|
+
|
48
|
+
server.start_server do |port|
|
49
|
+
uri = URI.parse("https://#{hostname}:#{port}/blah")
|
50
|
+
expect {
|
51
|
+
client.get(uri)
|
52
|
+
}.to raise_error(Puppet::Error,
|
53
|
+
%r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'provides valuable error message when cert names do not match' do
|
58
|
+
server.start_server do |port|
|
59
|
+
uri = URI.parse("https://#{wrong_hostname}:#{port}/blah")
|
60
|
+
expect {
|
61
|
+
client.get(uri)
|
62
|
+
}.to raise_error do |error|
|
63
|
+
pending("PUP-8213") if RUBY_VERSION.to_f >= 2.4 && !Puppet::Util::Platform.jruby?
|
64
|
+
|
65
|
+
expect(error).to be_instance_of(Puppet::SSL::CertMismatchError)
|
66
|
+
expect(error.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
|
67
|
+
|
68
|
+
md = error.message.match(/expected one of (.+)/)
|
69
|
+
expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|