knife-openvpn 0.0.1
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 +7 -0
- data/README.md +2 -0
- data/knife-openvpn.gemspec +16 -0
- data/lib/chef/knife/openvpn.rb +441 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8dd1f7ab9cad0728ed9c378d00f4572308435bad
|
4
|
+
data.tar.gz: 7426c914244fad2bfd8a9ae0d11b858369d20586
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 95308e27b0a782af5a32f28687b6d3c53bd1a05778b44bec4177ae14e780b898b70caaae04cea30af14f075cb214b136cf904be272c7249bdf11cac8e221f36f
|
7
|
+
data.tar.gz: 74f43c6fead9c46d76edb409ba435e4aece085be7841f1ec6fd6f5892cc43f05a4326b0323c1f5ebdd57dd3cc8cbca58bb7a3acf0ce3194584a5fcbe8f8d0cee
|
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'knife-openvpn'
|
6
|
+
gem.version = '0.0.1'
|
7
|
+
gem.summary = 'A knife plugin for Express 42 openvpn cookbook'
|
8
|
+
gem.description = gem.summary
|
9
|
+
gem.authors = ['LLC Express 42']
|
10
|
+
gem.email = 'cookbooks@express42.com'
|
11
|
+
gem.homepage = 'https://github.com/express42/knife-openvpn'
|
12
|
+
gem.license = 'MIT'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
end
|
@@ -0,0 +1,441 @@
|
|
1
|
+
#
|
2
|
+
# Cookbook Name:: openvpn
|
3
|
+
# OpenVPN knife plugin
|
4
|
+
#
|
5
|
+
# Copyright 2013, LLC Express 42
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#
|
26
|
+
|
27
|
+
module OpenvpnPlugin
|
28
|
+
class Openvpn < Chef::Knife
|
29
|
+
def run
|
30
|
+
ui.info 'knife openvpn (user|server) action ARGS OPTS'
|
31
|
+
end
|
32
|
+
|
33
|
+
deps do
|
34
|
+
require 'chef/encrypted_data_bag_item'
|
35
|
+
require 'json'
|
36
|
+
require 'openssl'
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_databag_secret
|
40
|
+
databag_secret_file = File.join(Dir.pwd, '.chef/encrypted_data_bag_secret')
|
41
|
+
unless File.exist? databag_secret_file
|
42
|
+
fail_with "Can't find encrypted databag secret file at #{databag_secret_file}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_existing_databag(server_name, fail_if_exists = false)
|
47
|
+
databag_directory = File.join(Dir.pwd, "data_bags/openvpn-#{server_name}")
|
48
|
+
if File.directory? databag_directory
|
49
|
+
if fail_if_exists # databag exists and we want to create new
|
50
|
+
fail_with "Data bag directory #{databag_directory} already exists."
|
51
|
+
end
|
52
|
+
else
|
53
|
+
unless fail_if_exists # no such databag, but we want to use it
|
54
|
+
fail_with "Data bag #{databag_directory} not exists."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fail_with(error_message)
|
60
|
+
ui.error "Error: #{error_message}"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def make_name(cn, cert_config)
|
65
|
+
name = OpenSSL::X509::Name.new
|
66
|
+
name.add_entry 'CN', cn
|
67
|
+
%w(C L O OU ST mail).each { |entry| name.add_entry(entry, cert_config[entry]) }
|
68
|
+
name
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_databag_secret
|
72
|
+
databag_secret_file = File.join(Dir.pwd, '.chef/encrypted_data_bag_secret')
|
73
|
+
secret = Chef::EncryptedDataBagItem.load_secret(databag_secret_file)
|
74
|
+
secret
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_extensions_factory(subject_cert, issuer_cert)
|
78
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
79
|
+
factory.subject_certificate = subject_cert
|
80
|
+
factory.issuer_certificate = issuer_cert
|
81
|
+
factory
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_ca_extensions(ca_cert)
|
85
|
+
ef = get_extensions_factory ca_cert, ca_cert
|
86
|
+
ca_cert.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true))
|
87
|
+
ca_cert.add_extension(ef.create_extension('keyUsage', 'keyCertSign, cRLSign', true))
|
88
|
+
ca_cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
|
89
|
+
ca_cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always', false))
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_endentity_extensions(entity_cert, ca_cert)
|
93
|
+
ef = get_extensions_factory entity_cert, ca_cert
|
94
|
+
entity_cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature', true))
|
95
|
+
entity_cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
|
96
|
+
end
|
97
|
+
|
98
|
+
def generate_cert_and_key(subject, cert_config, selfsigned = false, ca_cert = nil, ca_key = nil)
|
99
|
+
key = OpenSSL::PKey::RSA.generate(cert_config['rsa_keysize'])
|
100
|
+
cert = OpenSSL::X509::Certificate.new
|
101
|
+
cert.version = 2
|
102
|
+
cert.serial = Time.now.to_i
|
103
|
+
cert.public_key = key.public_key
|
104
|
+
|
105
|
+
cert.not_after = Time.now + (cert_config['years_to_expire'] * 365 * 24 * 60 * 60)
|
106
|
+
cert.not_before = Time.now - (24 * 60 * 60)
|
107
|
+
|
108
|
+
if selfsigned
|
109
|
+
cert.subject = subject
|
110
|
+
cert.issuer = subject
|
111
|
+
add_ca_extensions(cert)
|
112
|
+
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
113
|
+
else
|
114
|
+
if ca_cert.nil? || ca_key.nil?
|
115
|
+
fail_with "CA key or cert isn't specified"
|
116
|
+
end
|
117
|
+
cert.subject = subject
|
118
|
+
cert.issuer = ca_cert.subject
|
119
|
+
add_endentity_extensions(cert, ca_cert)
|
120
|
+
cert.sign(ca_key, OpenSSL::Digest::SHA1.new)
|
121
|
+
end
|
122
|
+
|
123
|
+
[cert, key]
|
124
|
+
end
|
125
|
+
|
126
|
+
def issue_crl(revoke_info, serial, lastup, nextup, extensions,
|
127
|
+
issuer, issuer_key, digest)
|
128
|
+
crl = OpenSSL::X509::CRL.new
|
129
|
+
crl.issuer = issuer.subject
|
130
|
+
crl.version = 1
|
131
|
+
crl.last_update = lastup
|
132
|
+
crl.next_update = nextup
|
133
|
+
revoke_info.each do|rserial, time, reason_code|
|
134
|
+
revoked = OpenSSL::X509::Revoked.new
|
135
|
+
revoked.serial = rserial
|
136
|
+
revoked.time = time
|
137
|
+
enum = OpenSSL::ASN1::Enumerated(reason_code)
|
138
|
+
ext = OpenSSL::X509::Extension.new('CRLReason', enum)
|
139
|
+
revoked.add_extension(ext)
|
140
|
+
crl.add_revoked(revoked)
|
141
|
+
end
|
142
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
143
|
+
ef.issuer_certificate = issuer
|
144
|
+
ef.crl = crl
|
145
|
+
crlnum = OpenSSL::ASN1::Integer(serial)
|
146
|
+
crl.add_extension(OpenSSL::X509::Extension.new('crlNumber', crlnum))
|
147
|
+
extensions.each do|oid, value, critical|
|
148
|
+
crl.add_extension(ef.create_extension(oid, value, critical))
|
149
|
+
end
|
150
|
+
crl.sign(issuer_key, digest)
|
151
|
+
crl
|
152
|
+
end
|
153
|
+
|
154
|
+
def load_cert_and_key(cert_str, key_str)
|
155
|
+
cert = OpenSSL::X509::Certificate.new cert_str
|
156
|
+
key = OpenSSL::PKey::RSA.new key_str
|
157
|
+
[cert, key]
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_databag_path(server_name)
|
161
|
+
directory_path = File.join(Dir.pwd, "data_bags/openvpn-#{server_name}")
|
162
|
+
directory_path
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_databag_name(server_name)
|
166
|
+
databag_name = "openvpn-#{server_name}"
|
167
|
+
databag_name
|
168
|
+
end
|
169
|
+
|
170
|
+
def save_databag_item(id, server_name, item_hash)
|
171
|
+
databag_path = get_databag_path server_name
|
172
|
+
item_hash['id'] = id
|
173
|
+
item_path = File.join(databag_path, "#{id}.json")
|
174
|
+
secret = get_databag_secret
|
175
|
+
encrypted_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(item_hash, secret)
|
176
|
+
unless File.exist? item_path
|
177
|
+
File.write item_path, JSON.pretty_generate(encrypted_data)
|
178
|
+
else
|
179
|
+
fail_with "#{item_path} already exists"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def load_databag_item(databag_name, item_id)
|
184
|
+
secret = get_databag_secret
|
185
|
+
# puts "Loading [#{databag_name}:#{item_id}]"
|
186
|
+
item = Chef::EncryptedDataBagItem.load(databag_name, item_id, secret)
|
187
|
+
item
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class OpenvpnServerCreate < Openvpn
|
192
|
+
banner 'knife openvpn server create NAME (options)'
|
193
|
+
deps do
|
194
|
+
require 'readline'
|
195
|
+
end
|
196
|
+
|
197
|
+
def run
|
198
|
+
check_arguments
|
199
|
+
vpn_server_name = name_args.first
|
200
|
+
check_existing_databag vpn_server_name, true
|
201
|
+
check_databag_secret
|
202
|
+
create_new_server vpn_server_name
|
203
|
+
end
|
204
|
+
|
205
|
+
def create_new_server(vpn_server_name)
|
206
|
+
now = Time.at(Time.now.to_i)
|
207
|
+
cert_config = ask_for_cert_config
|
208
|
+
ca_subject = make_name 'CA', cert_config
|
209
|
+
ca_cert, ca_key = generate_cert_and_key ca_subject, cert_config, true
|
210
|
+
server_subject = make_name vpn_server_name, cert_config
|
211
|
+
server_cert, server_key = generate_cert_and_key server_subject, cert_config, false, ca_cert, ca_key
|
212
|
+
dh_params = make_dh_params cert_config
|
213
|
+
crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA1.new)
|
214
|
+
databag_path = get_databag_path vpn_server_name
|
215
|
+
ui.info "Creating data bag directory at #{databag_path}"
|
216
|
+
create_databag_dir vpn_server_name
|
217
|
+
save_databag_item('openvpn-config', vpn_server_name, cert_config)
|
218
|
+
save_databag_item('openvpn-ca', vpn_server_name, 'cert' => ca_cert.to_pem, 'key' => ca_key.to_pem)
|
219
|
+
save_databag_item('openvpn-crl', vpn_server_name, 'crl' => crl.to_pem, 'revoke_info' => [])
|
220
|
+
|
221
|
+
save_databag_item('openvpn-server', vpn_server_name, 'cert' => server_cert.to_pem, 'key' => server_key.to_pem)
|
222
|
+
save_databag_item('openvpn-dh', vpn_server_name, 'dh' => dh_params.to_pem)
|
223
|
+
end
|
224
|
+
|
225
|
+
def check_arguments
|
226
|
+
unless name_args.size == 1
|
227
|
+
fail_with 'Specify NAME of new openvpn server!'
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def create_databag_dir(server_name)
|
232
|
+
databag_path = get_databag_path server_name
|
233
|
+
Dir.mkdir(databag_path, 0755)
|
234
|
+
databag_path
|
235
|
+
end
|
236
|
+
|
237
|
+
def read_with_prompt_and_default(prompt, default)
|
238
|
+
answer = Readline.readline("#{prompt} [#{default}]: ").strip
|
239
|
+
if answer.empty?
|
240
|
+
default
|
241
|
+
else
|
242
|
+
answer
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def make_dh_params(cert_config)
|
247
|
+
keysize = cert_config['dh_keysize']
|
248
|
+
dh_params = OpenSSL::PKey::DH.new keysize
|
249
|
+
dh_params
|
250
|
+
end
|
251
|
+
|
252
|
+
def ask_for_cert_config
|
253
|
+
cert_config = {}
|
254
|
+
strings_prompt_default = [
|
255
|
+
['C', 'Country Name', 'RU'],
|
256
|
+
['ST', 'State or Province Name', 'MSK'],
|
257
|
+
['L', 'Locality Name', 'Moscow'],
|
258
|
+
['O', 'Organization Name', 'Express 42'],
|
259
|
+
['OU', 'Organizational Unit Name', 'OPS'],
|
260
|
+
['mail', 'Email', 'ops@example.com']
|
261
|
+
]
|
262
|
+
numeric_prompt_default = [
|
263
|
+
['rsa_keysize', 'RSA key size (1024/2048/4096)', '2048'],
|
264
|
+
['dh_keysize', 'DH key size (1024/2048/4096)', '1024'],
|
265
|
+
['years_to_expire', 'Expiration (in years from now)', '5']
|
266
|
+
]
|
267
|
+
strings_prompt_default.each { |entry| cert_config[entry[0]] = read_with_prompt_and_default(entry[1], entry[2]) }
|
268
|
+
numeric_prompt_default.each { |entry| cert_config[entry[0]] = read_with_prompt_and_default(entry[1], entry[2]).to_i }
|
269
|
+
%w(rsa_keysize dh_keysize).each do |keysize|
|
270
|
+
unless [1024, 2048, 4096].include? cert_config[keysize]
|
271
|
+
fail_with "Wrong value for #{keysize}, must be one of 1024/2048/4096"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
cert_config
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class OpenvpnUserCreate < Openvpn
|
279
|
+
banner 'knife openvpn user create SERVERNAME USERNAME (options)'
|
280
|
+
|
281
|
+
def run
|
282
|
+
check_arguments
|
283
|
+
server_name = name_args[0]
|
284
|
+
user_name = name_args[1]
|
285
|
+
check_existing_databag server_name, false
|
286
|
+
check_databag_secret
|
287
|
+
create_new_user server_name, user_name
|
288
|
+
end
|
289
|
+
|
290
|
+
def create_new_user(server_name, user_name)
|
291
|
+
databag_name = get_databag_name server_name
|
292
|
+
ca_item = load_databag_item(databag_name, 'openvpn-ca')
|
293
|
+
ca_cert, ca_key = load_cert_and_key ca_item['cert'], ca_item['key']
|
294
|
+
config_item = load_databag_item(databag_name, 'openvpn-config')
|
295
|
+
cert_config = config_item.to_hash
|
296
|
+
user_subject = make_name user_name, cert_config
|
297
|
+
user_cert, user_key = generate_cert_and_key user_subject, cert_config, false, ca_cert, ca_key
|
298
|
+
save_databag_item(user_name, server_name, 'cert' => user_cert.to_pem, 'key' => user_key.to_pem)
|
299
|
+
ui.info "Done, now you can upload #{databag_name}/#{user_name}.json"
|
300
|
+
end
|
301
|
+
|
302
|
+
def check_arguments
|
303
|
+
unless name_args.size == 2
|
304
|
+
fail_with 'Specify SERVERNAME and USERNAME for new openvpn user!'
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
class OpenvpnUserExport < Openvpn
|
310
|
+
banner 'knife openvpn user export SERVERNAME USERNAME (options)'
|
311
|
+
|
312
|
+
deps do
|
313
|
+
require 'chef/search/query'
|
314
|
+
end
|
315
|
+
|
316
|
+
def run
|
317
|
+
check_arguments
|
318
|
+
server_name = name_args[0]
|
319
|
+
user_name = name_args[1]
|
320
|
+
check_existing_databag server_name, false
|
321
|
+
check_databag_secret
|
322
|
+
export_user server_name, user_name
|
323
|
+
end
|
324
|
+
|
325
|
+
def export_user(server_name, user_name)
|
326
|
+
databag_name = get_databag_name server_name
|
327
|
+
ca_item = load_databag_item(databag_name, 'openvpn-ca')
|
328
|
+
ca_cert, ca_key = load_cert_and_key ca_item['cert'], ca_item['key']
|
329
|
+
|
330
|
+
user_item = load_databag_item(databag_name, user_name)
|
331
|
+
user_cert, user_key = load_cert_and_key user_item['cert'], user_item['key']
|
332
|
+
tmpdir = Dir.mktmpdir
|
333
|
+
ui.info "tmpdir: #{tmpdir}"
|
334
|
+
begin
|
335
|
+
user_dir = "#{tmpdir}/#{user_name}-vpn"
|
336
|
+
Dir.mkdir user_dir
|
337
|
+
ui.info "userdir: #{user_dir}"
|
338
|
+
export_file "#{user_dir}/ca.crt", ca_cert.to_pem
|
339
|
+
export_file "#{user_dir}/#{user_name}.crt", user_cert.to_pem
|
340
|
+
export_file "#{user_dir}/#{user_name}.key", user_key.to_pem
|
341
|
+
config_content = generate_client_config server_name, user_name
|
342
|
+
export_file "#{user_dir}/#{user_name}.ovpn", config_content
|
343
|
+
exitcode = system("cd #{tmpdir} && tar cfz /tmp/#{user_name}-vpn.tar.gz *")
|
344
|
+
if exitcode
|
345
|
+
ui.info "Done, archive at /tmp/#{user_name}-vpn.tar.gz"
|
346
|
+
else
|
347
|
+
ui.error "Something went wrong, cant create archive at /tmp/#{user_name}-vpn.tar.gz"
|
348
|
+
end
|
349
|
+
ensure
|
350
|
+
FileUtils.rm_rf(tmpdir)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def export_file(file_path, content)
|
355
|
+
File.write file_path, content
|
356
|
+
FileUtils.chmod 'u=wr,go-wr', file_path
|
357
|
+
end
|
358
|
+
|
359
|
+
def generate_client_config(server_name, user_name)
|
360
|
+
query = "openvpn_server_name:#{server_name}"
|
361
|
+
query_nodes = Chef::Search::Query.new
|
362
|
+
search_result = query_nodes.search('node', query)[0]
|
363
|
+
unless search_result.length == 1
|
364
|
+
fail_with "Found #{search_result.length} vpn servers for #{server_name}"
|
365
|
+
end
|
366
|
+
config_content = ''
|
367
|
+
newline = "\n"
|
368
|
+
node = search_result[0]
|
369
|
+
config_content << 'client' << newline
|
370
|
+
config_content << "dev #{node['openvpn'][server_name]['dev']}" << newline
|
371
|
+
config_content << "proto #{node['openvpn'][server_name]['proto']}" << newline
|
372
|
+
config_content << "remote #{node['openvpn'][server_name]['remote_host']} "
|
373
|
+
config_content << "#{node['openvpn'][server_name]['port']}" << newline
|
374
|
+
config_content << "verb #{node['openvpn'][server_name]['verb']}" << newline
|
375
|
+
config_content << 'comp-lzo' << newline
|
376
|
+
config_content << 'ca ca.crt' << newline
|
377
|
+
config_content << "cert #{user_name}.crt" << newline
|
378
|
+
config_content << "key #{user_name}.key" << newline
|
379
|
+
config_content << 'nobind' << newline
|
380
|
+
config_content << 'persist-key' << newline
|
381
|
+
config_content << 'persist-tun' << newline
|
382
|
+
config_content
|
383
|
+
end
|
384
|
+
|
385
|
+
def check_arguments
|
386
|
+
unless name_args.size == 2
|
387
|
+
fail_with 'Specify SERVERNAME and USERNAME for new openvpn user!'
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
class OpenvpnUserRevoke < Openvpn
|
393
|
+
banner 'knife openvpn user revoke SERVERNAME USERNAME'
|
394
|
+
|
395
|
+
deps do
|
396
|
+
require 'chef/search/query'
|
397
|
+
end
|
398
|
+
|
399
|
+
def run
|
400
|
+
check_arguments
|
401
|
+
server_name = name_args[0]
|
402
|
+
user_name = name_args[1]
|
403
|
+
check_existing_databag server_name, false
|
404
|
+
check_databag_secret
|
405
|
+
revoke_user server_name, user_name
|
406
|
+
end
|
407
|
+
|
408
|
+
def revoke_user(server_name, user_name)
|
409
|
+
now = Time.at(Time.now.to_i)
|
410
|
+
databag_name = get_databag_name server_name
|
411
|
+
ca_item = load_databag_item(databag_name, 'openvpn-ca')
|
412
|
+
ca_cert, ca_key = load_cert_and_key ca_item['cert'], ca_item['key']
|
413
|
+
begin
|
414
|
+
crl_item = load_databag_item(databag_name, 'openvpn-crl')
|
415
|
+
old_crl = OpenSSL::X509::CRL.new crl_item['crl']
|
416
|
+
revoke_info = crl_item['revoke_info']
|
417
|
+
rescue
|
418
|
+
old_crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA1.new)
|
419
|
+
revoke_info = []
|
420
|
+
end
|
421
|
+
user_item = load_databag_item(databag_name, user_name)
|
422
|
+
user_cert, user_key = load_cert_and_key user_item['cert'], user_item['key']
|
423
|
+
user_revoke_info = [[user_cert.serial, now, 0]]
|
424
|
+
new_revoke_info = revoke_info + user_revoke_info
|
425
|
+
new_crl = add_user_to_crl ca_cert, ca_key, old_crl, new_revoke_info
|
426
|
+
save_databag_item('openvpn-crl', server_name, 'crl' => new_crl.to_pem, 'revoke_info' => new_revoke_info)
|
427
|
+
ui.info "revoked #{user_name}, do not forget to upload CRL databag item"
|
428
|
+
end
|
429
|
+
|
430
|
+
def add_user_to_crl(ca_cert, ca_key, old_crl, revoke_info)
|
431
|
+
new_crl = issue_crl(revoke_info, old_crl.version + 1, Time.at(Time.now.to_i), Time.at(Time.now.to_i) + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA1.new)
|
432
|
+
new_crl
|
433
|
+
end
|
434
|
+
|
435
|
+
def check_arguments
|
436
|
+
unless name_args.size == 2
|
437
|
+
fail_with 'Specify SERVERNAME and USERNAME for existing openvpn user!'
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knife-openvpn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- LLC Express 42
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-13 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A knife plugin for Express 42 openvpn cookbook
|
14
|
+
email: cookbooks@express42.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- knife-openvpn.gemspec
|
21
|
+
- lib/chef/knife/openvpn.rb
|
22
|
+
homepage: https://github.com/express42/knife-openvpn
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata: {}
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
requirements: []
|
41
|
+
rubyforge_project:
|
42
|
+
rubygems_version: 2.4.1
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: A knife plugin for Express 42 openvpn cookbook
|
46
|
+
test_files: []
|
47
|
+
has_rdoc:
|