Showing with 50 additions and 25 deletions.
  1. +1 −0 .gitignore
  2. +5 −4 Modulefile
  3. +32 −21 lib/puppet/provider/java_ks/keytool.rb
  4. +12 −0 lib/puppet/type/java_ks.rb
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pkg
metadata.json
9 changes: 5 additions & 4 deletions Modulefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name 'puppetlabs-java_ks'
version '1.0.0'
source 'https://github.com/puppetlabs/puppetlabs-java_ks.git'
name 'puppetlabs-java_ks'
version '1.0.1'

author 'puppetlabs'
license 'ASL 2.0'
project_page 'https://github.com/puppetlabs/puppetlabs-java_ks'
source 'https://github.com/puppetlabs/puppetlabs-java_ks.git'
summary 'Manage arbitrary Java keystore files'
description 'Uses a combination of keytool and openssl to manage entries in a Java keystore.'
project_page 'https://github.com/puppetlabs/puppetlabs-java_ks'
53 changes: 32 additions & 21 deletions lib/puppet/provider/java_ks/keytool.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
require 'puppet/util/filetype'

Puppet::Type.type(:java_ks).provide(:keytool) do
desc 'Uses a combination of openssl and keytool to manage Java keystores'

commands :openssl => 'openssl'
commands :keytool => 'keytool'
def command_openssl
'openssl'
end

def command_keytool
'keytool'
end

# 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 = ''
cmd = [
command(:openssl),
command_openssl,
'pkcs12', '-export', '-passout', 'stdin',
'-in', @resource[:certificate],
'-inkey', @resource[:private_key],
Expand All @@ -25,14 +31,7 @@ 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]}.")
if Puppet::Util::Execution.respond_to?(:withenv)
withenv = Puppet::Util::Execution.method(:withenv)
else
withenv = Puppet::Util.method(:withenv)
end
output = withenv.call('RANDFILE' => randfile.path) do
run_command(cmd, false, tmpfile)
end
output = run_command(cmd, false, tmpfile, 'RANDFILE' => randfile.path)
tmpfile.close!
randfile.close!
return output
Expand All @@ -44,7 +43,7 @@ def import_ks
tmppk12.write(to_pkcs12)
tmppk12.flush
cmd = [
command(:keytool),
command_keytool,
'-importkeystore', '-srcstoretype', 'PKCS12',
'-destkeystore', @resource[:target],
'-srckeystore', tmppk12.path,
Expand All @@ -65,7 +64,7 @@ def import_ks

def exists?
cmd = [
command(:keytool),
command_keytool,
'-list',
'-keystore', @resource[:target],
'-alias', @resource[:name]
Expand All @@ -85,7 +84,7 @@ def exists?
# Reading the fingerprint of the certificate on disk.
def latest
cmd = [
command(:openssl),
command_openssl,
'x509', '-fingerprint', '-md5', '-noout',
'-in', @resource[:certificate]
]
Expand All @@ -98,7 +97,7 @@ def latest
def current
output = ''
cmd = [
command(:keytool),
command_keytool,
'-list', '-v',
'-keystore', @resource[:target],
'-alias', @resource[:name]
Expand All @@ -121,7 +120,7 @@ def create
raise Puppet::Error 'Keytool is not capable of importing a private key without an accomapaning certificate.'
else
cmd = [
command(:keytool),
command_keytool,
'-importcert', '-noprompt',
'-alias', @resource[:name],
'-file', @resource[:certificate],
Expand All @@ -142,7 +141,7 @@ def create

def destroy
cmd = [
command(:keytool),
command_keytool,
'-delete',
'-alias', @resource[:name],
'-keystore', @resource[:target]
Expand All @@ -160,7 +159,9 @@ def update
create
end

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

env[:PATH] = @resource[:path].join(File::PATH_SEPARATOR) if resource[:path]

# The Puppet::Util::Execution.execute method is deparcated in Puppet 3.x
# but we need this to work on 2.7.x too.
Expand All @@ -170,6 +171,12 @@ def run_command(cmd, target=false, stdinfile=false)
exec_method = Puppet::Util.method(:execute)
end

if Puppet::Util::Execution.respond_to?(:withenv)
withenv = Puppet::Util::Execution.method(:withenv)
else
withenv = Puppet::Util.method(:withenv)
end

# the java keytool will not correctly deal with an empty target keystore
# file. If we encounter an empty keystore target file, preserve the mode,
# owner and group, and delete the empty file.
Expand All @@ -187,7 +194,7 @@ def run_command(cmd, target=false, stdinfile=false)
# trumps.
if Facter.value('osfamily') == 'Suse' and @resource[:password]
cmd_to_run = cmd.is_a?(String) ? cmd.split(/\s/).first : cmd.first
if cmd_to_run == command(:keytool)
if cmd_to_run == command_keytool
cmd << '-srcstorepass' << @resource[:password]
cmd << '-deststorepass' << @resource[:password]
end
Expand All @@ -196,9 +203,13 @@ def run_command(cmd, target=false, stdinfile=false)
# Now run the command
options = { :failonfail => true, :combine => true }
output = if stdinfile
exec_method.call(cmd, options.merge(:stdinfile => stdinfile.path))
withenv.call(env) do
exec_method.call(cmd, options.merge(:stdinfile => stdinfile.path))
end
else
exec_method.call(cmd, options)
withenv.call(env) do
exec_method.call(cmd, options)
end
end

# for previously empty files, restore the mode, owner and group. The funky
Expand Down
12 changes: 12 additions & 0 deletions lib/puppet/type/java_ks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ def insync?(is)
defaultto :false
end

newparam(:path) do
desc "The search path used for command (keytool, openssl) execution.
Paths can be specified as an array or as a '#{File::PATH_SEPARATOR}' separated list."

# Support both arrays and colon-separated fields.
def value=(*values)
@value = values.flatten.collect { |val|
val.split(File::PATH_SEPARATOR)
}.flatten
end
end

# Where we setup autorequires.
autorequire(:file) do
auto_requires = []
Expand Down