puppetserver-ca 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/puppetserver/ca/clean_action.rb +159 -0
- data/lib/puppetserver/ca/cli.rb +25 -18
- data/lib/puppetserver/ca/create_action.rb +267 -0
- data/lib/puppetserver/ca/generate_action.rb +227 -0
- data/lib/puppetserver/ca/host.rb +26 -0
- data/lib/puppetserver/ca/import_action.rb +11 -69
- data/lib/puppetserver/ca/list_action.rb +155 -0
- data/lib/puppetserver/ca/puppet_config.rb +71 -9
- data/lib/puppetserver/ca/revoke_action.rb +138 -0
- data/lib/puppetserver/ca/sign_action.rb +192 -0
- data/lib/puppetserver/ca/utils.rb +80 -0
- data/lib/puppetserver/ca/version.rb +1 -1
- data/lib/puppetserver/settings/ttl_setting.rb +48 -0
- data/lib/puppetserver/utils/file_utilities.rb +78 -0
- data/lib/puppetserver/utils/http_client.rb +116 -0
- data/lib/puppetserver/utils/signing_digest.rb +25 -0
- data/puppetserver-ca.gemspec +2 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 652bd1357434ad6509c6c08678b57373a4bd6ef1
|
4
|
+
data.tar.gz: 0ad23edeca68b2813ff5d3b1683882f911e7a4c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7445af71aaa5aeed30c9e69ac679cd88bffd60a126e9f0766196a69edb353b8473312dec8b34cb767a87f58837af307005fc11e13d368538404a8ab464415970
|
7
|
+
data.tar.gz: 3a3e1ca91518f68c6a6aedb682357c34d9ddf845f482586d9bc21f342c0a36f9f59f577401c73cfcf9ae12d1dd44a952b5c30c28c84f7fd1032f2ac4e0e8db6f
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'puppetserver/ca/utils'
|
2
|
+
require 'puppetserver/utils/http_client'
|
3
|
+
require 'puppetserver/utils/file_utilities'
|
4
|
+
require 'puppetserver/ca/puppet_config'
|
5
|
+
require 'puppetserver/ca/revoke_action'
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
module Puppetserver
|
11
|
+
module Ca
|
12
|
+
class CleanAction
|
13
|
+
|
14
|
+
include Puppetserver::Utils
|
15
|
+
|
16
|
+
CERTNAME_BLACKLIST = %w{--all --config}
|
17
|
+
|
18
|
+
SUMMARY = 'Clean files from the CA for certificate(s)'
|
19
|
+
BANNER = <<-BANNER
|
20
|
+
Usage:
|
21
|
+
puppetserver ca clean [--help|--version]
|
22
|
+
puppetserver ca clean [--config] --certname CERTNAME[,ADDLCERTNAME]
|
23
|
+
|
24
|
+
Description:
|
25
|
+
Given one or more valid certnames, instructs the CA to revoke certificates
|
26
|
+
matching the given certnames if they exist, and then remove files pertaining
|
27
|
+
to them (keys, cert, and certificate request) over HTTPS using the local
|
28
|
+
agent's PKI
|
29
|
+
|
30
|
+
Options:
|
31
|
+
BANNER
|
32
|
+
|
33
|
+
def self.parser(parsed = {})
|
34
|
+
parsed['certnames'] = []
|
35
|
+
OptionParser.new do |o|
|
36
|
+
o.banner = BANNER
|
37
|
+
o.on('--certname foo,bar', Array,
|
38
|
+
'One or more comma separated certnames') do |certs|
|
39
|
+
parsed['certnames'] += certs
|
40
|
+
end
|
41
|
+
o.on('--config PUPPET.CONF', 'Custom path to puppet.conf') do |conf|
|
42
|
+
parsed['config'] = conf
|
43
|
+
end
|
44
|
+
o.on('--help', 'Displays this clean specific help output') do |help|
|
45
|
+
parsed['help'] = help
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(logger)
|
51
|
+
@logger = logger
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse(args)
|
55
|
+
results = {}
|
56
|
+
parser = self.class.parser(results)
|
57
|
+
|
58
|
+
errors = Utils.parse_with_errors(parser, args)
|
59
|
+
|
60
|
+
results['certnames'].each do |certname|
|
61
|
+
if CERTNAME_BLACKLIST.include?(certname)
|
62
|
+
errors << " Cannot manage cert named `#{certname}` from " +
|
63
|
+
"the CLI, if needed use the HTTP API directly"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if results['certnames'].empty?
|
68
|
+
errors << ' At least one certname is required to clean'
|
69
|
+
end
|
70
|
+
|
71
|
+
errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
|
72
|
+
|
73
|
+
exit_code = errors_were_handled ? 1 : nil
|
74
|
+
|
75
|
+
return results, exit_code
|
76
|
+
end
|
77
|
+
|
78
|
+
def run(args)
|
79
|
+
certnames = args['certnames']
|
80
|
+
config = args['config']
|
81
|
+
|
82
|
+
if config
|
83
|
+
errors = FileUtilities.validate_file_paths(config)
|
84
|
+
return 1 if Utils.handle_errors(@logger, errors)
|
85
|
+
end
|
86
|
+
|
87
|
+
puppet = PuppetConfig.parse(config)
|
88
|
+
return 1 if Utils.handle_errors(@logger, puppet.errors)
|
89
|
+
|
90
|
+
passed = clean_certs(certnames, puppet.settings)
|
91
|
+
|
92
|
+
return passed ? 0 : 1
|
93
|
+
end
|
94
|
+
|
95
|
+
def clean_certs(certnames, settings)
|
96
|
+
client = HttpClient.new(settings[:localcacert],
|
97
|
+
settings[:certificate_revocation],
|
98
|
+
settings[:hostcrl])
|
99
|
+
|
100
|
+
url = client.make_ca_url(settings[:ca_server],
|
101
|
+
settings[:ca_port],
|
102
|
+
'certificate_status')
|
103
|
+
|
104
|
+
results = client.with_connection(url) do |connection|
|
105
|
+
certnames.map do |certname|
|
106
|
+
url.resource_name = certname
|
107
|
+
revoke_result = connection.put(RevokeAction::REQUEST_BODY, url)
|
108
|
+
revoked = check_revocation(revoke_result, certname)
|
109
|
+
|
110
|
+
cleaned = nil
|
111
|
+
unless revoked == :error
|
112
|
+
clean_result = connection.delete(url)
|
113
|
+
cleaned = check_result(clean_result, certname)
|
114
|
+
end
|
115
|
+
|
116
|
+
cleaned == :success && [:success, :not_found].include?(revoked)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
return results.all?
|
121
|
+
end
|
122
|
+
|
123
|
+
# possibly logs the action, always returns a status symbol 👑
|
124
|
+
def check_revocation(result, certname)
|
125
|
+
case result.code
|
126
|
+
when '200', '204'
|
127
|
+
@logger.inform "Revoked certificate for #{certname}"
|
128
|
+
return :success
|
129
|
+
when '404'
|
130
|
+
return :not_found
|
131
|
+
else
|
132
|
+
@logger.err 'Error:'
|
133
|
+
@logger.err " Failed revoking certificate for #{certname}"
|
134
|
+
@logger.err " Received code: #{result.code}, body: #{result.body}"
|
135
|
+
return :error
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# logs the action and returns a status symbol 👑
|
140
|
+
def check_result(result, certname)
|
141
|
+
case result.code
|
142
|
+
when '200', '204'
|
143
|
+
@logger.inform "Cleaned files related to #{certname}"
|
144
|
+
return :success
|
145
|
+
when '404'
|
146
|
+
@logger.err 'Error:'
|
147
|
+
@logger.err " Could not find files for #{certname}"
|
148
|
+
return :not_found
|
149
|
+
else
|
150
|
+
@logger.err 'Error:'
|
151
|
+
@logger.err " When cleaning #{certname} received:"
|
152
|
+
@logger.err " code: #{result.code}"
|
153
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
154
|
+
return :error
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/puppetserver/ca/cli.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'puppetserver/ca/version'
|
3
|
-
require 'puppetserver/ca/import_action'
|
4
3
|
require 'puppetserver/ca/logger'
|
4
|
+
require 'puppetserver/ca/clean_action'
|
5
|
+
require 'puppetserver/ca/import_action'
|
6
|
+
require 'puppetserver/ca/generate_action'
|
7
|
+
require 'puppetserver/ca/revoke_action'
|
8
|
+
require 'puppetserver/ca/list_action'
|
9
|
+
require 'puppetserver/ca/sign_action'
|
10
|
+
require 'puppetserver/ca/utils'
|
11
|
+
require 'puppetserver/ca/create_action'
|
5
12
|
|
6
13
|
module Puppetserver
|
7
14
|
module Ca
|
@@ -13,7 +20,15 @@ Manage the Private Key Infrastructure for
|
|
13
20
|
Puppet Server's built-in Certificate Authority
|
14
21
|
BANNER
|
15
22
|
|
16
|
-
VALID_ACTIONS = {
|
23
|
+
VALID_ACTIONS = {
|
24
|
+
'clean' => CleanAction,
|
25
|
+
'create' => CreateAction,
|
26
|
+
'generate' => GenerateAction,
|
27
|
+
'import' => ImportAction,
|
28
|
+
'list' => ListAction,
|
29
|
+
'revoke' => RevokeAction,
|
30
|
+
'sign' => SignAction
|
31
|
+
}
|
17
32
|
|
18
33
|
ACTION_LIST = "\nAvailable Actions:\n" +
|
19
34
|
VALID_ACTIONS.map do |action, cls|
|
@@ -22,10 +37,12 @@ BANNER
|
|
22
37
|
|
23
38
|
ACTION_OPTIONS = "\nAction Options:\n" +
|
24
39
|
VALID_ACTIONS.map do |action, cls|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
action_summary = cls.parser.summarize.
|
41
|
+
select{|line| line =~ /^\s*--/ }.
|
42
|
+
reject{|line| line =~ /--help|--version/ }
|
43
|
+
summary = action_summary.empty? ? ' N/A' : action_summary.join('')
|
44
|
+
|
45
|
+
" #{action}:\n" + summary
|
29
46
|
end.join("\n")
|
30
47
|
|
31
48
|
|
@@ -86,19 +103,9 @@ BANNER
|
|
86
103
|
|
87
104
|
end
|
88
105
|
|
89
|
-
|
90
|
-
|
91
|
-
begin
|
92
|
-
general_parser.order!(inputs) do |nonopt|
|
93
|
-
nonopts << nonopt
|
94
|
-
end
|
95
|
-
rescue OptionParser::InvalidOption => e
|
96
|
-
unparsed += e.args
|
97
|
-
unparsed << inputs.shift unless inputs.first =~ /^-{1,2}/
|
98
|
-
retry
|
99
|
-
end
|
106
|
+
all,_,_,_ = Utils.parse_without_raising(general_parser, inputs)
|
100
107
|
|
101
|
-
return general_parser, parsed,
|
108
|
+
return general_parser, parsed, all
|
102
109
|
end
|
103
110
|
end
|
104
111
|
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'puppetserver/ca/utils'
|
2
|
+
require 'puppetserver/ca/host'
|
3
|
+
require 'puppetserver/ca/puppet_config'
|
4
|
+
require 'puppetserver/utils/file_utilities'
|
5
|
+
require 'puppetserver/utils/http_client'
|
6
|
+
require 'puppetserver/utils/signing_digest'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Puppetserver
|
10
|
+
module Ca
|
11
|
+
class CreateAction
|
12
|
+
|
13
|
+
include Puppetserver::Utils
|
14
|
+
|
15
|
+
# Only allow printing ascii characters, excluding /
|
16
|
+
VALID_CERTNAME = /\A[ -.0-~]+\Z/
|
17
|
+
CERTNAME_BLACKLIST = %w{--all --config}
|
18
|
+
|
19
|
+
SUMMARY = "Create a new certificate signed by the CA"
|
20
|
+
BANNER = <<-BANNER
|
21
|
+
Usage:
|
22
|
+
puppetserver ca create [--help]
|
23
|
+
puppetserver ca create [--config] --certname CERTNAME[,ADDLCERTNAME]
|
24
|
+
|
25
|
+
Description:
|
26
|
+
Creates a new certificate signed by the intermediate CA
|
27
|
+
and stores generated keys and certs on disk.
|
28
|
+
|
29
|
+
To determine the target location, the default puppet.conf
|
30
|
+
is consulted for custom values. If using a custom puppet.conf
|
31
|
+
provide it with the --config flag
|
32
|
+
|
33
|
+
Options:
|
34
|
+
BANNER
|
35
|
+
def initialize(logger)
|
36
|
+
@logger = logger
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.parser(parsed = {})
|
40
|
+
parsed['certnames'] = []
|
41
|
+
OptionParser.new do |opts|
|
42
|
+
opts.banner = BANNER
|
43
|
+
opts.on('--certname FOO,BAR', Array,
|
44
|
+
'One or more comma separated certnames') do |certs|
|
45
|
+
parsed['certnames'] += certs
|
46
|
+
end
|
47
|
+
opts.on('--help', 'Display this create specific help output') do |help|
|
48
|
+
parsed['help'] = true
|
49
|
+
end
|
50
|
+
opts.on('--config CONF', 'Path to puppet.conf') do |conf|
|
51
|
+
parsed['config'] = conf
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse(args)
|
57
|
+
results = {}
|
58
|
+
parser = self.class.parser(results)
|
59
|
+
|
60
|
+
errors = Utils.parse_with_errors(parser, args)
|
61
|
+
|
62
|
+
if results['certnames'].empty?
|
63
|
+
errors << ' At least one certname is required to create'
|
64
|
+
else
|
65
|
+
results['certnames'].each do |certname|
|
66
|
+
if CERTNAME_BLACKLIST.include?(certname)
|
67
|
+
errors << " Cannot manage cert named `#{certname}` from " +
|
68
|
+
"the CLI, if needed use the HTTP API directly"
|
69
|
+
end
|
70
|
+
|
71
|
+
if certname.match(/\p{Upper}/)
|
72
|
+
errors << " Certificate names must be lower case"
|
73
|
+
end
|
74
|
+
|
75
|
+
unless certname =~ VALID_CERTNAME
|
76
|
+
errors << " Certname #{certname} must not contain unprintable or non-ASCII characters"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
|
82
|
+
|
83
|
+
exit_code = errors_were_handled ? 1 : nil
|
84
|
+
|
85
|
+
return results, exit_code
|
86
|
+
end
|
87
|
+
|
88
|
+
def run(input)
|
89
|
+
certnames = input['certnames']
|
90
|
+
config_path = input['config']
|
91
|
+
|
92
|
+
# Validate config_path provided
|
93
|
+
if config_path
|
94
|
+
errors = FileUtilities.validate_file_paths(config_path)
|
95
|
+
return 1 if Utils.handle_errors(@logger, errors)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Load, resolve, and validate puppet config settings
|
99
|
+
puppet = PuppetConfig.parse(config_path)
|
100
|
+
return 1 if Utils.handle_errors(@logger, puppet.errors)
|
101
|
+
|
102
|
+
# Load most secure signing digest we can for csr signing.
|
103
|
+
signer = SigningDigest.new
|
104
|
+
return 1 if Utils.handle_errors(@logger, signer.errors)
|
105
|
+
|
106
|
+
# Make sure we have all the directories where we will be writing files
|
107
|
+
FileUtilities.ensure_dir(puppet.settings[:certdir])
|
108
|
+
FileUtilities.ensure_dir(puppet.settings[:privatekeydir])
|
109
|
+
FileUtilities.ensure_dir(puppet.settings[:publickeydir])
|
110
|
+
|
111
|
+
# Generate and save certs and associated keys
|
112
|
+
all_passed = generate_certs(certnames, puppet.settings, signer.digest)
|
113
|
+
return all_passed ? 0 : 1
|
114
|
+
end
|
115
|
+
|
116
|
+
# Create csrs and keys, then submit them to CA, request for the CA to sign
|
117
|
+
# them, download the signed certificates from the CA, and finally save
|
118
|
+
# the signed certs and associated keys. Returns true if all certs were
|
119
|
+
# successfully created and saved.
|
120
|
+
def generate_certs(certnames, settings, digest)
|
121
|
+
passed = certnames.map do |certname|
|
122
|
+
key, csr = generate_key_csr(certname, settings, digest)
|
123
|
+
return false unless submit_certificate_request(certname, csr.to_s, settings)
|
124
|
+
return false unless sign_cert(certname, settings)
|
125
|
+
if download_cert(certname, settings)
|
126
|
+
save_keys(key, certname, settings)
|
127
|
+
true
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
passed.all?
|
133
|
+
end
|
134
|
+
|
135
|
+
def generate_key_csr(certname, settings, digest)
|
136
|
+
host = Puppetserver::Ca::Host.new(digest)
|
137
|
+
private_key = host.create_private_key(settings[:keylength])
|
138
|
+
csr = host.create_csr(certname, private_key)
|
139
|
+
|
140
|
+
return private_key, csr
|
141
|
+
end
|
142
|
+
|
143
|
+
def http_client(settings)
|
144
|
+
@client ||= HttpClient.new(settings[:localcacert],
|
145
|
+
settings[:certificate_revocation],
|
146
|
+
settings[:hostcrl])
|
147
|
+
end
|
148
|
+
|
149
|
+
# Make an HTTP request to submit certificate requests to CA
|
150
|
+
# @param certname [String] the name of the certificate to fetch
|
151
|
+
# @param csr [String] string version of a OpenSSL::X509::Request
|
152
|
+
# @param settings [Hash] a hash of config settings
|
153
|
+
# @return [Boolean] success of all csrs being submitted to CA
|
154
|
+
def submit_certificate_request(certname, csr, settings)
|
155
|
+
client = http_client(settings)
|
156
|
+
url = client.make_ca_url(settings[:ca_server],
|
157
|
+
settings[:ca_port],
|
158
|
+
'certificate_request',
|
159
|
+
certname)
|
160
|
+
|
161
|
+
client.with_connection(url) do |connection|
|
162
|
+
result = connection.put(csr, url)
|
163
|
+
check_submit_result(result, certname)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def check_submit_result(result, certname)
|
168
|
+
case result.code
|
169
|
+
when '200', '204'
|
170
|
+
@logger.inform "Successfully submitted certificate request for #{certname}"
|
171
|
+
return true
|
172
|
+
else
|
173
|
+
@logger.err 'Error:'
|
174
|
+
@logger.err " When certificate request submitted for #{certname}:"
|
175
|
+
@logger.err " code: #{result.code}"
|
176
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Make an HTTP request to CA to sign the named certificates
|
182
|
+
# @param certname [String] the name of the certificate to have signed
|
183
|
+
# @param settings [Hash] a hash of config settings
|
184
|
+
# @return [Boolean] the success of certificates being signed
|
185
|
+
def sign_cert(certname, settings)
|
186
|
+
client = http_client(settings)
|
187
|
+
|
188
|
+
url = client.make_ca_url(settings[:ca_server],
|
189
|
+
settings[:ca_port],
|
190
|
+
'certificate_status',
|
191
|
+
certname)
|
192
|
+
|
193
|
+
client.with_connection(url) do |connection|
|
194
|
+
body = JSON.dump({desired_state: 'signed'})
|
195
|
+
result = connection.put(body, url)
|
196
|
+
check_sign_result(result, certname)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_sign_result(result, certname)
|
201
|
+
case result.code
|
202
|
+
when '204'
|
203
|
+
@logger.inform "Successfully signed certificate request for #{certname}"
|
204
|
+
return true
|
205
|
+
else
|
206
|
+
@logger.err 'Error:'
|
207
|
+
@logger.err " When signing request submitted for #{certname}:"
|
208
|
+
@logger.err " code: #{result.code}"
|
209
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
210
|
+
return false
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Make an HTTP request to fetch the named certificates from CA
|
215
|
+
# @param certname [String] the name of the certificate to fetch
|
216
|
+
# @param settings [Hash] a hash of config settings
|
217
|
+
# @return [Boolean] the success of certificate being downloaded
|
218
|
+
def download_cert(certname, settings)
|
219
|
+
client = http_client(settings)
|
220
|
+
url = client.make_ca_url(settings[:ca_server],
|
221
|
+
settings[:ca_port],
|
222
|
+
'certificate',
|
223
|
+
certname)
|
224
|
+
client.with_connection(url) do |connection|
|
225
|
+
result = connection.get(url)
|
226
|
+
if downloaded = check_download_result(result, certname)
|
227
|
+
save_file(result.body, certname, settings[:certdir], "Certificate")
|
228
|
+
@logger.inform "Successfully downloaded and saved certificate #{certname} to #{settings[:certdir]}/#{certname}.pem"
|
229
|
+
end
|
230
|
+
downloaded
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def check_download_result(result, certname)
|
235
|
+
case result.code
|
236
|
+
when '200'
|
237
|
+
return true
|
238
|
+
when '404'
|
239
|
+
@logger.err 'Error:'
|
240
|
+
@logger.err " Signed certificate #{certname} could not be found on the CA"
|
241
|
+
return false
|
242
|
+
else
|
243
|
+
@logger.err 'Error:'
|
244
|
+
@logger.err " When download requested for certificate #{certname}:"
|
245
|
+
@logger.err " code: #{result.code}"
|
246
|
+
@logger.err " body: #{result.body.to_s}" if result.body
|
247
|
+
return false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def save_keys(key, certname, settings)
|
252
|
+
public_key = key.public_key
|
253
|
+
save_file(key, certname, settings[:privatekeydir], "Private key")
|
254
|
+
save_file(public_key, certname, settings[:publickeydir], "Public key")
|
255
|
+
@logger.inform "Successfully saved private key for #{certname} to #{settings[:privatekeydir]}/#{certname}.pem"
|
256
|
+
@logger.inform "Successfully saved public key for #{certname} to #{settings[:publickeydir]}/#{certname}.pem"
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
def save_file(content, certname, dir, type)
|
261
|
+
location = File.join(dir, "#{certname}.pem")
|
262
|
+
@logger.warn "#{type} #{certname}.pem already exists, overwriting" if File.exist?(location)
|
263
|
+
FileUtilities.write_file(location, content, 0640)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|