Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding jceks support #132

Merged
merged 1 commit into from Oct 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -114,6 +114,18 @@ Sets a private key that encrypts traffic to a server application. Must be accomp
#####`trustcacerts`
Certificate authorities input into a keystore aren’t trusted by default, so if you are adding a CA you need to set this parameter to 'true'. Valid options: 'true' or 'false'. Default: 'false'.

### `storetype`

The storetype parameter allows you to use 'jceks' format if desired.

java_ks { 'puppetca:/opt/puppet/truststore.jceks':
ensure => latest,
storetype => 'jceks',
certificate => '/etc/puppet/ssl/certs/ca.pem',
password => 'puppet',
trustcacerts => true,
}


Limitations
------------
Expand Down
34 changes: 34 additions & 0 deletions lib/puppet/provider/java_ks/keytool.rb
Expand Up @@ -22,6 +22,13 @@ def to_pkcs12(path)
File.open(path, "wb") { |f| f.print pkcs12.to_der }
end

# Keytool can only import a jceks keystore if the format is der. Generating and
# importing a keystore is used to add private_key and certifcate pairs.
def to_der(path)
x509_cert = OpenSSL::X509::Certificate.new File.read certificate
File.open(path, "wb") { |f| f.print x509_cert.to_der }
end

def get_password
if @resource[:password_file].nil?
@resource[:password]
Expand Down Expand Up @@ -66,13 +73,33 @@ def import_ks
pwfile.close! if pwfile.is_a? Tempfile
end

def import_jceks
tmpder = Tempfile.new("#{@resource[:name]}.")
to_der(tmpder.path)
cmd = [
command_keytool,
'-importcert', '-noprompt',
'-alias', @resource[:name],
'-file', tmpder.path,
'-keystore', @resource[:target],
'-storetype', storetype
]
cmd << '-trustcacerts' if @resource[:trustcacerts] == :true
cmd += [ '-destkeypass', @resource[:destkeypass] ] unless @resource[:destkeypass].nil?

pwfile = password_file
run_command(cmd, @resource[:target], pwfile)
pwfile.close! if pwfile.is_a? Tempfile
end

