beaker-pe 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4669811fd96282e6cbb309d4d65eb083e25e5222
4
- data.tar.gz: f28009cebec926e2ade3db68ce801ab958208718
3
+ metadata.gz: 472cb8527da6bbb6256c8342fff325b91dfe0d62
4
+ data.tar.gz: 75b005236142feec1df7954cac688d4d4f9f613c
5
5
  SHA512:
6
- metadata.gz: d96ae7fc50c9a581781519459735ed8923a843b110f1ad0d9827c8e46b617c333287ad7353dbb915ebf7c12bb4f5d351e73cd8ed21d583b6097db1b8e6e47e88
7
- data.tar.gz: 25a4f33451f1faa88aa410c209980ffa607c77f5fba71df18b504b0acc8b3b42be8f83221fae6f7eca17b44f95395fb965bcc2a79c9c5b01569b68d152e73be6
6
+ metadata.gz: 24d74792e19f75a3d33f30de4644878dcc742a00b872775c8e30965a891e86641a89fff1a106cdf0a6b8fd9260dfb886890c013ac908f55e609d709419d5e554
7
+ data.tar.gz: 05daf324e0665af413f85c35dbefc81e2cc1e310bfb734328f93adb02ba4298993f4c41843a299e41200f36040efa8b9dd6e6c1d82319c99110337b79ca73a8b
data/Gemfile CHANGED
@@ -17,9 +17,7 @@ group :acceptance_testing do
17
17
  gem "beaker-vmpooler", *location_for(ENV['BEAKER_VMPOOLER_VERSION'] || '~> 1.3')
18
18
  end
19
19
 
20
- if ENV['GEM_SOURCE'] =~ /artifactory\.delivery\.puppetlabs\.net/
21
- gem "scooter", *location_for(ENV['SCOOTER_VERSION'] || '~> 3.0')
22
- end
20
+ gem "scooter", *location_for(ENV['SCOOTER_VERSION'] || '~> 4.3')
23
21
 
24
22
  gem 'deep_merge'
25
23
 
data/lib/beaker-pe.rb CHANGED
@@ -5,6 +5,7 @@ require 'stringify-hash'
5
5
  require 'beaker-pe/version'
6
6
  require 'beaker-pe/install/pe_defaults'
7
7
  require 'beaker-pe/install/pe_utils'
8
+ require 'beaker-pe/install/ca_utils'
8
9
  require 'beaker-pe/options/pe_version_scraper'
9
10
  require 'beaker-pe/pe-client-tools/config_file_helper'
10
11
  require 'beaker-pe/pe-client-tools/install_helper'
@@ -16,6 +17,7 @@ module Beaker
16
17
  include Beaker::DSL::InstallUtils::PEDefaults
17
18
  include Beaker::DSL::InstallUtils::PEUtils
18
19
  include Beaker::DSL::InstallUtils::PEClientTools
20
+ include Beaker::DSL::InstallUtils::CAUtils
19
21
  include Beaker::Options::PEVersionScraper
20
22
  include Beaker::DSL::PEClientTools::ConfigFileHelper
21
23
  include Beaker::DSL::PEClientTools::ExecutableHelper
