knife-openvpn 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dd1f7ab9cad0728ed9c378d00f4572308435bad
4
- data.tar.gz: 7426c914244fad2bfd8a9ae0d11b858369d20586
3
+ metadata.gz: 88392df23f3aa58d807022146707b08fead539ea
4
+ data.tar.gz: 18252c75306991cb22f8101ae2071c0503632e7f
5
5
  SHA512:
6
- metadata.gz: 95308e27b0a782af5a32f28687b6d3c53bd1a05778b44bec4177ae14e780b898b70caaae04cea30af14f075cb214b136cf904be272c7249bdf11cac8e221f36f
7
- data.tar.gz: 74f43c6fead9c46d76edb409ba435e4aece085be7841f1ec6fd6f5892cc43f05a4326b0323c1f5ebdd57dd3cc8cbca58bb7a3acf0ce3194584a5fcbe8f8d0cee
6
+ metadata.gz: d9e1e4dff1b795dafd80e1b5ffad74ccc412e819d9fe28c693314fc86a20b00ae041e000de7aa6d726754b898f16ac0a5b3591bfb948b7448fc0a2471ea57fd5
7
+ data.tar.gz: 724635b4ad05598b6ce0f96cfa8388eb51737f6bca1345a83868a09d623a3d3845dcb7c5dd1aca929c34505f0710e3b6e6bf8b6f8dd91f1350b5843f17b0ca17
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *~
2
+ *#
3
+ .#*
4
+ \#*#
5
+ .*.sw[a-z]
6
+ *.un~
7
+
8
+ # Bundler
9
+ Gemfile.lock
10
+ bin/*
11
+ .bundle/*
12
+
13
+ # Gem
14
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Metrics/AbcSize:
2
+ Enabled: false
3
+ Style/GuardClause:
4
+ Enabled: false
5
+ Style/Documentation:
6
+ Enabled: false
7
+ Metrics/LineLength:
8
+ Enabled: false
9
+ Metrics/MethodLength:
10
+ Enabled: false
11
+ Metrics/ClassLength:
12
+ Max: 150
13
+ Metrics/ParameterLists:
14
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ script:
5
+ - bundle exec rubocop
6
+ notifications:
7
+ flowdock: d4fbb65de6c9c868a2dfc4b38fbd34c1
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :lint do
4
+ gem 'rubocop'
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Express 42
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,2 +1,36 @@
1
+ [![Build Status](https://travis-ci.org/express42/knife-openvpn.svg)](https://travis-ci.org/express42/knife-openvpn)
2
+
1
3
  # knife-openvpn
2
- A knife plugin for Express 42 openvpn cookbook
4
+ ## Description
5
+ A knife plugin for [Express 42 OpenVPN cookbook].
6
+
7
+ ## Installation
8
+ This plugin is distributed as a Ruby Gem. To install it, run:
9
+
10
+ `gem install knife-openvpn`
11
+
12
+ ## Basic Examples
13
+
14
+ Create server ca, server cert, server key and dh params:
15
+
16
+ `knife openvpn server create office`
17
+
18
+ Add openvpn client:
19
+
20
+ `knife openvpn user create office john`
21
+
22
+ Export client data (.ovpn config, server ca, client cert and key):
23
+
24
+ `knife openvpn user export office john`
25
+
26
+ Revoke access:
27
+
28
+ `knife openvpn user revoke office john`
29
+
30
+ ## License and Maintainer
31
+
32
+ Maintainer:: LLC Express 42 (<cookbooks@express42.com>)
33
+
34
+ License:: MIT
35
+
36
+ [Express 42 OpenVPN cookbook]: https://github.com/express42-cookbooks/openvpn
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = 'knife-openvpn'
6
- gem.version = '0.0.1'
6
+ gem.version = '0.0.2'
7
7
  gem.summary = 'A knife plugin for Express 42 openvpn cookbook'
8
8
  gem.description = gem.summary
9
9
  gem.authors = ['LLC Express 42']
@@ -37,7 +37,7 @@ module OpenvpnPlugin
37
37
  end
38
38
 
39
39
  def check_databag_secret
40
- databag_secret_file = File.join(Dir.pwd, '.chef/encrypted_data_bag_secret')
40
+ databag_secret_file = File.join(Dir.pwd, config[:databag_secret_file]) || Chef::Config[:knife][:secret_file]
41
41
  unless File.exist? databag_secret_file
42
42
  fail_with "Can't find encrypted databag secret file at #{databag_secret_file}."
43
43
  end
@@ -68,8 +68,8 @@ module OpenvpnPlugin
68
68
  name
69
69
  end
70
70
 
71
- def get_databag_secret
72
- databag_secret_file = File.join(Dir.pwd, '.chef/encrypted_data_bag_secret')
71
+ def load_databag_secret
72
+ databag_secret_file = File.join(Dir.pwd, config[:databag_secret_file]) || Chef::Config[:knife][:secret_file]
73
73
  secret = Chef::EncryptedDataBagItem.load_secret(databag_secret_file)
74
74
  secret
75
75
  end
@@ -89,13 +89,14 @@ module OpenvpnPlugin
89
89
  ca_cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always', false))
90
90
  end
91
91
 
92
- def add_endentity_extensions(entity_cert, ca_cert)
92
+ def add_endentity_extensions(entity_cert, ca_cert, is_user = false)
93
93
  ef = get_extensions_factory entity_cert, ca_cert
94
94
  entity_cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature', true))
95
95
  entity_cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
96
+ entity_cert.add_extension(ef.create_extension('nsCertType', 'server')) unless is_user
96
97
  end
97
98
 
98
- def generate_cert_and_key(subject, cert_config, selfsigned = false, ca_cert = nil, ca_key = nil)
99
+ def generate_cert_and_key(subject, cert_config, selfsigned = false, ca_cert = nil, ca_key = nil, is_user = false)
99
100
  key = OpenSSL::PKey::RSA.generate(cert_config['rsa_keysize'])
100
101
  cert = OpenSSL::X509::Certificate.new
101
102
  cert.version = 2
@@ -109,15 +110,24 @@ module OpenvpnPlugin
109
110
  cert.subject = subject
110
111
  cert.issuer = subject
111
112
  add_ca_extensions(cert)
112
- cert.sign(key, OpenSSL::Digest::SHA1.new)
113
+ cert.sign(key, OpenSSL::Digest::SHA256.new)
113
114
  else
114
115
  if ca_cert.nil? || ca_key.nil?
115
116
  fail_with "CA key or cert isn't specified"
116
117
  end
117
118
  cert.subject = subject
118
119
  cert.issuer = ca_cert.subject
119
- add_endentity_extensions(cert, ca_cert)
120
- cert.sign(ca_key, OpenSSL::Digest::SHA1.new)
120
+ add_endentity_extensions(cert, ca_cert, is_user)
121
+ cert.sign(ca_key, OpenSSL::Digest::SHA256.new)
122
+ end
123
+
124
+ if is_user
125
+ require 'highline/import'
126
+ passphrase = ask('Enter a passphrase [blank for passphraseless]: ') { |q| q.echo = false }
127
+ unless passphrase == ''
128
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
129
+ key = key.export(cipher, passphrase)
130
+ end
121
131
  end
122
132
 
123
133
  [cert, key]
@@ -171,18 +181,17 @@ module OpenvpnPlugin
171
181
  databag_path = get_databag_path server_name
172
182
  item_hash['id'] = id
173
183
  item_path = File.join(databag_path, "#{id}.json")
174
- secret = get_databag_secret
184
+ secret = load_databag_secret
175
185
  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
186
+ if File.exist? item_path
179
187
  fail_with "#{item_path} already exists"
188
+ else
189
+ File.write item_path, JSON.pretty_generate(encrypted_data)
180
190
  end
181
191
  end
182
192
 
183
193
  def load_databag_item(databag_name, item_id)
184
- secret = get_databag_secret
185
- # puts "Loading [#{databag_name}:#{item_id}]"
194
+ secret = load_databag_secret
186
195
  item = Chef::EncryptedDataBagItem.load(databag_name, item_id, secret)
187
196
  item
188
197
  end
@@ -190,10 +199,16 @@ module OpenvpnPlugin
190
199
 
191
200
  class OpenvpnServerCreate < Openvpn
192
201
  banner 'knife openvpn server create NAME (options)'
202
+
193
203
  deps do
194
204
  require 'readline'
195
205
  end
196
206
 
207
+ option :databag_secret_file,
208
+ long: '--secret-file PATH',
209
+ description: 'Specifies path to encrypred data bag secret file.',
210
+ default: '.chef/encrypted_data_bag_secret'
211
+
197
212
  def run
198
213
  check_arguments
199
214
  vpn_server_name = name_args.first
@@ -210,7 +225,7 @@ module OpenvpnPlugin
210
225
  server_subject = make_name vpn_server_name, cert_config
211
226
  server_cert, server_key = generate_cert_and_key server_subject, cert_config, false, ca_cert, ca_key
212
227
  dh_params = make_dh_params cert_config
213
- crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA1.new)
228
+ crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA256.new)
214
229
  databag_path = get_databag_path vpn_server_name
215
230
  ui.info "Creating data bag directory at #{databag_path}"
216
231
  create_databag_dir vpn_server_name
@@ -223,9 +238,7 @@ module OpenvpnPlugin
223
238
  end
224
239
 
225
240
  def check_arguments
226
- unless name_args.size == 1
227
- fail_with 'Specify NAME of new openvpn server!'
228
- end
241
+ fail_with 'Specify NAME of new openvpn server!' unless name_args.size == 1
229
242
  end
230
243
 
231
244
  def create_databag_dir(server_name)
@@ -278,6 +291,11 @@ module OpenvpnPlugin
278
291
  class OpenvpnUserCreate < Openvpn
279
292
  banner 'knife openvpn user create SERVERNAME USERNAME (options)'
280
293
 
294
+ option :databag_secret_file,
295
+ long: '--secret-file PATH',
296
+ description: 'Specifies path to encrypred data bag secret file.',
297
+ default: '.chef/encrypted_data_bag_secret'
298
+
281
299
  def run
282
300
  check_arguments
283
301
  server_name = name_args[0]
@@ -294,8 +312,8 @@ module OpenvpnPlugin
294
312
  config_item = load_databag_item(databag_name, 'openvpn-config')
295
313
  cert_config = config_item.to_hash
296
314
  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)
315
+ user_cert, user_key = generate_cert_and_key user_subject, cert_config, false, ca_cert, ca_key, true
316
+ save_databag_item(user_name, server_name, 'cert' => user_cert.to_pem, 'key' => user_key.to_s)
299
317
  ui.info "Done, now you can upload #{databag_name}/#{user_name}.json"
300
318
  end
301
319
 
@@ -313,6 +331,11 @@ module OpenvpnPlugin
313
331
  require 'chef/search/query'
314
332
  end
315
333
 
334
+ option :databag_secret_file,
335
+ long: '--secret-file PATH',
336
+ description: 'Specifies path to encrypred data bag secret file.',
337
+ default: '.chef/encrypted_data_bag_secret'
338
+
316
339
  def run
317
340
  check_arguments
318
341
  server_name = name_args[0]
@@ -325,19 +348,28 @@ module OpenvpnPlugin
325
348
  def export_user(server_name, user_name)
326
349
  databag_name = get_databag_name server_name
327
350
  ca_item = load_databag_item(databag_name, 'openvpn-ca')
328
- ca_cert, ca_key = load_cert_and_key ca_item['cert'], ca_item['key']
351
+ ca_cert, _ca_key = load_cert_and_key ca_item['cert'], ca_item['key']
352
+
353
+ ta_key = ''
354
+ begin
355
+ ta_item = load_databag_item(databag_name, 'openvpn-ta')
356
+ ta_key = ta_item['ta']
357
+ rescue Net::HTTPServerException
358
+ ui.warn 'Unable to load openvpn-ta, proceding without it. (Ignore unless you use tls-auth)'
359
+ end
329
360
 
330
361
  user_item = load_databag_item(databag_name, user_name)
331
- user_cert, user_key = load_cert_and_key user_item['cert'], user_item['key']
362
+ user_cert, _user_key = load_cert_and_key user_item['cert'], user_item['key']
332
363
  tmpdir = Dir.mktmpdir
333
- ui.info "tmpdir: #{tmpdir}"
364
+ ui.debug "created tmpdir: #{tmpdir}"
334
365
  begin
335
366
  user_dir = "#{tmpdir}/#{user_name}-vpn"
336
367
  Dir.mkdir user_dir
337
- ui.info "userdir: #{user_dir}"
368
+ ui.debug "created userdir: #{user_dir}"
338
369
  export_file "#{user_dir}/ca.crt", ca_cert.to_pem
339
370
  export_file "#{user_dir}/#{user_name}.crt", user_cert.to_pem
340
- export_file "#{user_dir}/#{user_name}.key", user_key.to_pem
371
+ export_file "#{user_dir}/#{user_name}.key", user_item['key'].to_s
372
+ export_file "#{user_dir}/ta.key", ta_key unless ta_key.empty?
341
373
  config_content = generate_client_config server_name, user_name
342
374
  export_file "#{user_dir}/#{user_name}.ovpn", config_content
343
375
  exitcode = system("cd #{tmpdir} && tar cfz /tmp/#{user_name}-vpn.tar.gz *")
@@ -360,22 +392,27 @@ module OpenvpnPlugin
360
392
  query = "openvpn_server_name:#{server_name}"
361
393
  query_nodes = Chef::Search::Query.new
362
394
  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}"
395
+ unless search_result.length < 1
396
+ fail_with "Cant find vpn server named '#{server_name}'"
365
397
  end
366
398
  config_content = ''
367
399
  newline = "\n"
368
400
  node = search_result[0]
401
+ config = Chef::Mixin::DeepMerge.merge(node['openvpn']['default'].to_hash, node['openvpn'][server_name].to_hash)
369
402
  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
403
+ config_content << "dev #{config['dev']}" << newline
404
+ config_content << "proto #{config['proto']}" << newline
405
+ search_result.each do |result|
406
+ config_content << "remote #{result['openvpn'][server_name]['remote_host']} "
407
+ config_content << "#{config['port']}" << newline
408
+ end
409
+ config_content << "verb #{config['verb']}" << newline
375
410
  config_content << 'comp-lzo' << newline
376
411
  config_content << 'ca ca.crt' << newline
377
412
  config_content << "cert #{user_name}.crt" << newline
378
413
  config_content << "key #{user_name}.key" << newline
414
+ config_content << 'tls-auth ta.key 1' << newline if config['use_tls_auth']
415
+ config_content << 'ns-cert-type server' << newline
379
416
  config_content << 'nobind' << newline
380
417
  config_content << 'persist-key' << newline
381
418
  config_content << 'persist-tun' << newline
@@ -396,6 +433,11 @@ module OpenvpnPlugin
396
433
  require 'chef/search/query'
397
434
  end
398
435
 
436
+ option :databag_secret_file,
437
+ long: '--secret-file PATH',
438
+ description: 'Specifies path to encrypred data bag secret file.',
439
+ default: '.chef/encrypted_data_bag_secret'
440
+
399
441
  def run
400
442
  check_arguments
401
443
  server_name = name_args[0]
@@ -415,11 +457,11 @@ module OpenvpnPlugin
415
457
  old_crl = OpenSSL::X509::CRL.new crl_item['crl']
416
458
  revoke_info = crl_item['revoke_info']
417
459
  rescue
418
- old_crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA1.new)
460
+ old_crl = issue_crl([], 1, now, now + 3600, [], ca_cert, ca_key, OpenSSL::Digest::SHA256.new)
419
461
  revoke_info = []
420
462
  end
421
463
  user_item = load_databag_item(databag_name, user_name)
422
- user_cert, user_key = load_cert_and_key user_item['cert'], user_item['key']
464
+ user_cert, _user_key = load_cert_and_key user_item['cert'], user_item['key']
423
465
  user_revoke_info = [[user_cert.serial, now, 0]]
424
466
  new_revoke_info = revoke_info + user_revoke_info
425
467
  new_crl = add_user_to_crl ca_cert, ca_key, old_crl, new_revoke_info
@@ -428,7 +470,7 @@ module OpenvpnPlugin
428
470
  end
429
471
 
430
472
  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)
473
+ 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::SHA256.new)
432
474
  new_crl
433
475
  end
434
476
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-openvpn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - LLC Express 42
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-13 00:00:00.000000000 Z
11
+ date: 2015-09-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A knife plugin for Express 42 openvpn cookbook
14
14
  email: cookbooks@express42.com
@@ -16,6 +16,11 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
+ - ".gitignore"
20
+ - ".rubocop.yml"
21
+ - ".travis.yml"
22
+ - Gemfile
23
+ - LICENSE
19
24
  - README.md
20
25
  - knife-openvpn.gemspec
21
26
  - lib/chef/knife/openvpn.rb
@@ -39,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
44
  version: '0'
40
45
  requirements: []
41
46
  rubyforge_project:
42
- rubygems_version: 2.4.1
47
+ rubygems_version: 2.4.4
43
48
  signing_key:
44
49
  specification_version: 4
45
50
  summary: A knife plugin for Express 42 openvpn cookbook