Skip to content

Commit b0c9748

Browse files
authored
Merge pull request #72 from jgnagy/fix/better-support-for-cert-manager
Improve detection of SANS for cert-manager
2 parents f1c6470 + 49b752e commit b0c9748

File tree

3 files changed

+37
-29
lines changed

3 files changed

+37
-29
lines changed

lib/bullion.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ class ConfigError < Error; end
6969
MetricsRegistry = Prometheus::Client.registry
7070

7171
def self.ca_key
72-
@ca_key ||= OpenSSL::PKey::RSA.new(File.read(config.ca.key_path), config.ca.secret)
72+
@ca_key ||= begin
73+
OpenSSL::PKey::RSA.new(File.read(config.ca.key_path), config.ca.secret)
74+
rescue OpenSSL::PKey::RSAError
75+
OpenSSL::PKey::EC.new(File.read(config.ca.key_path), config.ca.secret)
76+
end
7377
end
7478

7579
def self.ca_cert_file

lib/bullion/helpers/acme.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ def acme_csr_valid?(order_csr)
119119
csr_attrs = extract_csr_attrs(csr)
120120
csr_sans = extract_csr_sans(csr_attrs)
121121
csr_domains = extract_csr_domains(csr_sans)
122-
csr_cn = cn_from_csr(csr)
122+
csr_cn = cn_from_csr(csr) || csr_domains.first
123123

124124
# Make sure the CSR has a valid public key
125125
raise Bullion::Acme::Errors::BadCsr unless csr.verify(csr.public_key)
126126

127127
return false unless order.ready_status?
128-
raise Bullion::Acme::Errors::BadCsr unless csr_domains.include?(csr_cn)
128+
raise Bullion::Acme::Errors::BadCsr if csr_cn && !csr_domains.include?(csr_cn)
129129
raise Bullion::Acme::Errors::BadCsr unless csr_domains.sort == order.domains.sort
130130

131131
true

lib/bullion/helpers/ssl.rb

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,31 +127,14 @@ def manage_csr_extensions(csr, new_cert)
127127
)
128128

129129
# Alternate Names
130-
cn = cn_from_csr(csr)
131-
existing_sans = filter_sans(csr_sans(csr))
132-
valid_alts = (["DNS:#{cn}"] + [*existing_sans]).uniq
130+
valid_alts = build_valid_alt_names(csr)
133131

134132
new_cert.add_extension(ef.create_extension("subjectAltName", valid_alts.join(",")))
135133

136134
# return the updated cert and any subject alternate names added
137135
[new_cert, valid_alts]
138136
end
139137

140-
def csr_sans(csr)
141-
raw_attributes = csr.attributes
142-
return [] unless raw_attributes
143-
144-
seq = extract_csr_attrs(csr)
145-
return [] unless seq
146-
147-
values = extract_san_values(seq)
148-
return [] unless values
149-
150-
values = OpenSSL::ASN1.decode(values).value
151-
152-
values.select { |v| v.tag == 2 }.map { |v| "DNS:#{v.value}" }
153-
end
154-
155138
def extract_csr_attrs(csr)
156139
csr.attributes.select { |a| a.oid == "extReq" }.map { |a| a.value.map(&:value) }
157140
end
@@ -161,8 +144,9 @@ def extract_csr_sans(csr_attrs)
161144
end
162145

163146
def extract_csr_domains(csr_sans)
164-
csr_decoded_sans = OpenSSL::ASN1.decode(csr_sans.first.value[1].value)
165-
csr_decoded_sans.select { |v| v.tag == 2 }.map(&:value)
147+
subject_alt_names = csr_sans.first.value.find { |v| v.tag == 4 }
148+
csr_decoded_sans = OpenSSL::ASN1.decode(subject_alt_names.value)
149+
csr_decoded_sans.value.select { |v| v.tag == 2 }.map(&:value)
166150
end
167151

168152
def extract_san_values(sequence)
@@ -184,13 +168,33 @@ def filter_sans(potential_sans)
184168
end
185169

186170
def cn_from_csr(csr)
187-
if csr.subject.to_s
188-
cns = csr.subject.to_s.split("/").grep(/^CN=/)
171+
return unless csr.subject.to_s
189172

190-
return cns.first.split("=").last if cns && !cns.empty?
191-
end
173+
cns = csr.subject.to_s.split("/").grep(/^CN=/)
174+
175+
cns.first.split("=").last if cns && !cns.empty?
176+
end
177+
178+
def cn_or_first_san_from_csr(csr)
179+
cn = cn_from_csr(csr)
180+
return cn if cn
181+
182+
csr_attrs = extract_csr_attrs(csr)
183+
csr_sans = extract_csr_sans(csr_attrs)
184+
extract_csr_domains(csr_sans).first
185+
end
186+
187+
def domains_from_csr(csr)
188+
csr_attrs = extract_csr_attrs(csr)
189+
csr_sans = extract_csr_sans(csr_attrs)
190+
extract_csr_domains(csr_sans)
191+
end
192192

193-
csr_sans(csr).first.split(":").last
193+
def build_valid_alt_names(csr)
194+
cn = cn_or_first_san_from_csr(csr)
195+
csr_domains = domains_from_csr(csr)
196+
existing_sans = filter_sans(csr_domains).map { |d| "DNS:#{d}" }
197+
(["DNS:#{cn}"] + [*existing_sans]).uniq
194198
end
195199

196200
# Signs an ACME CSR
@@ -206,7 +210,7 @@ def sign_csr(csr, username)
206210
csr_cert.not_after = csr_cert.not_before + (3 * 30 * 24 * 60 * 60)
207211

208212
# Force a subject if the cert doesn't have one
209-
cert.subject = simple_subject(cn_from_csr(csr)) unless cert.subject
213+
cert.subject = simple_subject(cn_or_first_san_from_csr(csr)) unless cert.subject
210214

211215
csr_cert.subject = simple_subject(cert.subject.to_s)
212216

0 commit comments

Comments
 (0)