@@ -0,0 +1,192 @@
1
+ #Much of this is taken from PuppetSpec:SSL
2
+ require "openssl"
3
+
4
+ module Beaker
5
+ module DSL
6
+ module InstallUtils
7
+ module CAUtils
8
+ PRIVATE_KEY_LENGTH = 2048
9
+ FIVE_YEARS = 5 * 365 * 24 * 60 * 60
10
+ CA_EXTENSIONS = [
11
+ ["basicConstraints", "CA:TRUE", true],
12
+ ["keyUsage", "keyCertSign, cRLSign", true],
13
+ ["subjectKeyIdentifier", "hash", false],
14
+ ["authorityKeyIdentifier", "keyid:always", false]
15
+ ]
16
+ NODE_EXTENSIONS = [
17
+ ["keyUsage", "digitalSignature", true],
18
+ ["subjectKeyIdentifier", "hash", false]
19
+ ]
20
+ DEFAULT_SIGNING_DIGEST = OpenSSL::Digest::SHA256.new
21
+ DEFAULT_REVOCATION_REASON = OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
22
+ ROOT_CA_NAME = "/CN=root-ca"
23
+ INT_CA_NAME = "/CN=intermediate-ca"
24
+ EXPLANATORY_TEXT = <<-EOT
25
+ # Root Issuer: #{ROOT_CA_NAME}
26
+ # Intermediate Issuer: #{INT_CA_NAME}
27
+ EOT
28
+
29
+ # Generate CA bundle with root and intermediate certs, as well as CRL chain and private key for
30
+ # the intermediate CA, pushed to the host for use during PE install with pe_install::signing_ca
31
+ #
32
+ # @param [Host] host The host to create CA bundle files on. Defaults to global 'master' object.
33
+ # @param [String] targetdir Location to save files on host, to be referenced in pe.conf for install.
34
+ # @return [Hash] File names => where they were put on the host
35
+ def generate_ca_bundle_on(host = master, targetdir = '/tmp/ca_bundle')
36
+ files = {}
37
+ pki = create_chained_pki
38
+ on(host, "mkdir -p #{targetdir}", :acceptable_exit_codes => [0])
39
+ pki.each do |name,cert|
40
+ create_remote_file(host, "#{targetdir}/#{name}", cert.to_s, :acceptable_exit_codes => [0])
41
+ files["#{name}".to_sym] = "#{targetdir}/#{name}"
42
+ end
43
+ files
44
+ end
45
+
46
+ def create_private_key(length = PRIVATE_KEY_LENGTH)
47
+ OpenSSL::PKey::RSA.new(length)
48
+ end
49
+
50
+ def self_signed_ca(key, name)
51
+ cert = OpenSSL::X509::Certificate.new
52
+
53
+ cert.public_key = key.public_key
54
+ cert.subject = OpenSSL::X509::Name.parse(name)
55
+ cert.issuer = cert.subject
56
+ cert.version = 2
57
+ cert.serial = rand(2**128)
58
+
59
+ not_before = just_now
60
+ cert.not_before = not_before
61
+ cert.not_after = not_before + FIVE_YEARS
62
+
63
+ ext_factory = extension_factory_for(cert, cert)
64
+ CA_EXTENSIONS.each do |ext|
65
+ extension = ext_factory.create_extension(*ext)
66
+ cert.add_extension(extension)
67
+ end
68
+
69
+ cert.sign(key, DEFAULT_SIGNING_DIGEST)
70
+
71
+ cert
72
+ end
73
+
74
+ def create_csr(key, name)
75
+ csr = OpenSSL::X509::Request.new
76
+
77
+ csr.public_key = key.public_key
78
+ csr.subject = OpenSSL::X509::Name.parse(name)
79
+ csr.version = 2
80
+ csr.sign(key, DEFAULT_SIGNING_DIGEST)
81
+
82
+ csr
83
+ end
84
+
85
+ def sign(ca_key, ca_cert, csr, extensions = NODE_EXTENSIONS)
86
+ cert = OpenSSL::X509::Certificate.new
87
+
88
+ cert.public_key = csr.public_key
89
+ cert.subject = csr.subject
90
+ cert.issuer = ca_cert.subject
91
+ cert.version = 2
92
+ cert.serial = rand(2**128)
93
+
94
+ not_before = just_now
95
+ cert.not_before = not_before
96
+ cert.not_after = not_before + FIVE_YEARS
97
+
98
+ ext_factory = extension_factory_for(ca_cert, cert)
99
+ extensions.each do |ext|
100
+ extension = ext_factory.create_extension(*ext)
101
+ cert.add_extension(extension)
102
+ end
103
+
104
+ cert.sign(ca_key, DEFAULT_SIGNING_DIGEST)
105
+
106
+ cert
107
+ end
108
+
109
+ def create_crl_for(ca_cert, ca_key)
110
+ crl = OpenSSL::X509::CRL.new
111
+ crl.version = 1
112
+ crl.issuer = ca_cert.subject
113
+
114
+ ef = extension_factory_for(ca_cert)
115
+ crl.add_extension(
116
+ ef.create_extension(["authorityKeyIdentifier", "keyid:always", false]))
117
+ crl.add_extension(
118
+ OpenSSL::X509::Extension.new("crlNumber", OpenSSL::ASN1::Integer(0)))
119
+
120
+ not_before = just_now
121
+ crl.last_update = not_before
122
+ crl.next_update = not_before + FIVE_YEARS
123
+ crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)
124
+
125
+ crl
126
+ end
127
+
128
+ def revoke(serial, crl, ca_key)
129
+ revoked = OpenSSL::X509::Revoked.new
130
+ revoked.serial = serial
131
+ revoked.time = Time.now
132
+ revoked.add_extension(
133
+ OpenSSL::X509::Extension.new("CRLReason",
134
+ OpenSSL::ASN1::Enumerated(DEFAULT_REVOCATION_REASON)))
135
+
136
+ crl.add_revoked(revoked)
137
+ extensions = crl.extensions.group_by{|e| e.oid == 'crlNumber' }
138
+ crl_number = extensions[true].first
139
+ unchanged_exts = extensions[false]
140
+
141
+ next_crl_number = crl_number.value.to_i + 1
142
+ new_crl_number_ext = OpenSSL::X509::Extension.new("crlNumber",
143
+ OpenSSL::ASN1::Integer(next_crl_number))
144
+
145
+ crl.extensions = unchanged_exts + [new_crl_number_ext]
146
+ crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)
147
+
148
+ crl
149
+ end
150
+
151
+ def create_chained_pki
152
+ root_key = create_private_key
153
+ root_cert = self_signed_ca(root_key, ROOT_CA_NAME)
154
+ root_crl = create_crl_for(root_cert, root_key)
155
+
156
+ int_key = create_private_key
157
+ int_csr = create_csr(int_key, INT_CA_NAME)
158
+ int_cert = sign(root_key, root_cert, int_csr, CA_EXTENSIONS)
159
+ int_crl = create_crl_for(int_cert, int_key)
160
+
161
+ int_ca_bundle = bundle(int_cert, root_cert)
162
+ int_crl_chain = bundle(int_crl, root_crl)
163
+
164
+ {
165
+ :root_cert => root_cert,
166
+ :int_cert => int_cert,
167
+ :int_ca_bundle => int_ca_bundle,
168
+ :int_key => int_key,
169
+ :int_crl_chain => int_crl_chain,
170
+ }
171
+ end
172
+
173
+ def just_now
174
+ Time.now - 1
175
+ end
176
+
177
+ def extension_factory_for(ca, cert = nil)
178
+ ef = OpenSSL::X509::ExtensionFactory.new
179
+ ef.issuer_certificate = ca
180
+ ef.subject_certificate = cert if cert
181
+
182
+ ef
183
+ end
184
+
185
+ def bundle(*items)
186
+ items.map {|i| EXPLANATORY_TEXT + i.to_pem }.join("\n")
187
+ end
188
+
189
+ end
190
+ end
191
+ end
192
+ end
@@ -3,7 +3,7 @@ module Beaker
3
3
  module PE
