2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pkg
metadata.json
.rspec_system/
Gemfile.lock
31 changes: 31 additions & 0 deletions .nodeset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
default_set: 'centos-64-x64'
sets:
'centos-59-x64':
nodes:
"main.foo.vm":
prefab: 'centos-59-x64'
'centos-64-x64':
nodes:
"main.foo.vm":
prefab: 'centos-64-x64'
'fedora-18-x64':
nodes:
"main.foo.vm":
prefab: 'fedora-18-x64'
'debian-607-x64':
nodes:
"main.foo.vm":
prefab: 'debian-607-x64'
'debian-70rc1-x64':
nodes:
"main.foo.vm":
prefab: 'debian-70rc1-x64'
'ubuntu-server-10044-x64':
nodes:
"main.foo.vm":
prefab: 'ubuntu-server-10044-x64'
'ubuntu-server-12042-x64':
nodes:
"main.foo.vm":
prefab: 'ubuntu-server-12042-x64'
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ Release notes for the puppetlabs-java_ks module

---------------------------------------

2013-09-18 Release 1.2.0
========================

### Summary
This release adds `puppet://` URI support, a few bugfixes, and lots of tests.

### Features
- `puppet://` URI support for the `chain`, `certificate`, and `private_key` parameters

### Bugfixes
- Validate that keystore passwords are > 6 characters (would silent fail before)
- Fixed corrupted keystore PKCS12 files in some cases.
- More acceptance tests, unit tests, and rspec-puppet tests.

1.1.0
=====

Expand Down
9 changes: 6 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ source "https://rubygems.org"

group :development, :test do
gem 'rake'
gem 'rspec', "~> 2.11.0", :require => false
gem 'mocha', "~> 0.10.5", :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec', :require => false
gem 'mocha', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec-system', :require => false
gem 'rspec-system-puppet', :require => false
gem 'rspec-system-serverspec', :require => false
end

if puppetversion = ENV['PUPPET_GEM_VERSION']
Expand Down
2 changes: 1 addition & 1 deletion Modulefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name 'puppetlabs-java_ks'
version '1.1.0'
version '1.2.0'

author 'puppetlabs'
license 'ASL 2.0'
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ This resource manages the entries in a java keystore, and uses composite namevar

#### `certificate`

An already-signed certificate to place in the keystore.
An already-signed certificate to place in the keystore. Accepts local file paths or `puppet://` uri paths.

To have a java application server use a specific certificate for incoming connections, you will need to simultaneously import the private key accompanying the signed certificate you want to use. As long as you provide the path to the key and the certificate, the provider will do the conversion for you.

#### `chain`

Some java applications do not properly send intermediary certificate authorities. In these cases, you can bundle them with the server certificate using this chain parameter.
Some java applications do not properly send intermediary certificate authorities. In these cases, you can bundle them with the server certificate using this chain parameter. Accepts local file paths or `puppet://` uri paths.

