diff --git a/metadata.json b/metadata.json index 3407180..7b5b1d2 100644 --- a/metadata.json +++ b/metadata.json @@ -1,105 +1,106 @@ { "name": "puppetlabs-java_ks", "version": "4.1.0", "author": "puppetlabs", "summary": "Manage arbitrary Java keystore files", "license": "Apache-2.0", "source": "https://github.com/puppetlabs/puppetlabs-java_ks", "project_page": "https://github.com/puppetlabs/puppetlabs-java_ks", "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", "dependencies": [ ], "operatingsystem_support": [ { "operatingsystem": "RedHat", "operatingsystemrelease": [ "6", "7", "8" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ "6", "7", "8" ] }, { "operatingsystem": "OracleLinux", "operatingsystemrelease": [ "6", "7" ] }, { "operatingsystem": "Scientific", "operatingsystemrelease": [ "6", "7" ] }, { "operatingsystem": "SLES", "operatingsystemrelease": [ "12", "15" ] }, { "operatingsystem": "Debian", "operatingsystemrelease": [ "8", "9", - "10" + "10", + "11" ] }, { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "14.04", "16.04", "18.04", "20.04" ] }, { "operatingsystem": "Solaris", "operatingsystemrelease": [ "10", "11" ] }, { "operatingsystem": "AIX", "operatingsystemrelease": [ "7.1" ] }, { "operatingsystem": "Windows", "operatingsystemrelease": [ "2008 R2", "2012", "2012 R2", "2016", "2019", "7", "8.1", "10" ] } ], "requirements": [ { "name": "puppet", "version_requirement": ">= 6.0.0 < 8.0.0" } ], "description": "Uses a combination of keytool and Ruby openssl library to manage entries in a Java keystore.", "template-url": "https://github.com/puppetlabs/pdk-templates.git#main", "template-ref": "heads/main-0-g03daa92", "pdk-version": "2.1.0" } diff --git a/spec/acceptance/pkcs12_spec.rb b/spec/acceptance/pkcs12_spec.rb index d4c8bbd..b933a4e 100644 --- a/spec/acceptance/pkcs12_spec.rb +++ b/spec/acceptance/pkcs12_spec.rb @@ -1,149 +1,150 @@ # frozen_string_literal: true require 'spec_helper_acceptance' # SLES by default does not support this form of encyrption. describe 'managing java pkcs12', unless: (os[:family] == 'sles' || (os[:family] == 'debian' && os[:release].start_with?('10')) || (os[:family] == 'ubuntu' && os[:release].start_with?('18'))) do # rubocop:disable RSpec/InstanceVariable : Instance variables are inherited and thus cannot be contained within lets include_context 'common variables' context 'with defaults' do it 'creates a private key with chain' do pp = <<-MANIFEST java_ks { 'Leaf Cert:#{@temp_dir}pkcs12.ks': ensure => #{@ensure_ks}, certificate => "#{@temp_dir}leaf.p12", storetype => 'pkcs12', password => 'puppet', path => #{@resource_path}, source_password => 'pkcs12pass' } MANIFEST idempotent_apply(pp) end expectations = [ %r{Alias name: leaf cert}, %r{Entry type: (keyEntry|PrivateKeyEntry)}, %r{Certificate chain length: 3}, %r{^Serial number: 5.*^Serial number: 4.*^Serial number: 3}m, ] it 'verifies the private key and chain' do run_shell(keytool_command("-list -v -keystore #{@temp_dir}pkcs12.ks -storepass puppet"), expect_failures: true) do |r| expectations.each do |expect| expect(r.stdout).to match(expect) end end end it 'updates the chain' do pp = <<-MANIFEST java_ks { 'Leaf Cert:#{@temp_dir}pkcs12.ks': ensure => #{@ensure_ks}, certificate => "#{@temp_dir}leaf2.p12", storetype => 'pkcs12', password => 'puppet', path => #{@resource_path}, source_password => 'pkcs12pass' } MANIFEST idempotent_apply(pp) - expectations = if os[:family] == 'windows' || (os[:family] == 'ubuntu' && os[:release] == '20.04') + expectations = if os[:family] == 'windows' || (os[:family] == 'ubuntu' && os[:release] == '20.04') || + (os[:family] == 'debian' && os[:release] =~ %r{^11}) [ %r{Alias name: leaf cert}, %r{Entry type: (keyEntry|PrivateKeyEntry)}, %r{Certificate chain length: 3}, %r{^Serial number: 3}m, %r{^Serial number: 4}m, %r{^Serial number: 5}m, ] else [ %r{Alias name: leaf cert}, %r{Entry type: (keyEntry|PrivateKeyEntry)}, %r{Certificate chain length: 2}, %r{^Serial number: 5$.*^Serial number: 6$}m, ] end run_shell(keytool_command("-list -v -keystore #{@temp_dir}pkcs12.ks -storepass puppet"), expect_failures: true) do |r| expectations.each do |expect| expect(r.stdout).to match(expect) end end end end # context 'with defaults' context 'with a different alias' do it 'creates a private key with chain' do pp = <<-MANIFEST java_ks { 'Leaf_Cert:#{@temp_dir}pkcs12.ks': ensure => #{@ensure_ks}, certificate => "#{@temp_dir}leaf.p12", storetype => 'pkcs12', password => 'puppet', path => #{@resource_path}, source_password => 'pkcs12pass', source_alias => 'Leaf Cert' } MANIFEST idempotent_apply(pp) end expectations = [ %r{Alias name: leaf_cert}, %r{Entry type: (keyEntry|PrivateKeyEntry)}, %r{Certificate chain length: 3}, %r{^Serial number: 5.*^Serial number: 4.*^Serial number: 3}m, ] it 'verifies the private key and chain' do run_shell(keytool_command("-list -v -keystore #{@temp_dir}pkcs12.ks -storepass puppet"), expect_failures: true) do |r| expectations.each do |expect| expect(r.stdout).to match(expect) end end end end # context 'with a different alias' context 'with a destkeypass' do command = if os[:family] == 'windows' interpolate_powershell("rm -force #{@temp_dir}pkcs12.ks") else "rm -f #{@temp_dir}pkcs12.ks" end before(:all) { run_shell(command, expect_failures: true) } it 'creates a private key with chain' do pp = <<-MANIFEST java_ks { 'Leaf_Cert:#{@temp_dir}/pkcs12.ks': ensure => #{@ensure_ks}, certificate => "#{@temp_dir}leaf.p12", destkeypass => "abcdef123456", storetype => 'pkcs12', password => 'puppet', path => #{@resource_path}, source_password => 'pkcs12pass', source_alias => 'Leaf Cert' } MANIFEST idempotent_apply(pp) end expectations = [ %r{Alias name: leaf_cert}, %r{Entry type: (keyEntry|PrivateKeyEntry)}, %r{Certificate chain length: 3}, %r{^Serial number: 5.*^Serial number: 4.*^Serial number: 3}m, ] it 'verifies the private key and chain' do run_shell(keytool_command("-list -v -keystore #{@temp_dir}pkcs12.ks -storepass puppet"), expect_failures: true) do |r| expectations.each do |expect| expect(r.stdout).to match(expect) end end end end # context 'with a destkeypass' end diff --git a/spec/spec_helper_acceptance_local.rb b/spec/spec_helper_acceptance_local.rb index cdddf5e..28b74b1 100644 --- a/spec/spec_helper_acceptance_local.rb +++ b/spec/spec_helper_acceptance_local.rb @@ -1,200 +1,202 @@ # frozen_string_literal: true UNSUPPORTED_PLATFORMS = [].freeze require 'singleton' class LitmusHelper include Singleton include PuppetLitmus end def keytool_command(arguments) # The @keytool global does not exist right now as the function is defined. # When the tests call the function, RSpec.shared_context below will have run # by then and the variable will exist. # os[:family] == 'windows' ? interpolate_powershell("& '#{@keytool_path}keytool'") : "'#{@keytool_path}keytool'" if os[:family] == 'windows' interpolate_powershell("& '#{@keytool_path}keytool' #{arguments}") else "'#{@keytool_path}keytool' #{arguments}" end end def interpolate_powershell(command) "powershell.exe -NoProfile -Nologo -Command \"#{command}\"" end def remote_windows_temp_dir @remote_windows_temp_dir ||= LitmusHelper.instance.run_shell(interpolate_powershell('echo "$ENV:TEMP"')).stdout.strip.tr('\\', '/') + '/' @remote_windows_temp_dir end def remote_file_exists?(filename) if os[:family] == 'windows' LitmusHelper.instance.run_shell(interpolate_powershell("Get-Item -Path '#{filename}' -ErrorAction SilentlyContinue"), expect_failures: true) else LitmusHelper.instance.run_shell("test -f '#{filename}'", expect_failures: true) end end def temp_dir @temp_dir ||= (os[:family] == 'windows') ? remote_windows_temp_dir : '/tmp/' @temp_dir end def create_and_upload_certs cert_files = ['privkey.pem', 'ca.pem', 'ca.der', 'ca2.pem', 'chain.pem', 'chain2.pem', 'leafkey.pem', 'leaf.pem', 'leafchain.pem', 'leafchain2.pem', 'leaf.p12', 'leaf2.p12'] recreate_certs = false cert_files.each do |cert_file| recreate_certs = true unless File.file?("spec/acceptance/certs/#{cert_file}") end create_certs if recreate_certs cert_files.each do |cert_file| if ENV['TARGET_HOST'].nil? || ENV['TARGET_HOST'] == 'localhost' command = "cp spec\\acceptance\\certs\\#{cert_file} #{ENV['TEMP']}\\#{cert_file}" command = interpolate_powershell(command) if os[:family] == 'windows' Open3.capture3(command) else LitmusHelper.instance.bolt_upload_file("spec/acceptance/certs/#{cert_file}", "#{temp_dir}#{cert_file}") end end end def create_certs require 'openssl' key = OpenSSL::PKey::RSA.new 1024 ca = OpenSSL::X509::Certificate.new ca.serial = 1 ca.public_key = key.public_key subj = '/CN=Test CA/ST=Denial/L=Springfield/O=Dis/CN=www.example.com' ca.subject = OpenSSL::X509::Name.parse subj ca.issuer = ca.subject ca.not_before = Time.now ca.not_after = ca.not_before + 360 ca.sign(key, OpenSSL::Digest::SHA256.new) key2 = OpenSSL::PKey::RSA.new 1024 ca2 = OpenSSL::X509::Certificate.new ca2.serial = 2 ca2.public_key = key2.public_key subj2 = '/CN=Test CA/ST=Denial/L=Springfield/O=Dis/CN=www.example.com' ca2.subject = OpenSSL::X509::Name.parse subj2 ca2.issuer = ca2.subject ca2.not_before = Time.now ca2.not_after = ca2.not_before + 360 ca2.sign(key2, OpenSSL::Digest::SHA256.new) key_chain = OpenSSL::PKey::RSA.new 1024 chain = OpenSSL::X509::Certificate.new chain.serial = 3 chain.public_key = key_chain.public_key chain_subj = '/CN=Chain CA/ST=Denial/L=Springfield/O=Dis/CN=www.example.net' chain.subject = OpenSSL::X509::Name.parse chain_subj chain.issuer = ca.subject chain.not_before = Time.now chain.not_after = chain.not_before + 360 chain.sign(key, OpenSSL::Digest::SHA256.new) key_chain2 = OpenSSL::PKey::RSA.new 1024 chain2 = OpenSSL::X509::Certificate.new chain2.serial = 4 chain2.public_key = key_chain2.public_key chain2_subj = '/CN=Chain CA 2/ST=Denial/L=Springfield/O=Dis/CN=www.example.net' chain2.subject = OpenSSL::X509::Name.parse chain2_subj chain2.issuer = chain.subject chain2.not_before = Time.now chain2.not_after = chain2.not_before + 360 chain2.sign(key_chain, OpenSSL::Digest::SHA256.new) key_leaf = OpenSSL::PKey::RSA.new 1024 leaf = OpenSSL::X509::Certificate.new leaf.serial = 5 leaf.public_key = key_leaf.public_key leaf_subj = '/CN=Leaf Cert/ST=Denial/L=Springfield/O=Dis/CN=www.example.net' leaf.subject = OpenSSL::X509::Name.parse leaf_subj leaf.issuer = chain2.subject leaf.not_before = Time.now leaf.not_after = leaf.not_before + 360 leaf.sign(key_chain2, OpenSSL::Digest::SHA256.new) chain3 = OpenSSL::X509::Certificate.new chain3.serial = 6 chain3.public_key = key_chain2.public_key chain3.subject = OpenSSL::X509::Name.parse chain2_subj chain3.issuer = ca.subject chain3.not_before = Time.now chain3.not_after = chain3.not_before + 360 chain3.sign(key, OpenSSL::Digest::SHA256.new) pkcs12 = OpenSSL::PKCS12.create('pkcs12pass', 'Leaf Cert', key_leaf, leaf, [chain2, chain]) pkcs12_chain3 = OpenSSL::PKCS12.create('pkcs12pass', 'Leaf Cert', key_leaf, leaf, [chain3]) create_cert_file('privkey.pem', key.to_pem) create_cert_file('ca.pem', ca.to_pem) create_cert_file('ca.der', ca.to_der) create_cert_file('ca2.pem', ca2.to_pem) create_cert_file('chain.pem', chain2.to_pem + chain.to_pem) create_cert_file('chain2.pem', chain3.to_pem) create_cert_file('leafkey.pem', key_leaf.to_pem) create_cert_file('leaf.pem', leaf.to_pem) create_cert_file('leafchain.pem', leaf.to_pem + chain2.to_pem + chain.to_pem) create_cert_file('leafchain2.pem', leaf.to_pem + chain3.to_pem) create_cert_file('leaf.p12', pkcs12.to_der) create_cert_file('leaf2.p12', pkcs12_chain3.to_der) end def create_cert_file(cert_name, contents) return if File.file?("spec/acceptance/certs/#{cert_name}") out_file = File.new("spec/acceptance/certs/#{cert_name}", 'w+') out_file.puts(contents) out_file.close end RSpec.configure do |c| c.before :suite do create_and_upload_certs # install java if windows if os[:family] == 'windows' LitmusHelper.instance.run_shell('puppet module install puppetlabs-chocolatey') pp_one = <<-MANIFEST include chocolatey package { 'jdk8': ensure => '8.0.211', provider => 'chocolatey', install_options => ['-y'] } MANIFEST LitmusHelper.instance.apply_manifest(pp_one, catch_failures: true) else LitmusHelper.instance.run_shell('puppet module install puppetlabs-java') pp_two = <<-MANIFEST class { 'java': } MANIFEST LitmusHelper.instance.apply_manifest(pp_two) end end end RSpec.shared_context 'common variables' do before(:each) do java_major, java_minor = (ENV['JAVA_VERSION'] || '8u211').split('u') @ensure_ks = 'latest' @resource_path = 'undef' @target_dir = '/etc/' @temp_dir = temp_dir case os[:family] when 'solaris' @keytool_path = '/usr/java/bin/' @resource_path = "['/usr/java/bin/','/opt/puppet/bin/']" when 'aix' @keytool_path = '/usr/java6/bin/' @resource_path = "['/usr/java6/bin/','/usr/bin/']" when 'windows' @ensure_ks = 'present' @keytool_path = "C:/Program Files/Java/jdk1.#{java_major}.0_#{java_minor}/bin/" @resource_path = "['C:/Program Files/Java/jdk1.#{java_major}.0_#{java_minor}/bin/']" when 'ubuntu' @ensure_ks = 'present' if os[:release] == '20.04' + when 'debian' + @ensure_ks = 'present' if os[:release].match?(%r{^11}) end end end