Skip to content

Commit 4b3b95b

Browse files
committed
Adding jceks support
1 parent 1a59e06 commit 4b3b95b

File tree

6 files changed

+131
-45
lines changed

6 files changed

+131
-45
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ Sets a private key that encrypts traffic to a server application. Must be accomp
114114
#####`trustcacerts`
115115
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'.
116116

117+
### `storetype`
118+
119+
The storetype parameter allows you to use 'jceks' format if desired.
120+
121+
java_ks { 'puppetca:/opt/puppet/truststore.jceks':
122+
ensure => latest,
123+
storetype => 'jceks',
124+
certificate => '/etc/puppet/ssl/certs/ca.pem',
125+
password => 'puppet',
126+
trustcacerts => true,
127+
}
128+
117129

118130
Limitations
119131
------------

lib/puppet/provider/java_ks/keytool.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ def to_pkcs12(path)
2222
File.open(path, "wb") { |f| f.print pkcs12.to_der }
2323
end
2424

25+
# Keytool can only import a jceks keystore if the format is der. Generating and
26+
# importing a keystore is used to add private_key and certifcate pairs.
27+
def to_der(path)
28+
x509_cert = OpenSSL::X509::Certificate.new File.read certificate
29+
File.open(path, "wb") { |f| f.print x509_cert.to_der }
30+
end
31+
2532
def get_password
2633
if @resource[:password_file].nil?
2734
@resource[:password]
@@ -66,13 +73,33 @@ def import_ks
6673
pwfile.close! if pwfile.is_a? Tempfile
6774
end
6875

76+
def import_jceks
77+
tmpder = Tempfile.new("#{@resource[:name]}.")
78+
to_der(tmpder.path)
79+
cmd = [
80+
command_keytool,
81+
'-importcert', '-noprompt',
82+
'-alias', @resource[:name],
83+
'-file', tmpder.path,
84+
'-keystore', @resource[:target],
85+
'-storetype', storetype
86+
]
87+
cmd << '-trustcacerts' if @resource[:trustcacerts] == :true
88+
cmd += [ '-destkeypass', @resource[:destkeypass] ] unless @resource[:destkeypass].nil?
89+
90+
pwfile = password_file
91+
run_command(cmd, @resource[:target], pwfile)
92+
pwfile.close! if pwfile.is_a? Tempfile
93+
end
94+
6995
def exists?
7096
cmd = [
7197
command_keytool,
7298
'-list',
7399
'-keystore', @resource[:target],
74100
'-alias', @resource[:name]
75101
]
102+
cmd += [ '-storetype', storetype ] if storetype == "jceks"
76103
begin
77104
tmpfile = password_file
78105
run_command(cmd, false, tmpfile)
@@ -112,6 +139,7 @@ def current
112139
'-keystore', @resource[:target],
113140
'-alias', @resource[:name]
114141
]
142+
cmd += [ '-storetype', storetype ] if storetype == "jceks"
115143
tmpfile = password_file
116144
output = run_command(cmd, false, tmpfile)
117145
tmpfile.close!
@@ -127,6 +155,8 @@ def create
127155
import_ks
128156
elsif certificate.nil? and !private_key.nil?
129157
raise Puppet::Error, 'Keytool is not capable of importing a private key without an accomapaning certificate.'
158+
elsif storetype == "jceks"
159+
import_jceks
130160
else
131161
cmd = [
132162
command_keytool,
@@ -172,6 +202,10 @@ def chain
172202
@resource[:chain]
173203
end
174204

205+
def storetype
206+
@resource[:storetype]
207+
end
208+
175209
def run_command(cmd, target=false, stdinfile=false, env={})
176210

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

lib/puppet/type/java_ks.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ def insync?(is)
6969
isrequired
7070
end
7171

72+
newparam(:storetype) do
73+
desc 'Optional storetype
74+
Valid options: <jceks>'
75+
76+
newvalues(:jceks)
77+
end
78+
7279
newparam(:private_key) do
7380
desc 'If you want an application to be a server and encrypt traffic,
7481
you will need a private key. Private key entries in a keystore must be

spec/acceptance/keystore_spec.rb

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,68 @@
1010
target = '/etc/keystore.ks'
1111
end
1212

13-
it 'creates a keystore' do
14-
pp = <<-EOS
15-
java_ks { 'puppetca:keystore':
16-
ensure => #{@ensure_ks},
17-
certificate => "#{@temp_dir}ca.pem",
18-
target => '#{target}',
19-
password => 'puppet',
20-
trustcacerts => true,
21-
path => #{@resource_path},
22-
}
23-
EOS
13+
describe 'basic tests' do
14+
it 'should create a keystore' do
15+
pp = <<-EOS
16+
java_ks { 'puppetca:keystore':
17+
ensure => latest,
18+
certificate => "${settings::ssldir}/certs/ca.pem",
19+
target => '/etc/keystore.ks',
20+
password => 'puppet',
21+
trustcacerts => true,
22+
path => #{resource_path},
23+
}
24+
EOS
2425

25-
apply_manifest(pp, :catch_failures => true)
26-
end
26+
apply_manifest(pp, :catch_failures => true)
27+
end
2728

28-
it 'verifies the keystore' do
29-
shell("#{@keytool_path}keytool -list -v -keystore #{target} -storepass puppet") do |r|
30-
expect(r.exit_code).to be_zero
31-
expect(r.stdout).to match(/Your keystore contains 1 entry/)
32-
expect(r.stdout).to match(/Alias name: puppetca/)
33-
expect(r.stdout).to match(/CN=Test CA/)
29+
it 'verifies the keystore' do
30+
shell("#{@keytool_path}keytool -list -v -keystore #{target} -storepass puppet") do |r|
31+
expect(r.exit_code).to be_zero
32+
expect(r.stdout).to match(/Your keystore contains 1 entry/)
33+
expect(r.stdout).to match(/Alias name: puppetca/)
34+
expect(r.stdout).to match(/CN=Test CA/)
35+
end
3436
end
35-
end
3637

37-
it 'uses password_file' do
38-
pp = <<-EOS
39-
file { '#{@temp_dir}password':
40-
ensure => file,
41-
content => 'puppet',
42-
}
43-
java_ks { 'puppetca2:keystore':
44-
ensure => latest,
45-
certificate => "#{@temp_dir}ca2.pem",
46-
target => '#{target}',
47-
password_file => '#{@temp_dir}password',
48-
trustcacerts => true,
49-
path => #{@resource_path},
50-
require => File['#{@temp_dir}password']
51-
}
52-
EOS
38+
it 'uses password_file' do
39+
pp = <<-EOS
40+
file { '#{@temp_dir}password':
41+
ensure => file,
42+
content => 'puppet',
43+
}
44+
java_ks { 'puppetca2:keystore':
45+
ensure => latest,
46+
certificate => "#{@temp_dir}ca2.pem",
47+
target => '#{target}',
48+
password_file => '#{@temp_dir}password',
49+
trustcacerts => true,
50+
path => #{@resource_path},
51+
require => File['#{@temp_dir}password']
52+
}
53+
EOS
5354

54-
apply_manifest(pp, :catch_failures => true)
55+
apply_manifest(pp, :catch_failures => true)
56+
end
5557
end
5658

57-
it 'verifies the keystore' do
58-
shell("#{@keytool_path}keytool -list -v -keystore #{target} -storepass puppet") do |r|
59-
expect(r.exit_code).to be_zero
60-
expect(r.stdout).to match(/Your keystore contains 2 entries/)
61-
expect(r.stdout).to match(/Alias name: puppetca2/)
62-
expect(r.stdout).to match(/CN=Test CA/)
59+
describe 'storetype' do
60+
it 'should create a keystore' do
61+
pp = <<-EOS
62+
java_ks { 'puppetca:keystore':
63+
ensure => latest,
64+
certificate => "${settings::ssldir}/certs/ca.pem",
65+
target => '/etc/keystore.ks',
66+
password => 'puppet',
67+
trustcacerts => true,
68+
path => #{resource_path},
69+
storetype => 'jceks',
70+
}
71+
EOS
72+
73+
apply_manifest(pp, :catch_failures => true)
6374
end
6475
end
76+
6577
end

spec/unit/puppet/provider/java_ks/keytool_spec.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
:password => 'puppet',
1212
:certificate => '/tmp/app.example.com.pem',
1313
:private_key => '/tmp/private/app.example.com.pem',
14+
:storetype => 'jceks',
1415
:provider => described_class.name
1516
}
1617
end
@@ -88,7 +89,7 @@
8889
'mykeytool', '-importkeystore', '-srcstoretype', 'PKCS12',
8990
'-destkeystore', resource[:target],
9091
'-srckeystore', '/tmp/testing.stuff',
91-
'-alias', resource[:name]
92+
'-alias', resource[:name],
9293
], any_parameters
9394
)
9495
provider.import_ks
@@ -111,6 +112,25 @@
111112
end
112113