java_ks { 'broker.example.com:/etc/activemq/broker.jks':
ensure => latest,
Expand Down Expand Up @@ -103,7 +103,7 @@ The search path used for command (keytool, openssl) execution. Paths can be spec

#### `private_key`

If you want an application to be a server and encrypt traffic, you will need a private key. Private key entries in a keystore must be accompanied by a signed certificate for the keytool provider.
If you want an application to be a server and encrypt traffic, you will need a private key. Private key entries in a keystore must be accompanied by a signed certificate for the keytool provider. Accepts local file paths or `puppet://` uri paths.

#### `target`

Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
require 'rubygems'
require 'puppetlabs_spec_helper/rake_tasks'
require 'rspec-system/rake_task'
48 changes: 33 additions & 15 deletions lib/puppet/provider/java_ks/keytool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ def command_keytool

# Keytool can only import a keystore if the format is pkcs12. Generating and
# importing a keystore is used to add private_key and certifcate pairs.
def to_pkcs12
output = ''
def to_pkcs12(path)
cmd = [
command_openssl,
'pkcs12', '-export', '-passout', 'stdin',
'-in', @resource[:certificate],
'-inkey', @resource[:private_key],
'-name', @resource[:name]
'-in', certificate,
'-inkey', private_key,
'-name', @resource[:name],
'-out', path
]
cmd << [ '-certfile', @resource[:chain] ] if @resource[:chain]
cmd << [ '-certfile', chain ] if chain
tmpfile = Tempfile.new("#{@resource[:name]}.")
tmpfile.write(@resource[:password])
tmpfile.flush
Expand All @@ -31,10 +31,9 @@ def to_pkcs12
# code to make sure RANDFILE is passed as an environment variable to the
# openssl command but not retained in the Puppet process environment.
randfile = Tempfile.new("#{@resource[:name]}.")
output = run_command(cmd, false, tmpfile, 'RANDFILE' => randfile.path)
run_command(cmd, false, tmpfile, 'RANDFILE' => randfile.path)
tmpfile.close!
randfile.close!
return output
end

def password_file
Expand All @@ -55,8 +54,7 @@ def password_file
# Where we actually to the import of the file created using to_pkcs12.
def import_ks
tmppk12 = Tempfile.new("#{@resource[:name]}.")
tmppk12.write(to_pkcs12)
tmppk12.flush
to_pkcs12(tmppk12.path)
cmd = [
command_keytool,
'-importkeystore', '-srcstoretype', 'PKCS12',
Expand Down Expand Up @@ -96,7 +94,7 @@ def latest
cmd = [
command_openssl,
'x509', '-fingerprint', '-md5', '-noout',
'-in', @resource[:certificate]
'-in', certificate
]
output = run_command(cmd)
latest = output.scan(/MD5 Fingerprint=(.*)/)[0][0]
Expand Down Expand Up @@ -124,16 +122,16 @@ def current
# Determine if we need to do an import of a private_key and certificate pair
# or just add a signed certificate, then do it.
def create
if ! @resource[:certificate].nil? and ! @resource[:private_key].nil?
if ! certificate.nil? and ! private_key.nil?
import_ks
elsif @resource[:certificate].nil? and ! @resource[:private_key].nil?
raise Puppet::Error 'Keytool is not capable of importing a private key without an accomapaning certificate.'
elsif certificate.nil? and ! private_key.nil?
raise Puppet::Error, 'Keytool is not capable of importing a private key without an accomapaning certificate.'
else
cmd = [
command_keytool,
'-importcert', '-noprompt',
'-alias', @resource[:name],
'-file', @resource[:certificate],
'-file', certificate,
'-keystore', @resource[:target]
]
cmd << '-trustcacerts' if @resource[:trustcacerts] == :true
Expand Down Expand Up @@ -169,6 +167,26 @@ def update
create
end

def certificate
file_path @resource[:certificate]
end

def private_key
file_path @resource[:private_key]
end

def chain
file_path @resource[:chain]
end

def file_path(path)
return path unless path and path.start_with? 'puppet://'

served_file = Puppet::FileServing::Metadata.indirection.find(path, :environment => @resource.catalog.environment)
self.fail "Could not retrieve information for #{path}" unless served_file
served_file.full_path
end

def run_command(cmd, target=false, stdinfile=false, env={})

env[:PATH] = @resource[:path].join(File::PATH_SEPARATOR) if resource[:path]
Expand Down
4 changes: 4 additions & 0 deletions lib/puppet/type/java_ks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ def insync?(is)
subsequently also protected this password will be used to attempt
unlocking...P.S. Let me know if you ever need a separate private key
password parameter...'

validate do |value|
raise Puppet::Error, "password is #{value.length} characters long; must be of length 6 or greater" if value.length < 6
end
end

newparam(:password_file) do
Expand Down
22 changes: 22 additions & 0 deletions spec/fixtures/manifests/site.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
node default {
java_ks { 'puppetca:truststore':
ensure => latest,
certificate => '/etc/puppet/ssl/certs/ca.pem',
target => '/etc/activemq/broker.ts',
password => 'puppet',
trustcacerts => true,
}
java_ks { 'puppetca:keystore':
ensure => latest,
certificate => '/etc/puppet/ssl/certs/ca.pem',
target => '/etc/activemq/broker.ks',
password => 'puppet',
trustcacerts => true,
}
java_ks { 'broker.example.com:/etc/activemq/broker.ks':
ensure => latest,
certificate => '/etc/puppet/ssl/certs/broker.example.com.pe-internal-broker.pem',
private_key => '/etc/puppet/ssl/private_keys/broker.example.com.pe-internal-broker.pem',
password => 'puppet',
}
}
7 changes: 7 additions & 0 deletions spec/hosts/default_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'spec_helper'

describe 'default' do
it 'should work' do
should contain_java_ks('puppetca:truststore')
end
end
28 changes: 28 additions & 0 deletions spec/spec_helper_system.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'rspec-system/spec_helper'
require 'rspec-system-puppet/helpers'
require 'rspec-system-serverspec/helpers'
include Serverspec::Helper::RSpecSystem
include Serverspec::Helper::DetectOS
include RSpecSystemPuppet::Helpers

RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))