4
4
 
5
5
  module Version
6
- STRING = '2.0.2'
6
+ STRING = '2.0.3'
7
7
  end
8
8
 
9
9
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ include Beaker::DSL::InstallUtils::CAUtils
3
+
4
+ describe Beaker::DSL::InstallUtils::CAUtils do
5
+ let(:dummy_pki) {
6
+ {
7
+ :root_cert => 'dummy_root_cert',
8
+ :int_cert => 'dummy_int_cert',
9
+ :int_ca_bundle => 'dummy_int_bundle',
10
+ :int_key => 'dummy_int_key',
11
+ :int_crl_chain => 'dummy_int_crl_chain',
12
+ }
13
+ }
14
+
15
+ before(:each) do
16
+ allow(subject).to receive(:create_chained_pki).and_return(dummy_pki)
17
+ end
18
+
19
+ describe 'generate_ca_bundle_on' do
20
+ let(:host) { make_host( 'unixhost', { :platform => 'linux'})}
21
+ let(:bundledir) { '/tmp/ca_bundle' }
22
+ let(:expected) {
23
+ {
24
+ :root_cert => "#{bundledir}/root_cert",
25
+ :int_cert => "#{bundledir}/int_cert",
26
+ :int_ca_bundle => "#{bundledir}/int_ca_bundle",
27
+ :int_key => "#{bundledir}/int_key",
28
+ :int_crl_chain => "#{bundledir}/int_crl_chain",
29
+ }
30
+ }
31
+
32
+ it "generates certs on host" do
33
+ expect(subject).to receive(:on).with(host, "mkdir -p #{bundledir}", :acceptable_exit_codes => [0])
34
+ expect(subject).to receive(:create_remote_file).with(host,"#{bundledir}/root_cert", "dummy_root_cert", :acceptable_exit_codes => [0])
35
+ expect(subject).to receive(:create_remote_file).with(host,"#{bundledir}/int_cert", "dummy_int_cert", :acceptable_exit_codes => [0])
36
+ expect(subject).to receive(:create_remote_file).with(host,"#{bundledir}/int_ca_bundle", "dummy_int_bundle", :acceptable_exit_codes => [0])
37
+ expect(subject).to receive(:create_remote_file).with(host,"#{bundledir}/int_key", "dummy_int_key", :acceptable_exit_codes => [0])
38
+ expect(subject).to receive(:create_remote_file).with(host,"#{bundledir}/int_crl_chain", "dummy_int_crl_chain", :acceptable_exit_codes => [0])
39
+ expect( subject.generate_ca_bundle_on(host,"#{bundledir}") ).to eq(expected)
40
+ end
41
+ end
42
+ end
@@ -64,25 +64,66 @@ describe MixedWithExecutableHelper do
64
64
 
