leap_cli 1.8.1 → 1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|