Skip to content

Commit 70dd111

Browse files
committed
fix random per-resource TLS failures for script-initiated requests
1 parent 7acab76 commit 70dd111

1 file changed

Lines changed: 32 additions & 23 deletions

File tree

src/http/proxy/server/certificate_generator.cr

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class HTTP::Proxy::Server
4747
serial_path : String) : Bool
4848
return false unless generate_private_key(key_path)
4949

50+
serial = next_serial(serial_path)
51+
5052
host_key = load_private_key(key_path)
5153
return false if host_key.null?
5254

@@ -59,11 +61,10 @@ class HTTP::Proxy::Server
5961
csr = create_csr(host, host_key)
6062
return false if csr.null?
6163

62-
cert, serial = sign_csr(host, csr, ca_cert, ca_key)
64+
cert = sign_csr(host, serial, csr, ca_cert, ca_key)
6365
return false if cert.null?
6466

6567
return false unless write_certificate(cert_path, cert)
66-
File.write(serial_path, serial.to_s)
6768
true
6869
ensure
6970
LibCrypto.x509_req_free(csr) if csr && !csr.null?
@@ -126,50 +127,58 @@ class HTTP::Proxy::Server
126127
# Native equivalent of:
127128
# openssl x509 -req -in <csr> -CA <ca_cert> -CAkey <ca_key> \
128129
# -days 825 -sha256 -extfile <ext>
129-
private def sign_csr(host : String, req : LibCrypto::X509_REQ, ca_cert : LibCrypto::X509, ca_key : LibCrypto::EVP_PKEY) : {LibCrypto::X509, Int64}
130+
private def sign_csr(host : String, serial : Int64, req : LibCrypto::X509_REQ, ca_cert : LibCrypto::X509, ca_key : LibCrypto::EVP_PKEY) : LibCrypto::X509
130131
cert = LibCrypto.x509_new
131-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if cert.null?
132+
return Pointer(Void).null.as(LibCrypto::X509) if cert.null?
132133

133134
req_pubkey = LibCrypto.x509_req_get_pubkey(req)
134-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if req_pubkey.null?
135+
return Pointer(Void).null.as(LibCrypto::X509) if req_pubkey.null?
135136

136137
begin
137-
serial = Time.utc.to_unix
138-
139-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_set_version(cert, 2) != 1
138+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_set_version(cert, 2) != 1
140139
serial_number = LibCrypto.x509_get_serial_number(cert)
141-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if serial_number.null?
142-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.asn1_integer_set(serial_number, serial) != 1
140+
return Pointer(Void).null.as(LibCrypto::X509) if serial_number.null?
141+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.asn1_integer_set(serial_number, serial) != 1
143142

144143
not_before = LibCrypto.x509_getm_not_before(cert)
145144
not_after = LibCrypto.x509_getm_not_after(cert)
146-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if not_before.null? || not_after.null?
147-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_gmtime_adj(not_before, 0).null?
148-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_gmtime_adj(not_after, 825_i64 * 24 * 60 * 60).null?
145+
return Pointer(Void).null.as(LibCrypto::X509) if not_before.null? || not_after.null?
146+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_gmtime_adj(not_before, 0).null?
147+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_gmtime_adj(not_after, 825_i64 * 24 * 60 * 60).null?
149148

150149
req_subject = LibCrypto.x509_req_get_subject_name(req)
151150
issuer_subject = LibCrypto.x509_get_subject_name(ca_cert)
152-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if req_subject.null? || issuer_subject.null?
151+
return Pointer(Void).null.as(LibCrypto::X509) if req_subject.null? || issuer_subject.null?
153152

154-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_set_subject_name(cert, req_subject) != 1
155-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_set_issuer_name(cert, issuer_subject) != 1
156-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_set_pubkey(cert, req_pubkey) != 1
153+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_set_subject_name(cert, req_subject) != 1
154+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_set_issuer_name(cert, issuer_subject) != 1
155+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_set_pubkey(cert, req_pubkey) != 1
157156

158-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} unless add_extension(cert, "basicConstraints", "critical,CA:FALSE")
159-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} unless add_extension(cert, "keyUsage", "critical,digitalSignature,keyEncipherment")
160-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} unless add_extension(cert, "extendedKeyUsage", "serverAuth")
157+
return Pointer(Void).null.as(LibCrypto::X509) unless add_extension(cert, "basicConstraints", "critical,CA:FALSE")
158+
return Pointer(Void).null.as(LibCrypto::X509) unless add_extension(cert, "keyUsage", "critical,digitalSignature,keyEncipherment")
159+
return Pointer(Void).null.as(LibCrypto::X509) unless add_extension(cert, "extendedKeyUsage", "serverAuth")
161160

162161
san_value = Socket::IPAddress.valid?(host) ? "IP:#{host}" : "DNS:#{host}"
163-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} unless add_extension(cert, "subjectAltName", san_value)
162+
return Pointer(Void).null.as(LibCrypto::X509) unless add_extension(cert, "subjectAltName", san_value)
164163

165-
return {Pointer(Void).null.as(LibCrypto::X509), 0_i64} if LibCrypto.x509_sign(cert, ca_key, LibCrypto.evp_sha256) <= 0
164+
return Pointer(Void).null.as(LibCrypto::X509) if LibCrypto.x509_sign(cert, ca_key, LibCrypto.evp_sha256) <= 0
166165

167-
{cert, serial}
166+
cert
168167
ensure
169168
LibCrypto.evp_pkey_free(req_pubkey)
170169
end
171170
end
172171

172+
private def next_serial(serial_path : String) : Int64
173+
current = if File.exists?(serial_path)
174+
File.read(serial_path).strip.to_i64?
175+
end
176+
base = current || (Time.utc.to_unix * 1000)
177+
serial = base + 1
178+
File.write(serial_path, serial.to_s)
179+
serial
180+
end
181+
173182
# Native equivalent of adding extension entries via extfile in `openssl x509 -extfile ...`.
174183
private def add_extension(cert : LibCrypto::X509, name : String, value : String) : Bool
175184
nid = LibCrypto.obj_sn2nid(name.to_unsafe)

0 commit comments

Comments
 (0)