leap_cli 1.8.1 → 1.9
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/bin/leap +6 -12
- data/lib/leap_cli.rb +3 -23
- data/lib/leap_cli/bootstrap.rb +36 -12
- data/lib/leap_cli/commands/common.rb +88 -46
- data/lib/leap_cli/commands/new.rb +24 -17
- data/lib/leap_cli/commands/pre.rb +3 -1
- data/lib/leap_cli/core_ext/hash.rb +19 -0
- data/lib/leap_cli/leapfile.rb +47 -32
- data/lib/leap_cli/log.rb +196 -88
- data/lib/leap_cli/path.rb +5 -5
- data/lib/leap_cli/util.rb +28 -18
- data/lib/leap_cli/version.rb +8 -3
- data/vendor/acme-client/lib/acme-client.rb +1 -0
- data/vendor/acme-client/lib/acme/client.rb +122 -0
- data/vendor/acme-client/lib/acme/client/certificate.rb +30 -0
- data/vendor/acme-client/lib/acme/client/certificate_request.rb +111 -0
- data/vendor/acme-client/lib/acme/client/crypto.rb +98 -0
- data/vendor/acme-client/lib/acme/client/error.rb +16 -0
- data/vendor/acme-client/lib/acme/client/faraday_middleware.rb +123 -0
- data/vendor/acme-client/lib/acme/client/resources.rb +5 -0
- data/vendor/acme-client/lib/acme/client/resources/authorization.rb +44 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges.rb +6 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/base.rb +43 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb +19 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb +18 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb +24 -0
- data/vendor/acme-client/lib/acme/client/resources/registration.rb +37 -0
- data/vendor/acme-client/lib/acme/client/self_sign_certificate.rb +60 -0
- data/vendor/acme-client/lib/acme/client/version.rb +7 -0
- data/vendor/base32/lib/base32.rb +67 -0
- data/vendor/certificate_authority/lib/certificate_authority.rb +2 -1
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +4 -4
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +7 -5
- data/vendor/certificate_authority/lib/certificate_authority/core_extensions.rb +46 -0
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +6 -2
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +10 -3
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +11 -9
- data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +3 -3
- data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +0 -2
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +8 -2
- data/vendor/certificate_authority/lib/certificate_authority/validations.rb +31 -0
- data/vendor/rsync_command/lib/rsync_command.rb +49 -12
- metadata +50 -91
- data/lib/leap/platform.rb +0 -90
- data/lib/leap_cli/config/environment.rb +0 -180
- data/lib/leap_cli/config/filter.rb +0 -178
- data/lib/leap_cli/config/manager.rb +0 -419
- data/lib/leap_cli/config/node.rb +0 -77
- data/lib/leap_cli/config/object.rb +0 -428
- data/lib/leap_cli/config/object_list.rb +0 -209
- data/lib/leap_cli/config/provider.rb +0 -22
- data/lib/leap_cli/config/secrets.rb +0 -87
- data/lib/leap_cli/config/sources.rb +0 -11
- data/lib/leap_cli/config/tag.rb +0 -25
- data/lib/leap_cli/lib_ext/capistrano_connections.rb +0 -16
- data/lib/leap_cli/logger.rb +0 -237
- data/lib/leap_cli/remote/leap_plugin.rb +0 -192
- data/lib/leap_cli/remote/puppet_plugin.rb +0 -26
- data/lib/leap_cli/remote/rsync_plugin.rb +0 -35
- data/lib/leap_cli/remote/tasks.rb +0 -51
- data/lib/leap_cli/ssh_key.rb +0 -195
- data/lib/leap_cli/util/remote_command.rb +0 -158
- data/lib/leap_cli/util/secret.rb +0 -55
- data/lib/leap_cli/util/x509.rb +0 -33
data/lib/leap_cli/path.rb
CHANGED
@@ -3,7 +3,7 @@ require 'fileutils'
|
|
3
3
|
module LeapCli; module Path
|
4
4
|
|
5
5
|
def self.platform
|
6
|
-
@platform
|
6
|
+
@platform ||= nil
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.provider_base
|
@@ -40,14 +40,14 @@ module LeapCli; module Path
|
|
40
40
|
[Path.provider, Path.provider_base].each do |base|
|
41
41
|
if arg.is_a?(Symbol) || arg.is_a?(Array)
|
42
42
|
named_path(arg, base).tap {|path|
|
43
|
-
return path if File.
|
43
|
+
return path if File.exist?(path)
|
44
44
|
}
|
45
45
|
else
|
46
46
|
File.join(base, arg).tap {|path|
|
47
|
-
return path if File.
|
47
|
+
return path if File.exist?(path)
|
48
48
|
}
|
49
49
|
File.join(base, 'files', arg).tap {|path|
|
50
|
-
return path if File.
|
50
|
+
return path if File.exist?(path)
|
51
51
|
}
|
52
52
|
end
|
53
53
|
end
|
@@ -83,7 +83,7 @@ module LeapCli; module Path
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def self.exists?(name, provider_dir=nil)
|
86
|
-
File.
|
86
|
+
File.exist?(named_path(name, provider_dir))
|
87
87
|
end
|
88
88
|
|
89
89
|
def self.defined?(name)
|
data/lib/leap_cli/util.rb
CHANGED
@@ -10,6 +10,10 @@ module LeapCli
|
|
10
10
|
|
11
11
|
@@exit_status = nil
|
12
12
|
|
13
|
+
def log(*args, &block)
|
14
|
+
LeapCli.log(*args, &block)
|
15
|
+
end
|
16
|
+
|
13
17
|
##
|
14
18
|
## QUITTING
|
15
19
|
##
|
@@ -36,15 +40,14 @@ module LeapCli
|
|
36
40
|
#
|
37
41
|
# exit with error code and with a message that we are bailing out.
|
38
42
|
#
|
39
|
-
def bail!(*message)
|
40
|
-
if
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
log
|
43
|
+
def bail!(*message, &block)
|
44
|
+
LeapCli.logger.log_level = 3 if LeapCli.logger.log_level < 3
|
45
|
+
if message.any?
|
46
|
+
log(0, *message, &block)
|
47
|
+
else
|
48
|
+
log(0, :bailing, "out", :color => :red, :style => :bold, &block)
|
45
49
|
end
|
46
|
-
|
47
|
-
raise SystemExit.new(@exit_status || 1)
|
50
|
+
raise SystemExit.new(exit_status || 1)
|
48
51
|
end
|
49
52
|
|
50
53
|
#
|
@@ -52,7 +55,7 @@ module LeapCli
|
|
52
55
|
#
|
53
56
|
def quit!(message='')
|
54
57
|
puts(message)
|
55
|
-
raise SystemExit.new(
|
58
|
+
raise SystemExit.new(exit_status || 0)
|
56
59
|
end
|
57
60
|
|
58
61
|
#
|
@@ -119,7 +122,7 @@ module LeapCli
|
|
119
122
|
base = options[:base] || Path.provider
|
120
123
|
file_list = files.collect { |file_path|
|
121
124
|
file_path = Path.named_path(file_path, base)
|
122
|
-
File.
|
125
|
+
File.exist?(file_path) ? Path.relative_path(file_path, base) : nil
|
123
126
|
}.compact
|
124
127
|
if file_list.length > 1
|
125
128
|
bail! do
|
@@ -138,7 +141,7 @@ module LeapCli
|
|
138
141
|
options = files.last.is_a?(Hash) ? files.pop : {}
|
139
142
|
file_list = files.collect { |file_path|
|
140
143
|
file_path = Path.named_path(file_path)
|
141
|
-
!File.
|
144
|
+
!File.exist?(file_path) ? Path.relative_path(file_path) : nil
|
142
145
|
}.compact
|
143
146
|
if file_list.length > 1
|
144
147
|
bail! do
|
@@ -157,7 +160,7 @@ module LeapCli
|
|
157
160
|
def file_exists?(*files)
|
158
161
|
files.each do |file_path|
|
159
162
|
file_path = Path.named_path(file_path)
|
160
|
-
if !File.
|
163
|
+
if !File.exist?(file_path)
|
161
164
|
return false
|
162
165
|
end
|
163
166
|
end
|
@@ -233,7 +236,7 @@ module LeapCli
|
|
233
236
|
#
|
234
237
|
def replace_file!(filepath, &block)
|
235
238
|
filepath = Path.named_path(filepath)
|
236
|
-
if !File.
|
239
|
+
if !File.exist?(filepath)
|
237
240
|
content = yield(nil)
|
238
241
|
unless content.nil?
|
239
242
|
write_file!(filepath, content)
|
@@ -258,7 +261,7 @@ module LeapCli
|
|
258
261
|
|
259
262
|
def remove_file!(filepath)
|
260
263
|
filepath = Path.named_path(filepath)
|
261
|
-
if File.
|
264
|
+
if File.exist?(filepath)
|
262
265
|
if File.directory?(filepath)
|
263
266
|
remove_directory!(filepath)
|
264
267
|
else
|
@@ -298,7 +301,7 @@ module LeapCli
|
|
298
301
|
def write_file!(filepath, contents)
|
299
302
|
filepath = Path.named_path(filepath)
|
300
303
|
ensure_dir File.dirname(filepath)
|
301
|
-
existed = File.
|
304
|
+
existed = File.exist?(filepath)
|
302
305
|
if existed
|
303
306
|
if file_content_equals?(filepath, contents)
|
304
307
|
log :nochange, filepath, 2
|
@@ -320,11 +323,11 @@ module LeapCli
|
|
320
323
|
def rename_file!(oldpath, newpath)
|
321
324
|
oldpath = Path.named_path(oldpath)
|
322
325
|
newpath = Path.named_path(newpath)
|
323
|
-
if File.
|
326
|
+
if File.exist? newpath
|
324
327
|
log :skipping, "#{Path.relative_path(newpath)}, file already exists"
|
325
328
|
return
|
326
329
|
end
|
327
|
-
if !File.
|
330
|
+
if !File.exist? oldpath
|
328
331
|
log :skipping, "#{Path.relative_path(oldpath)}, file is missing"
|
329
332
|
return
|
330
333
|
end
|
@@ -425,11 +428,18 @@ module LeapCli
|
|
425
428
|
end
|
426
429
|
end
|
427
430
|
|
431
|
+
def is_git_subrepo?(dir)
|
432
|
+
Dir.chdir(dir) do
|
433
|
+
`ls .gitrepo 2>/dev/null`
|
434
|
+
return $? == 0
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
428
438
|
def current_git_branch(dir)
|
429
439
|
Dir.chdir(dir) do
|
430
440
|
branch = `git symbolic-ref HEAD 2>/dev/null`.strip
|
431
441
|
if branch.chars.any?
|
432
|
-
branch.sub
|
442
|
+
branch.sub(/^refs\/heads\//, '')
|
433
443
|
else
|
434
444
|
nil
|
435
445
|
end
|
data/lib/leap_cli/version.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module LeapCli
|
2
2
|
unless defined?(LeapCli::VERSION)
|
3
|
-
VERSION = '1.
|
4
|
-
COMPATIBLE_PLATFORM_VERSION = '0.
|
3
|
+
VERSION = '1.9'
|
4
|
+
COMPATIBLE_PLATFORM_VERSION = '0.9'..'0.99'
|
5
5
|
SUMMARY = 'Command line interface to the LEAP platform'
|
6
6
|
DESCRIPTION = 'The command "leap" can be used to manage a bevy of servers running the LEAP platform from the comfort of your own home.'
|
7
|
-
LOAD_PATHS = ['lib',
|
7
|
+
LOAD_PATHS = ['lib',
|
8
|
+
'vendor/certificate_authority/lib',
|
9
|
+
'vendor/rsync_command/lib',
|
10
|
+
'vendor/base32/lib',
|
11
|
+
'vendor/acme-client/lib'
|
12
|
+
]
|
8
13
|
end
|
9
14
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'acme/client'
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
require 'digest'
|
7
|
+
require 'forwardable'
|
8
|
+
require 'base64'
|
9
|
+
require 'time'
|
10
|
+
|
11
|
+
module Acme; end
|
12
|
+
class Acme::Client; end
|
13
|
+
|
14
|
+
require 'acme/client/version'
|
15
|
+
require 'acme/client/certificate'
|
16
|
+
require 'acme/client/certificate_request'
|
17
|
+
require 'acme/client/self_sign_certificate'
|
18
|
+
require 'acme/client/crypto'
|
19
|
+
require 'acme/client/resources'
|
20
|
+
require 'acme/client/faraday_middleware'
|
21
|
+
require 'acme/client/error'
|
22
|
+
|
23
|
+
class Acme::Client
|
24
|
+
DEFAULT_ENDPOINT = 'http://127.0.0.1:4000'.freeze
|
25
|
+
DIRECTORY_DEFAULT = {
|
26
|
+
'new-authz' => '/acme/new-authz',
|
27
|
+
'new-cert' => '/acme/new-cert',
|
28
|
+
'new-reg' => '/acme/new-reg',
|
29
|
+
'revoke-cert' => '/acme/revoke-cert'
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
def initialize(private_key:, endpoint: DEFAULT_ENDPOINT, directory_uri: nil, connection_options: {})
|
33
|
+
@endpoint, @private_key, @directory_uri, @connection_options = endpoint, private_key, directory_uri, connection_options
|
34
|
+
@nonces ||= []
|
35
|
+
load_directory!
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :private_key, :nonces, :operation_endpoints
|
39
|
+
|
40
|
+
def register(contact:)
|
41
|
+
payload = {
|
42
|
+
resource: 'new-reg', contact: Array(contact)
|
43
|
+
}
|
44
|
+
|
45
|
+
response = connection.post(@operation_endpoints.fetch('new-reg'), payload)
|
46
|
+
::Acme::Client::Resources::Registration.new(self, response)
|
47
|
+
end
|
48
|
+
|
49
|
+
def authorize(domain:)
|
50
|
+
payload = {
|
51
|
+
resource: 'new-authz',
|
52
|
+
identifier: {
|
53
|
+
type: 'dns',
|
54
|
+
value: domain
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
response = connection.post(@operation_endpoints.fetch('new-authz'), payload)
|
59
|
+
::Acme::Client::Resources::Authorization.new(self, response.headers['Location'], response)
|
60
|
+
end
|
61
|
+
|
62
|
+
def fetch_authorization(uri)
|
63
|
+
response = connection.get(uri)
|
64
|
+
::Acme::Client::Resources::Authorization.new(self, uri, response)
|
65
|
+
end
|
66
|
+
|
67
|
+
def new_certificate(csr)
|
68
|
+
payload = {
|
69
|
+
resource: 'new-cert',
|
70
|
+
csr: Base64.urlsafe_encode64(csr.to_der)
|
71
|
+
}
|
72
|
+
|
73
|
+
response = connection.post(@operation_endpoints.fetch('new-cert'), payload)
|
74
|
+
::Acme::Client::Certificate.new(OpenSSL::X509::Certificate.new(response.body), response.headers['location'], fetch_chain(response), csr)
|
75
|
+
end
|
76
|
+
|
77
|
+
def revoke_certificate(certificate)
|
78
|
+
payload = { resource: 'revoke-cert', certificate: Base64.urlsafe_encode64(certificate.to_der) }
|
79
|
+
endpoint = @operation_endpoints.fetch('revoke-cert')
|
80
|
+
response = connection.post(endpoint, payload)
|
81
|
+
response.success?
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.revoke_certificate(certificate, *arguments)
|
85
|
+
client = new(*arguments)
|
86
|
+
client.revoke_certificate(certificate)
|
87
|
+
end
|
88
|
+
|
89
|
+
def connection
|
90
|
+
@connection ||= Faraday.new(@endpoint, **@connection_options) do |configuration|
|
91
|
+
configuration.use Acme::Client::FaradayMiddleware, client: self
|
92
|
+
configuration.adapter Faraday.default_adapter
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def fetch_chain(response, limit = 10)
|
99
|
+
links = response.headers['link']
|
100
|
+
if limit.zero? || links.nil? || links['up'].nil?
|
101
|
+
[]
|
102
|
+
else
|
103
|
+
issuer = connection.get(links['up'])
|
104
|
+
[OpenSSL::X509::Certificate.new(issuer.body), *fetch_chain(issuer, limit - 1)]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def load_directory!
|
109
|
+
@operation_endpoints = if @directory_uri
|
110
|
+
response = connection.get(@directory_uri)
|
111
|
+
body = response.body
|
112
|
+
{
|
113
|
+
'new-reg' => body.fetch('new-reg'),
|
114
|
+
'new-authz' => body.fetch('new-authz'),
|
115
|
+
'new-cert' => body.fetch('new-cert'),
|
116
|
+
'revoke-cert' => body.fetch('revoke-cert'),
|
117
|
+
}
|
118
|
+
else
|
119
|
+
DIRECTORY_DEFAULT
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Acme::Client::Certificate
|
2
|
+
extend Forwardable
|
3
|
+
|
4
|
+
attr_reader :x509, :x509_chain, :request, :private_key, :url
|
5
|
+
|
6
|
+
def_delegators :x509, :to_pem, :to_der
|
7
|
+
|
8
|
+
def initialize(certificate, url, chain, request)
|
9
|
+
@x509 = certificate
|
10
|
+
@url = url
|
11
|
+
@x509_chain = chain
|
12
|
+
@request = request
|
13
|
+
end
|
14
|
+
|
15
|
+
def chain_to_pem
|
16
|
+
x509_chain.map(&:to_pem).join
|
17
|
+
end
|
18
|
+
|
19
|
+
def x509_fullchain
|
20
|
+
[x509, *x509_chain]
|
21
|
+
end
|
22
|
+
|
23
|
+
def fullchain_to_pem
|
24
|
+
x509_fullchain.map(&:to_pem).join
|
25
|
+
end
|
26
|
+
|
27
|
+
def common_name
|
28
|
+
x509.subject.to_a.find { |name, _, _| name == 'CN' }[1]
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
class Acme::Client::CertificateRequest
|
2
|
+
extend Forwardable
|
3
|
+
|
4
|
+
DEFAULT_KEY_LENGTH = 2048
|
5
|
+
DEFAULT_DIGEST = OpenSSL::Digest::SHA256
|
6
|
+
SUBJECT_KEYS = {
|
7
|
+
common_name: 'CN',
|
8
|
+
country_name: 'C',
|
9
|
+
organization_name: 'O',
|
10
|
+
organizational_unit: 'OU',
|
11
|
+
state_or_province: 'ST',
|
12
|
+
locality_name: 'L'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
SUBJECT_TYPES = {
|
16
|
+
'CN' => OpenSSL::ASN1::UTF8STRING,
|
17
|
+
'C' => OpenSSL::ASN1::UTF8STRING,
|
18
|
+
'O' => OpenSSL::ASN1::UTF8STRING,
|
19
|
+
'OU' => OpenSSL::ASN1::UTF8STRING,
|
20
|
+
'ST' => OpenSSL::ASN1::UTF8STRING,
|
21
|
+
'L' => OpenSSL::ASN1::UTF8STRING
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
attr_reader :private_key, :common_name, :names, :subject
|
25
|
+
|
26
|
+
def_delegators :csr, :to_pem, :to_der
|
27
|
+
|
28
|
+
def initialize(common_name: nil, names: [], private_key: generate_private_key, subject: {}, digest: DEFAULT_DIGEST.new)
|
29
|
+
@digest = digest
|
30
|
+
@private_key = private_key
|
31
|
+
@subject = normalize_subject(subject)
|
32
|
+
@common_name = common_name || @subject[SUBJECT_KEYS[:common_name]] || @subject[:common_name]
|
33
|
+
@names = names.to_a.dup
|
34
|
+
normalize_names
|
35
|
+
@subject[SUBJECT_KEYS[:common_name]] ||= @common_name
|
36
|
+
validate_subject
|
37
|
+
end
|
38
|
+
|
39
|
+
def csr
|
40
|
+
@csr ||= generate
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def generate_private_key
|
46
|
+
OpenSSL::PKey::RSA.new(DEFAULT_KEY_LENGTH)
|
47
|
+
end
|
48
|
+
|
49
|
+
def normalize_subject(subject)
|
50
|
+
@subject = subject.each_with_object({}) do |(key, value), hash|
|
51
|
+
hash[SUBJECT_KEYS.fetch(key, key)] = value.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def normalize_names
|
56
|
+
if @common_name
|
57
|
+
@names.unshift(@common_name) unless @names.include?(@common_name)
|
58
|
+
else
|
59
|
+
raise ArgumentError, 'No common name and no list of names given' if @names.empty?
|
60
|
+
@common_name = @names.first
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_subject
|
65
|
+
validate_subject_attributes
|
66
|
+
validate_subject_common_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_subject_attributes
|
70
|
+
extra_keys = @subject.keys - SUBJECT_KEYS.keys - SUBJECT_KEYS.values
|
71
|
+
return if extra_keys.empty?
|
72
|
+
raise ArgumentError, "Unexpected subject attributes given: #{extra_keys.inspect}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_subject_common_name
|
76
|
+
return if @common_name == @subject[SUBJECT_KEYS[:common_name]]
|
77
|
+
raise ArgumentError, 'Conflicting common name given in arguments and subject'
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate
|
81
|
+
OpenSSL::X509::Request.new.tap do |csr|
|
82
|
+
csr.public_key = @private_key.public_key
|
83
|
+
csr.subject = generate_subject
|
84
|
+
csr.version = 2
|
85
|
+
add_extension(csr)
|
86
|
+
csr.sign @private_key, @digest
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def generate_subject
|
91
|
+
OpenSSL::X509::Name.new(
|
92
|
+
@subject.map {|name, value|
|
93
|
+
[name, value, SUBJECT_TYPES[name]]
|
94
|
+
}
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_extension(csr)
|
99
|
+
return if @names.size <= 1
|
100
|
+
|
101
|
+
extension = OpenSSL::X509::ExtensionFactory.new.create_extension(
|
102
|
+
'subjectAltName', @names.map { |name| "DNS:#{name}" }.join(', '), false
|
103
|
+
)
|
104
|
+
csr.add_attribute(
|
105
|
+
OpenSSL::X509::Attribute.new(
|
106
|
+
'extReq',
|
107
|
+
OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([extension])])
|
108
|
+
)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|