ovpn-key 0.7.4 → 0.8.2
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/NOTICE +1 -1
- data/README.md +9 -8
- data/bin/ovpn-key +106 -55
- data/defaults/ovpn-key.yml +13 -4
- data/lib/functions.rb +149 -27
- data/lib/version.rb +3 -1
- metadata +10 -15
- data/defaults/meta/index.txt +0 -0
- data/defaults/meta/index.txt.attr +0 -1
- data/defaults/meta/serial +0 -1
- data/defaults/openssl.ini +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af60a802bc319338bb17b74b6add7c6cd78d7bdc7a96021ad105a0928a59d491
|
4
|
+
data.tar.gz: 3b9aef64a304187f18a963ebf703a360f5790c9705af15864372796abdb353c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf1d15c25d12102fa3ac5429e40fae565e6ebeb2bc4fe32edea884eca9e9ffbe58bb5a15ce979a7d5403ef0ce67ceca28e38d7da5ea54f0890d1938e4ef6015a
|
7
|
+
data.tar.gz: 5855147ac84b0a8dc29b02b65d730a39caa46ad755ed69f92b845770d115478956045b99f40159abdb221ec2e5799abca1abd75c698a236612cfe4c10cf78421
|
data/NOTICE
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
This utility is designed as [easy-rsa](https://github.com/OpenVPN/easy-rsa) replacement suitable for one exact use case.
|
4
4
|
|
5
|
-
It's basically a wrapper around
|
5
|
+
It's basically a wrapper around OpenSSL API to:
|
6
6
|
* create a self-signed CA
|
7
7
|
* create client and server certificates and pack them to ZIP files along with the OpenVPN config
|
8
8
|
* revoke the certificates
|
@@ -28,14 +28,15 @@ If you're brave, [let me know](https://github.com/chillum/ovpn-key/issues), wher
|
|
28
28
|
### Usage
|
29
29
|
|
30
30
|
1. `ovpn-key --init`
|
31
|
-
2. edit `ovpn-key.yml`
|
32
|
-
3. `ovpn-key --ca --dh
|
33
|
-
4. `ovpn-key --
|
34
|
-
5. `ovpn-key --
|
35
|
-
6. `ovpn-key --
|
36
|
-
7.
|
31
|
+
2. edit `ovpn-key.yml`
|
32
|
+
3. `ovpn-key --ca --dh`
|
33
|
+
4. `ovpn-key --server --nopass`
|
34
|
+
5. `ovpn-key --client somebody [--nopass]`
|
35
|
+
6. `ovpn-key --revoke somebody`
|
36
|
+
7. `ovpn-key --static` (generates `ta.key`)
|
37
|
+
8. add a file with `.ovpn` extension to the directory
|
37
38
|
it should contain every setting except for `cert` and `key`
|
38
|
-
|
39
|
+
9. `ovpn-key --zip somebody-else [--nopass]`
|
39
40
|
|
40
41
|
### Configuration
|
41
42
|
|
data/bin/ovpn-key
CHANGED
@@ -1,80 +1,84 @@
|
|
1
|
-
#! /usr/bin/env ruby
|
2
|
-
|
1
|
+
#! /usr/bin/env ruby -w
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require 'fileutils'
|
5
|
+
require 'io/console'
|
6
|
+
require 'openssl'
|
7
|
+
require 'optparse'
|
4
8
|
require 'yaml'
|
5
9
|
require 'zip'
|
6
|
-
require_relative '../lib/version
|
7
|
-
require_relative '../lib/functions
|
10
|
+
require_relative '../lib/version'
|
11
|
+
require_relative '../lib/functions'
|
8
12
|
|
9
|
-
SSL_CONF = 'openssl.ini'
|
10
13
|
APP_CONF = 'ovpn-key.yml'
|
14
|
+
CRL_FILE = 'crl.pem'
|
15
|
+
SERIAL_FILE = 'serial'
|
11
16
|
|
12
17
|
options = {}
|
18
|
+
# rubocop:disable Metrics/BlockLength
|
13
19
|
OptionParser.new do |opts|
|
14
|
-
|
15
|
-
opts.
|
16
|
-
|
20
|
+
# rubocop:enable Metrics/BlockLength
|
21
|
+
opts.banner = "Usage: #{File.basename $PROGRAM_NAME} <options> [--nopass]"
|
22
|
+
opts.on('--init [directory]', 'Init a CA directory (defaults to current)') do |v|
|
23
|
+
options[:init] = v || '.'
|
17
24
|
end
|
18
|
-
opts.on(
|
25
|
+
opts.on('--ca', 'Generate a CA (ca.crt)') do |v|
|
19
26
|
check_crt('ca')
|
20
27
|
options[:generate_ca] = v
|
21
28
|
end
|
22
|
-
opts.on(
|
29
|
+
opts.on('--dh', 'Generate a DH keyfile (dh.pem)') do |v|
|
23
30
|
# it's safe to overwrite this file
|
24
31
|
options[:generate_dh] = v
|
25
32
|
end
|
26
|
-
opts.on(
|
27
|
-
options[:generate_static] = v
|
28
|
-
check_crt(
|
33
|
+
opts.on('--static', 'Generate OpenVPN static key (ta.key)') do |v|
|
34
|
+
options[:generate_static] = v
|
35
|
+
check_crt('ta')
|
29
36
|
end
|
30
|
-
opts.on(
|
31
|
-
options[:generate_server] = v
|
37
|
+
opts.on('--server [name]', "Generate a server key (defaults to 'server')") do |v|
|
38
|
+
options[:generate_server] = v || 'server'
|
32
39
|
check_crt(options[:generate_server])
|
33
40
|
end
|
34
|
-
opts.on(
|
41
|
+
opts.on('--client [name]', 'Generate a client key and sign it') do |v|
|
35
42
|
check_client(v)
|
36
43
|
options[:generate_client] = v
|
37
44
|
end
|
38
|
-
opts.on(
|
45
|
+
opts.on('--zip [name]', 'Ditto plus pack it to ZIP with OpenVPN config') do |v|
|
39
46
|
check_client(v)
|
40
47
|
options[:generate_zip] = v
|
41
48
|
end
|
42
|
-
opts.on(
|
43
|
-
abort
|
49
|
+
opts.on('--revoke [name]', "Revoke a certificate (using #{CRL_FILE}) and delete it") do |v|
|
50
|
+
abort 'Please specify what certificate to revoke' unless v
|
44
51
|
options[:revoke] = v
|
45
52
|
end
|
46
|
-
opts.on(
|
53
|
+
opts.on('--nopass', "Don't protect .key files with a password") do |v|
|
47
54
|
options[:no_password] = v
|
48
55
|
end
|
49
56
|
end.parse!
|
50
|
-
if ARGV.length
|
51
|
-
abort "Error: invalid args: #{ARGV.join ' '}\nSee `#{File.basename $
|
57
|
+
if ARGV.length.positive?
|
58
|
+
abort "Error: invalid args: #{ARGV.join ' '}\nSee `#{File.basename $PROGRAM_NAME} -h` for help"
|
52
59
|
end
|
53
60
|
unless options[:init] || options[:generate_ca] || options[:generate_dh] || options[:generate_static] \
|
54
61
|
|| options[:generate_server] || options[:generate_client] || options[:generate_zip] || options[:revoke]
|
55
|
-
abort "See `#{File.basename $
|
62
|
+
abort "See `#{File.basename $PROGRAM_NAME} -h` for usage"
|
56
63
|
end
|
57
|
-
if options[:generate_client]
|
64
|
+
if options[:generate_client] && options[:generate_zip]
|
58
65
|
# I assume that user likely wants one of them and is confused with usage
|
59
|
-
abort
|
66
|
+
abort 'There can be only one: --client or --zip'
|
60
67
|
end
|
61
|
-
umask = File.umask
|
68
|
+
umask = File.umask 0o077
|
62
69
|
|
63
70
|
if options[:init]
|
64
71
|
unless options[:init] == '.'
|
65
72
|
create_dir options[:init]
|
66
73
|
Dir.chdir options[:init]
|
67
74
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
puts "Created file: #{file}"
|
73
|
-
end
|
74
|
-
}
|
75
|
+
unless File.exist? APP_CONF
|
76
|
+
FileUtils.copy_file(File.expand_path("defaults/#{APP_CONF}", "#{__dir__}/.."), "./#{APP_CONF}")
|
77
|
+
puts "Created file: #{APP_CONF}"
|
78
|
+
end
|
75
79
|
elsif !File.exist? APP_CONF
|
76
80
|
begin
|
77
|
-
rc = YAML.load_file(File.expand_path
|
81
|
+
rc = YAML.load_file(File.expand_path("~/.#{APP_CONF}"))
|
78
82
|
rescue Errno::ENOENT
|
79
83
|
# no configuration file in home directory is not an error
|
80
84
|
end
|
@@ -84,33 +88,82 @@ end
|
|
84
88
|
begin
|
85
89
|
settings = YAML.load_file(APP_CONF)
|
86
90
|
rescue Errno::ENOENT
|
87
|
-
abort "Run `#{File.basename $
|
91
|
+
abort "Run `#{File.basename $PROGRAM_NAME} --init` before generating certificates"
|
88
92
|
end
|
89
93
|
ZIP_DIR = settings['zip_dir'] || '~'
|
90
94
|
OPENVPN = settings['openvpn'] || 'openvpn'
|
91
|
-
OPENSSL = settings['openssl'] || 'openssl'
|
92
|
-
KEY_SIZE = settings['key_size'] || 2048
|
93
95
|
ENCRYPT = settings['encrypt'] || 'aes128'
|
94
|
-
|
96
|
+
DIGEST = settings['digest'] || 'sha256'
|
97
|
+
KEY_SIZE = settings['key_size'] || 2048
|
95
98
|
CN_CA = settings['ca_name'] || 'Certification Authority'
|
96
|
-
|
99
|
+
|
100
|
+
unless settings['ca_days'].nil?
|
101
|
+
if settings['expire'].nil?
|
102
|
+
puts 'Migrating pre-0.8 configuration to new format: ca_days'
|
103
|
+
puts "WARNING: if you tweaked `default_days` or `default_days_crl` in #{SSL_CONF}, edit #{APP_CONF}"
|
104
|
+
File.open(APP_CONF, 'a') do |f|
|
105
|
+
f.write "# ca_days is not used anymore, you can remove it\nexpire:\n"
|
106
|
+
f.write " ca: #{settings['ca_days']}\n crl: 3650\n server: 3650\n client: 3650\n"
|
107
|
+
end
|
108
|
+
else
|
109
|
+
puts "WARNING: `ca_days` setting is deprecated, remove it from #{APP_CONF}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
settings['expire'] ||= {}
|
114
|
+
settings['expire']['ca'] ||= settings['ca_days'] || 3650
|
115
|
+
settings['expire']['crl'] ||= 3650
|
116
|
+
settings['expire']['server'] ||= 3650
|
117
|
+
settings['expire']['client'] ||= 3650
|
118
|
+
EXPIRE = settings['expire']
|
119
|
+
|
120
|
+
if settings['x509'].nil? && !settings['details'].nil?
|
121
|
+
puts 'Migrating pre-0.8 configuration to new format: details'
|
122
|
+
REQ = OpenSSL::X509::Name.parse(settings['details']).to_a
|
123
|
+
File.open(APP_CONF, 'a') do |f|
|
124
|
+
f.write "# details is not used anymore, you can remove it\nx509:\n"
|
125
|
+
REQ.map {|i, j| f.write " #{i}: #{j}\n" }
|
126
|
+
end
|
127
|
+
else
|
128
|
+
REQ = settings['x509']
|
129
|
+
puts "WARNING: `details` section is deprecated, remove it from #{APP_CONF}" unless settings['details'].nil?
|
130
|
+
end
|
131
|
+
if settings['openssl']
|
132
|
+
puts "WARNING: `openssl` setting is deprecated, remove it from #{APP_CONF}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if !File.exist?(SERIAL_FILE) && File.exist?('meta/serial')
|
136
|
+
FileUtils.copy_file('meta/serial', SERIAL_FILE)
|
137
|
+
puts 'Copied meta/serial to serial'
|
138
|
+
end
|
139
|
+
%w[certs meta].each {|dir|
|
140
|
+
puts "WARNING: #{dir} directory is not used anymore. you can remove it" if File.exist? dir
|
141
|
+
}
|
142
|
+
if File.exist? 'openssl.ini'
|
143
|
+
puts 'WARNING: openssl.ini file is not used anymore. you can remove it'
|
144
|
+
end
|
97
145
|
|
98
146
|
if options[:generate_ca]
|
99
|
-
|
100
|
-
|
101
|
-
|
147
|
+
ca_pass = options[:no_password] ? nil : ask_password('ca')
|
148
|
+
gen_key('ca', ca_pass)
|
149
|
+
sign_key('ca', CN_CA, ca_pass)
|
150
|
+
gen_crl(ca_pass)
|
102
151
|
end
|
103
152
|
if options[:generate_dh]
|
104
|
-
|
153
|
+
File.open('dh.pem', 'w') do |f|
|
154
|
+
print 'Generating dh.pem. This will take a while'
|
155
|
+
f.write OpenSSL::PKey::DH.new(KEY_SIZE)
|
156
|
+
puts '. Done'
|
157
|
+
end
|
105
158
|
end
|
106
159
|
if options[:generate_static]
|
107
|
-
exe "#{OPENVPN} --genkey --secret
|
160
|
+
exe "#{OPENVPN} --genkey --secret ta.key"
|
108
161
|
end
|
109
162
|
if options[:generate_server]
|
110
|
-
gen_and_sign('server', options[:generate_server], options[:no_password])
|
163
|
+
gen_and_sign('server', options[:generate_server], options[:no_password] ? nil : ask_password(options[:generate_server]))
|
111
164
|
end
|
112
165
|
if options[:generate_client]
|
113
|
-
gen_and_sign('client', options[:generate_client], options[:no_password])
|
166
|
+
gen_and_sign('client', options[:generate_client], options[:no_password] ? nil : ask_password(options[:generate_client]))
|
114
167
|
end
|
115
168
|
if options[:generate_zip]
|
116
169
|
ovpn_files = Dir['*.ovpn']
|
@@ -118,29 +171,27 @@ if options[:generate_zip]
|
|
118
171
|
when 1
|
119
172
|
ovpn_file = ovpn_files.first
|
120
173
|
when 0
|
121
|
-
abort
|
174
|
+
abort 'No .ovpn file in current directory, please add one'
|
122
175
|
else
|
123
|
-
abort
|
176
|
+
abort 'More than one .ovpn files in current directory, aborting'
|
124
177
|
end
|
125
178
|
|
126
|
-
gen_and_sign('client', options[:generate_zip], options[:no_password])
|
179
|
+
gen_and_sign('client', options[:generate_zip], options[:no_password] ? nil : ask_password(options[:generate_zip]))
|
127
180
|
|
128
181
|
zip_file = File.join(File.expand_path(ZIP_DIR), "#{File.basename ovpn_file, '.ovpn'}.tblk.zip")
|
129
182
|
File.delete(zip_file) if File.exist?(zip_file)
|
130
183
|
File.umask umask
|
131
184
|
Zip::File.open(zip_file, Zip::File::CREATE) do |zip|
|
132
185
|
zip.get_output_stream(ovpn_file) {|f|
|
133
|
-
File.
|
186
|
+
f.write File.read(ovpn_file)
|
134
187
|
f.write "cert #{options[:generate_zip]}.crt\nkey #{options[:generate_zip]}.key\n"
|
135
188
|
}
|
136
|
-
[
|
189
|
+
['ca.crt', "#{options[:generate_zip]}.crt", "#{options[:generate_zip]}.key"].each {|i|
|
137
190
|
zip.add(i, i)
|
138
191
|
}
|
139
|
-
|
192
|
+
zip.add('ta.key', 'ta.key') if File.exist? 'ta.key'
|
140
193
|
end
|
141
194
|
end
|
142
195
|
if options[:revoke]
|
143
|
-
|
144
|
-
gen_crl
|
145
|
-
['crt', 'key'].each {|ext| File.delete "#{options[:revoke]}.#{ext}"}
|
196
|
+
revoke(options[:revoke])
|
146
197
|
end
|
data/defaults/ovpn-key.yml
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
zip_dir: '~'
|
2
2
|
openvpn: openvpn
|
3
|
-
|
3
|
+
encrypt: AES128
|
4
|
+
digest: SHA256
|
4
5
|
key_size: 2048
|
5
|
-
|
6
|
-
|
6
|
+
expire: # days
|
7
|
+
ca: 3650
|
8
|
+
crl: 3650
|
9
|
+
server: 3650
|
10
|
+
client: 3650
|
7
11
|
ca_name: Certification Authority
|
8
|
-
|
12
|
+
x509:
|
13
|
+
C: US
|
14
|
+
ST: CA
|
15
|
+
L: San Francisco
|
16
|
+
O: Dva Debila
|
17
|
+
OU: OpenVPN
|
data/lib/functions.rb
CHANGED
@@ -1,48 +1,170 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def check_crt(filename)
|
4
|
+
%w[key crt].each {|ext|
|
3
5
|
abort "#{filename}.#{ext} already exists, exiting" if File.exist? "#{filename}.#{ext}"
|
4
6
|
}
|
5
7
|
end
|
6
8
|
|
7
|
-
def check_client
|
8
|
-
abort
|
9
|
+
def check_client(name)
|
10
|
+
abort 'Error: client should have an alphanumeric name' unless name
|
9
11
|
check_crt(name)
|
10
12
|
end
|
11
13
|
|
12
|
-
def exe
|
13
|
-
system(cmd)
|
14
|
+
def exe(cmd)
|
15
|
+
system(cmd) || abort("error executing: #{cmd}")
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
18
|
+
def ask_password(name)
|
19
|
+
password = ''
|
20
|
+
loop do
|
21
|
+
print "Enter password for #{name}.key: "
|
22
|
+
password = $stdin.noecho(&:gets).chomp
|
23
|
+
puts # trailing newline
|
24
|
+
break unless password.empty?
|
25
|
+
end
|
26
|
+
password
|
19
27
|
end
|
20
28
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
def unencrypt_ca_key
|
30
|
+
begin
|
31
|
+
OpenSSL::PKey::RSA.new File.read('ca.key'), ''
|
32
|
+
rescue OpenSSL::PKey::RSAError
|
33
|
+
# this means the file is encrypted
|
34
|
+
OpenSSL::PKey::RSA.new File.read('ca.key'), ask_password('ca')
|
26
35
|
end
|
36
|
+
rescue OpenSSL::PKey::RSAError
|
37
|
+
retry
|
38
|
+
end
|
39
|
+
|
40
|
+
def gen_and_sign(type, certname, password)
|
41
|
+
gen_key(certname, password)
|
42
|
+
sign_key(type, certname, password)
|
27
43
|
end
|
28
44
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
exe "#{OPENSSL} req -new -key '#{certname}.key' -out '#{certname}.csr' -config #{SSL_CONF} -subj '/CN=#{cn}#{REQ}' -extensions ext.#{type}"
|
34
|
-
exe "#{OPENSSL} ca -in '#{certname}.csr' -out '#{certname}.crt' -config #{SSL_CONF} -extensions ext.#{type} -batch"
|
35
|
-
File.delete "#{certname}.csr"
|
45
|
+
def gen_key(certname, password)
|
46
|
+
key = OpenSSL::PKey::RSA.new(KEY_SIZE)
|
47
|
+
File.open("#{certname}.key", 'w') do |f|
|
48
|
+
f.write password ? key.to_pem(OpenSSL::Cipher.new(ENCRYPT), password) : key
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
39
|
-
|
40
|
-
|
52
|
+
# type is one of: 'ca', 'server', 'client'
|
53
|
+
def sign_key(type, cn, password)
|
54
|
+
certname = type == 'ca' ? 'ca' : cn
|
55
|
+
key = OpenSSL::PKey::RSA.new File.read("#{certname}.key"), password
|
56
|
+
serial = new_serial
|
57
|
+
cert = gen_cert(type, cn, key, serial)
|
58
|
+
|
59
|
+
ca_key = type == 'ca' ? key : unencrypt_ca_key
|
60
|
+
cert.sign ca_key, OpenSSL::Digest.new(DIGEST)
|
61
|
+
|
62
|
+
File.open(SERIAL_FILE, 'w') {|f| f.write serial }
|
63
|
+
File.open("#{certname}.crt", 'w') {|f| f.write cert.to_pem }
|
64
|
+
end
|
65
|
+
|
66
|
+
def gen_cert(type, cn, key, serial)
|
67
|
+
cert = basic_cert(type, cn)
|
68
|
+
cert.public_key = key.public_key
|
69
|
+
cert.serial = serial
|
70
|
+
|
71
|
+
customize_cert(type, cert)
|
72
|
+
end
|
73
|
+
|
74
|
+
# rubocop:disable Metrics/AbcSize
|
75
|
+
def basic_cert(type, cn)
|
76
|
+
# rubocop:enable Metrics/AbcSize
|
77
|
+
subj = OpenSSL::X509::Name.new([['CN', cn]] + REQ.to_a)
|
78
|
+
cert = OpenSSL::X509::Certificate.new
|
79
|
+
|
80
|
+
cert.version = 2
|
81
|
+
cert.subject = subj
|
82
|
+
cert.issuer = OpenSSL::X509::Name.new([['CN', CN_CA]] + REQ.to_a)
|
83
|
+
cert.not_before = Time.now
|
84
|
+
cert.not_after = Time.now + EXPIRE[type] * 86_400 # days to seconds
|
85
|
+
|
86
|
+
cert
|
87
|
+
end
|
88
|
+
|
89
|
+
# rubocop:disable Metrics/MethodLength
|
90
|
+
# rubocop:disable Metrics/AbcSize
|
91
|
+
def customize_cert(type, cert)
|
92
|
+
# rubocop:enable Metrics/AbcSize
|
93
|
+
# rubocop:enable Metrics/MethodLength
|
94
|
+
|
95
|
+
ef = OpenSSL::X509::ExtensionFactory.new nil, cert
|
96
|
+
ef.issuer_certificate = cert
|
97
|
+
|
98
|
+
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
|
99
|
+
cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid,issuer:always')
|
100
|
+
cert.add_extension ef.create_extension('basicConstraints', type == 'ca' ? 'CA:true' : 'CA:false')
|
101
|
+
|
102
|
+
case type
|
103
|
+
when 'ca'
|
104
|
+
cert.add_extension ef.create_extension('keyUsage', 'cRLSign,keyCertSign')
|
105
|
+
when 'server'
|
106
|
+
cert.add_extension ef.create_extension('keyUsage', 'keyEncipherment,digitalSignature')
|
107
|
+
cert.add_extension ef.create_extension('extendedKeyUsage', 'serverAuth')
|
108
|
+
when 'client'
|
109
|
+
cert.add_extension ef.create_extension('keyUsage', 'digitalSignature')
|
110
|
+
cert.add_extension ef.create_extension('extendedKeyUsage', 'clientAuth')
|
111
|
+
end
|
112
|
+
|
113
|
+
cert
|
41
114
|
end
|
42
115
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
116
|
+
# rubocop:disable Metrics/AbcSize
|
117
|
+
# rubocop:disable Metrics/MethodLength
|
118
|
+
def revoke(certname)
|
119
|
+
# rubocop:enable Metrics/AbcSize
|
120
|
+
# rubocop:enable Metrics/MethodLength
|
121
|
+
crl = OpenSSL::X509::CRL.new(File.read(CRL_FILE))
|
122
|
+
cert = OpenSSL::X509::Certificate.new(File.read("#{certname}.crt"))
|
123
|
+
revoke = OpenSSL::X509::Revoked.new.tap {|rev|
|
124
|
+
rev.serial = cert.serial
|
125
|
+
rev.time = Time.now
|
126
|
+
}
|
127
|
+
crl.next_update = Time.now + EXPIRE['crl'] * 86_400 # days to seconds
|
128
|
+
crl.add_revoked(revoke)
|
129
|
+
begin
|
130
|
+
update_crl(crl, ask_password('ca'))
|
131
|
+
rescue OpenSSL::PKey::RSAError
|
132
|
+
retry
|
47
133
|
end
|
134
|
+
|
135
|
+
%w[crt key].each {|ext| File.delete "#{certname}.#{ext}" }
|
136
|
+
end
|
137
|
+
|
138
|
+
def gen_crl(ca_pass)
|
139
|
+
return if File.exist? CRL_FILE
|
140
|
+
|
141
|
+
crl = OpenSSL::X509::CRL.new
|
142
|
+
crl.issuer = OpenSSL::X509::Name.new([['CN', CN_CA]] + REQ.to_a)
|
143
|
+
update_crl(crl, ca_pass)
|
144
|
+
end
|
145
|
+
|
146
|
+
# rubocop:disable Metrics/AbcSize
|
147
|
+
def update_crl(crl, ca_pass)
|
148
|
+
# rubocop:enable Metrics/AbcSize
|
149
|
+
ca_key = OpenSSL::PKey::RSA.new File.read('ca.key'), ca_pass
|
150
|
+
crl.last_update = Time.now
|
151
|
+
crl.next_update = Time.now + EXPIRE['crl'] * 86_400 # days to seconds
|
152
|
+
crl.version = crl.version + 1
|
153
|
+
crl.sign(ca_key, OpenSSL::Digest.new(DIGEST))
|
154
|
+
File.open(CRL_FILE, 'w') {|f| f.write crl.to_pem }
|
155
|
+
end
|
156
|
+
|
157
|
+
def new_serial
|
158
|
+
begin
|
159
|
+
File.read(SERIAL_FILE).to_i
|
160
|
+
rescue Errno::ENOENT
|
161
|
+
0
|
162
|
+
end + 1
|
163
|
+
end
|
164
|
+
|
165
|
+
def create_dir(name)
|
166
|
+
return if Dir.exist? name
|
167
|
+
|
168
|
+
Dir.mkdir name
|
169
|
+
puts "Created directory: #{name}"
|
48
170
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ovpn-key
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vasily Korytov
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -16,17 +16,17 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
description: Generates and revokes certificates, also packs them to ZIP files with
|
28
28
|
OpenVPN configuration
|
29
|
-
email:
|
29
|
+
email: v.korytov@outlook.com
|
30
30
|
executables:
|
31
31
|
- ovpn-key
|
32
32
|
extensions: []
|
@@ -35,10 +35,6 @@ files:
|
|
35
35
|
- NOTICE
|
36
36
|
- README.md
|
37
37
|
- bin/ovpn-key
|
38
|
-
- defaults/meta/index.txt
|
39
|
-
- defaults/meta/index.txt.attr
|
40
|
-
- defaults/meta/serial
|
41
|
-
- defaults/openssl.ini
|
42
38
|
- defaults/ovpn-key.yml
|
43
39
|
- lib/functions.rb
|
44
40
|
- lib/version.rb
|
@@ -46,7 +42,7 @@ homepage: https://github.com/chillum/ovpn-key
|
|
46
42
|
licenses:
|
47
43
|
- Apache-2.0
|
48
44
|
metadata: {}
|
49
|
-
post_install_message:
|
45
|
+
post_install_message:
|
50
46
|
rdoc_options: []
|
51
47
|
require_paths:
|
52
48
|
- lib
|
@@ -54,16 +50,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
50
|
requirements:
|
55
51
|
- - ">="
|
56
52
|
- !ruby/object:Gem::Version
|
57
|
-
version: '2.
|
53
|
+
version: '2.4'
|
58
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
55
|
requirements:
|
60
56
|
- - ">="
|
61
57
|
- !ruby/object:Gem::Version
|
62
58
|
version: '0'
|
63
59
|
requirements: []
|
64
|
-
|
65
|
-
|
66
|
-
signing_key:
|
60
|
+
rubygems_version: 3.2.3
|
61
|
+
signing_key:
|
67
62
|
specification_version: 4
|
68
63
|
summary: Key management utility for OpenVPN
|
69
64
|
test_files: []
|
data/defaults/meta/index.txt
DELETED
File without changes
|
@@ -1 +0,0 @@
|
|
1
|
-
unique_subject = yes
|
data/defaults/meta/serial
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
01
|
data/defaults/openssl.ini
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
[req]
|
2
|
-
default_md = sha256
|
3
|
-
distinguished_name = dn.ovpn
|
4
|
-
|
5
|
-
[dn.ovpn]
|
6
|
-
CN = Certificate name (required)
|
7
|
-
|
8
|
-
[ca]
|
9
|
-
default_ca = ca.ovpn
|
10
|
-
|
11
|
-
[ca.ovpn]
|
12
|
-
default_md = sha256
|
13
|
-
private_key = ca.key
|
14
|
-
certificate = ca.crt
|
15
|
-
database = meta/index.txt
|
16
|
-
serial = meta/serial
|
17
|
-
crl = crl.pem
|
18
|
-
policy = policy.ovpn
|
19
|
-
# create this directory if changing this value
|
20
|
-
new_certs_dir = certs
|
21
|
-
default_days = 3650
|
22
|
-
default_crl_days = 3650
|
23
|
-
subjectKeyIdentifier = hash
|
24
|
-
authorityKeyIdentifier = keyid,issuer:always
|
25
|
-
|
26
|
-
[ext.ca]
|
27
|
-
basicConstraints = CA:true
|
28
|
-
|
29
|
-
[ext.server]
|
30
|
-
basicConstraints = CA:false
|
31
|
-
nsCertType = server
|
32
|
-
extendedKeyUsage = serverAuth
|
33
|
-
keyUsage = digitalSignature, keyEncipherment
|
34
|
-
|
35
|
-
[ext.client]
|
36
|
-
basicConstraints = CA:false
|
37
|
-
extendedKeyUsage = clientAuth
|
38
|
-
keyUsage = digitalSignature
|
39
|
-
|
40
|
-
[policy.ovpn]
|
41
|
-
commonName = supplied
|
42
|
-
countryName = optional
|
43
|
-
stateOrProvinceName = optional
|
44
|
-
localityName = optional
|
45
|
-
organizationName = optional
|
46
|
-
organizationalUnitName = optional
|
47
|
-
name = optional
|
48
|
-
emailAddress = optional
|