65
65
  context 'puppet access login with lifetime parameter' do
66
66
  let(:logger) {Beaker::Logger.new}
67
- let(:test_host) {Beaker::Host.create('my_super_host',
68
- {:roles => ['master', 'agent'],
69
- :platform => 'linux',
70
- :type => 'pe'},
71
- make_opts)}
72
- let(:username) {'T'}
73
- let(:password) {'Swift'}
74
- let(:credentials) {{:login => username, :password => password}}
75
- let(:test_dispatcher) {Scooter::HttpDispatchers::ConsoleDispatcher.new('my_super_host', credentials)}
67
+ let(:test_host) {
68
+ make_host('my_super_host', {
69
+ :roles => ['master', 'agent'],
70
+ :platform => 'linux',
71
+ :type => 'pe'
72
+ }
73
+ )
74
+ }
75
+ let(:credentials) {
76
+ mock = Object.new
77
+ allow(mock).to receive(:login).and_return('T')
78
+ allow(mock).to receive(:password).and_return('Swift')
79
+ mock
80
+ }
81
+ let(:test_dispatcher) {
82
+ mock = Object.new
83
+ allow(mock).to receive(:credentials).and_return(credentials)
84
+ mock
85
+ }
76
86
 
77
87
  before do
78
88
  allow(logger).to receive(:debug) { true }
79
- expect(test_dispatcher).to be_kind_of(Scooter::HttpDispatchers::ConsoleDispatcher)
80
89
  expect(test_host).to be_kind_of(Beaker::Host)
81
- expect(test_host).to receive(:exec)
82
90
  end
83
91
 
84
- it 'accepts correct value' do
85
- expect{subject.login_with_puppet_access_on(test_host, test_dispatcher, {:lifetime => '5d'})}.not_to raise_error
92
+ it 'passes the lifetime value to :puppet_access_on on linux' do
93
+ lifetime_value = '5d'
94
+
95
+ expect(subject).to receive(:puppet_access_on).with(
96
+ test_host,
97
+ "login",
98
+ "--lifetime #{lifetime_value}",
99
+ anything
100
+ )
101
+
102
+ expect{
103
+ subject.login_with_puppet_access_on(
104
+ test_host,
105
+ test_dispatcher,
106
+ {:lifetime => lifetime_value}
107
+ )
108
+ }.not_to raise_error
109
+ end
110
+
111
+ it 'passes the lifetime value to the passed dispatcher on windows' do
112
+ test_host[:platform] = "win-stuff"
113
+ allow(subject).to receive(:create_remote_file)
114
+ lifetime_value = '6d'
115
+
116
+ expect(test_dispatcher).to receive(
117
+ :acquire_token_with_credentials
118
+ ).with(lifetime_value)
119
+
120
+ expect{
121
+ subject.login_with_puppet_access_on(
122
+ test_host,
123
+ test_dispatcher,
124
+ {:lifetime => lifetime_value}
125
+ )
126
+ }.not_to raise_error
86
127
  end
87
128
  end
88
129
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beaker-pe
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppetlabs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-12 00:00:00.000000000 Z
11
+ date: 2018-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -253,6 +253,7 @@ files:
253
253
  - bin/beaker-template
254
254
  - docs/how_to/install_puppet_enterprise.md
255
255
  - lib/beaker-pe.rb
256
+ - lib/beaker-pe/install/ca_utils.rb
256
257
  - lib/beaker-pe/install/feature_flags.rb
257
258
  - lib/beaker-pe/install/pe_defaults.rb
258
259
  - lib/beaker-pe/install/pe_utils.rb
@@ -262,6 +263,7 @@ files:
262
263
  - lib/beaker-pe/pe-client-tools/install_helper.rb
263
264
  - lib/beaker-pe/version.rb
264
265
  - spec/beaker-pe/helpers_spec.rb
266
+ - spec/beaker-pe/install/ca_utils_spec.rb
265
267
  - spec/beaker-pe/install/feature_flags_spec.rb
266
268
  - spec/beaker-pe/install/pe_defaults_spec.rb
267
269
  - spec/beaker-pe/install/pe_utils_spec.rb