def exists?
cmd = [
command_keytool,
'-list',
'-keystore', @resource[:target],
'-alias', @resource[:name]
]
cmd += [ '-storetype', storetype ] if storetype == "jceks"
begin
tmpfile = password_file
run_command(cmd, false, tmpfile)
Expand Down Expand Up @@ -112,6 +139,7 @@ def current
'-keystore', @resource[:target],
'-alias', @resource[:name]
]
cmd += [ '-storetype', storetype ] if storetype == "jceks"
tmpfile = password_file
output = run_command(cmd, false, tmpfile)
tmpfile.close!
Expand All @@ -127,6 +155,8 @@ def create
import_ks
elsif certificate.nil? and !private_key.nil?
raise Puppet::Error, 'Keytool is not capable of importing a private key without an accomapaning certificate.'
elsif storetype == "jceks"
import_jceks
else
cmd = [
command_keytool,
Expand Down Expand Up @@ -172,6 +202,10 @@ def chain
@resource[:chain]
end

def storetype
@resource[:storetype]
end

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

env[:PATH] = @resource[:path].join(File::PATH_SEPARATOR) if resource[:path]
Expand Down
7 changes: 7 additions & 0 deletions lib/puppet/type/java_ks.rb
Expand Up @@ -69,6 +69,13 @@ def insync?(is)
isrequired
end

newparam(:storetype) do
desc 'Optional storetype
Valid options: <jceks>'

newvalues(:jceks)
end

newparam(:private_key) do
desc '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
Expand Down
98 changes: 55 additions & 43 deletions spec/acceptance/keystore_spec.rb
Expand Up @@ -10,56 +10,68 @@
target = '/etc/keystore.ks'
end

it 'creates a keystore' do
pp = <<-EOS
java_ks { 'puppetca:keystore':
ensure => #{@ensure_ks},
certificate => "#{@temp_dir}ca.pem",
target => '#{target}',
password => 'puppet',
trustcacerts => true,
path => #{@resource_path},
}
EOS
describe 'basic tests' do
it 'should create a keystore' do
pp = <<-EOS
java_ks { 'puppetca:keystore':
ensure => latest,
certificate => "${settings::ssldir}/certs/ca.pem",
target => '/etc/keystore.ks',
password => 'puppet',
trustcacerts => true,
path => #{resource_path},
}
EOS

apply_manifest(pp, :catch_failures => true)
end
apply_manifest(pp, :catch_failures => true)
end

it 'verifies the keystore' do
shell("#{@keytool_path}keytool -list -v -keystore #{target} -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=Test CA/)
it 'verifies the keystore' do
shell("#{@keytool_path}keytool -list -v -keystore #{target} -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=Test CA/)
end
end
end

it 'uses password_file' do
pp = <<-EOS
file { '#{@temp_dir}password':
ensure => file,
content => 'puppet',
}
java_ks { 'puppetca2:keystore':
ensure => latest,
certificate => "#{@temp_dir}ca2.pem",
target => '#{target}',
password_file => '#{@temp_dir}password',
trustcacerts => true,
path => #{@resource_path},
require => File['#{@temp_dir}password']
}
EOS
it 'uses password_file' do
pp = <<-EOS
file { '#{@temp_dir}password':
ensure => file,
content => 'puppet',
}
java_ks { 'puppetca2:keystore':
ensure => latest,
certificate => "#{@temp_dir}ca2.pem",
target => '#{target}',
password_file => '#{@temp_dir}password',
trustcacerts => true,
path => #{@resource_path},
require => File['#{@temp_dir}password']
}
EOS

apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_failures => true)
end
end

it 'verifies the keystore' do
shell("#{@keytool_path}keytool -list -v -keystore #{target} -storepass puppet") do |r|
expect(r.exit_code).to be_zero
expect(r.stdout).to match(/Your keystore contains 2 entries/)
expect(r.stdout).to match(/Alias name: puppetca2/)
expect(r.stdout).to match(/CN=Test CA/)
describe 'storetype' do
it 'should create a keystore' do
pp = <<-EOS
java_ks { 'puppetca:keystore':
ensure => latest,
certificate => "${settings::ssldir}/certs/ca.pem",
target => '/etc/keystore.ks',
password => 'puppet',
trustcacerts => true,
path => #{resource_path},
storetype => 'jceks',
}
EOS

apply_manifest(pp, :catch_failures => true)
end
end

end
24 changes: 22 additions & 2 deletions spec/unit/puppet/provider/java_ks/keytool_spec.rb
Expand Up @@ -11,6 +11,7 @@
:password => 'puppet',
:certificate => '/tmp/app.example.com.pem',
:private_key => '/tmp/private/app.example.com.pem',
:storetype => 'jceks',
:provider => described_class.name
}
end
Expand Down Expand Up @@ -88,7 +89,7 @@
'mykeytool', '-importkeystore', '-srcstoretype', 'PKCS12',
'-destkeystore', resource[:target],
'-srckeystore', '/tmp/testing.stuff',
'-alias', resource[:name]
'-alias', resource[:name],
], any_parameters
)
provider.import_ks
Expand All @@ -111,6 +112,25 @@
end

describe 'when creating entires in a keystore' do
let(:params) do
{
:title => 'app.example.com:/tmp/application.jks',
:name => 'app.example.com',
:target => '/tmp/application.jks',
:password => 'puppet',
:certificate => '/tmp/app.example.com.pem',
:private_key => '/tmp/private/app.example.com.pem',
:provider => described_class.name
}
end

let(:resource) do
Puppet::Type.type(:java_ks).new(params)
end

let(:provider) do
resource.provider
end
it 'should call import_ks if private_key and certificate are provided' do
provider.expects(:import_ks)
provider.create
Expand All @@ -123,7 +143,7 @@
'mykeytool', '-importcert', '-noprompt',
'-alias', no_pk[:name],
'-file', no_pk[:certificate],
'-keystore', no_pk[:target]
'-keystore', no_pk[:target],
], any_parameters
)
no_pk.provider.expects(:import_ks).never
Expand Down
1 change: 1 addition & 0 deletions spec/unit/puppet/type/java_ks_spec.rb
Expand Up @@ -12,6 +12,7 @@
:destkeypass => 'keypass',
:certificate => '/tmp/app.example.com.pem',
:private_key => '/tmp/private/app.example.com.pem',
:storetype => 'jceks',
:provider => :keytool
}
@provider = stub('provider', :class => Puppet::Type.type(:java_ks).defaultprovider, :clear => nil)
Expand Down