# Enable colour
c.tty = true

c.include RSpecSystemPuppet::Helpers

# This is where we 'setup' the nodes before running our tests
c.before :suite do
# Install puppet
puppet_install
puppet_master_install

# Install modules and dependencies
puppet_module_install(:source => proj_root, :module_name => 'apache')
shell('puppet module install puppetlabs-java --version 1.0.1')
end
end

9 changes: 9 additions & 0 deletions spec/system/basic_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'spec_helper_system'

describe 'prep nodes' do
it 'requires java' do
puppet_apply(%{
class { 'java': }
}) { |r| [0,2].should include r.exit_code}
end
end
24 changes: 24 additions & 0 deletions spec/system/keystore_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'spec_helper_system'

describe 'managing java keystores' do
it 'creates a keystore' do
puppet_apply(%{
java_ks { 'puppetca:keystore':
ensure => latest,
certificate => '/var/lib/puppet/ssl/certs/ca.pem',
target => '/etc/keystore.ks',
password => 'puppet',
trustcacerts => true,
}
}) { |r| [0,2].should include r.exit_code}
end

it 'verifies the keystore' do
shell('keytool -list -v -keystore /etc/keystore.ks -storepass puppet') do |r|
expect(r.exit_code).to be_zero
expect(r.stdout).to match(/Your keystore contains 1 entry/)
expect(r.stdout).to match(/Alias name: puppetca/)
expect(r.stdout).to match(/CN=Puppet CA/)
end
end
end
69 changes: 69 additions & 0 deletions spec/system/private_key_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'spec_helper_system'

describe 'managing java private keys' do
it 'creates a private key' do
puppet_apply(%{
java_ks { 'broker.example.com:/etc/private_key.ks':
ensure => latest,
certificate => '/var/lib/puppet/ssl/certs/main.foo.vm.pem',
private_key => '/var/lib/puppet/ssl/private_keys/main.foo.vm.pem',
password => 'puppet',
}
}) { |r| [0,2].should include r.exit_code}
end

it 'verifies the private key' do
shell('keytool -list -v -keystore /etc/private_key.ks -storepass puppet') do |r|
expect(r.exit_code).to be_zero
expect(r.stdout).to match(/Alias name: broker\.example\.com/)
expect(r.stdout).to match(/Entry type: PrivateKeyEntry/)
expect(r.stdout).to match(/CN=Puppet CA/)
end
end

describe 'from a puppet:// uri' do
it 'puts a key in a module' do
puppet_apply(%{
file { [
'/etc/puppet/modules/keys',
'/etc/puppet/modules/keys/files',
]:
ensure => directory,
}
file { '/etc/puppet/modules/keys/files/ca.pem':
ensure => file,
source => '/var/lib/puppet/ssl/certs/ca.pem',
}
file { '/etc/puppet/modules/keys/files/certificate.pem':
ensure => file,
source => '/var/lib/puppet/ssl/certs/main.foo.vm.pem',
}
file { '/etc/puppet/modules/keys/files/private_key.pem':
ensure => file,
source => '/var/lib/puppet/ssl/private_keys/main.foo.vm.pem',
}
}) { |r| [0,2].should include r.exit_code}
end

it 'creates a keystore' do
puppet_apply(%{
java_ks { 'uri.example.com:/etc/uri_key.ks':
ensure => latest,
certificate => 'puppet:///modules/keys/certificate.pem',
private_key => 'puppet:///modules/keys/private_key.pem',
chain => 'puppet:///modules/keys/ca.pem',
password => 'puppet',
}
}) { |r| [0,2].should include r.exit_code}
end

it 'verifies the private key' do
shell('keytool -list -v -keystore /etc/uri_key.ks -storepass puppet') do |r|
expect(r.exit_code).to be_zero
expect(r.stdout).to match(/Alias name: uri\.example\.com/)
expect(r.stdout).to match(/Entry type: PrivateKeyEntry/)
expect(r.stdout).to match(/CN=Puppet CA/)
end
end
end
end
Loading