113114
describe 'when creating entires in a keystore' do
115+
let(:params) do
116+
{
117+
:title => 'app.example.com:/tmp/application.jks',
118+
:name => 'app.example.com',
119+
:target => '/tmp/application.jks',
120+
:password => 'puppet',
121+
:certificate => '/tmp/app.example.com.pem',
122+
:private_key => '/tmp/private/app.example.com.pem',
123+
:provider => described_class.name
124+
}
125+
end
126+
127+
let(:resource) do
128+
Puppet::Type.type(:java_ks).new(params)
129+
end
130+
131+
let(:provider) do
132+
resource.provider
133+
end
114134
it 'should call import_ks if private_key and certificate are provided' do
115135
provider.expects(:import_ks)
116136
provider.create
@@ -123,7 +143,7 @@
123143
'mykeytool', '-importcert', '-noprompt',
124144
'-alias', no_pk[:name],
125145
'-file', no_pk[:certificate],
126-
'-keystore', no_pk[:target]
146+
'-keystore', no_pk[:target],
127147
], any_parameters
128148
)
129149
no_pk.provider.expects(:import_ks).never

spec/unit/puppet/type/java_ks_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
:destkeypass => 'keypass',
1313
:certificate => '/tmp/app.example.com.pem',
1414
:private_key => '/tmp/private/app.example.com.pem',
15+
:storetype => 'jceks',
1516
:provider => :keytool
1617
}
1718
@provider = stub('provider', :class => Puppet::Type.type(:java_ks).defaultprovider, :clear => nil)

0 commit comments

Comments
 (0)