itsi 0.2.26 → 0.2.27.rc1
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/Cargo.lock +7 -3
- data/Rakefile +24 -3
- data/crates/itsi_acme/Cargo.toml +2 -1
- data/crates/itsi_acme/src/acceptor.rs +1 -1
- data/crates/itsi_acme/src/acme.rs +31 -3
- data/crates/itsi_acme/src/http_challenge.rs +81 -0
- data/crates/itsi_acme/src/https_helper.rs +3 -1
- data/crates/itsi_acme/src/jose.rs +6 -2
- data/crates/itsi_acme/src/lib.rs +2 -0
- data/crates/itsi_acme/src/resolver.rs +27 -4
- data/crates/itsi_acme/src/state.rs +183 -22
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +115 -64
- data/crates/itsi_scheduler/src/lib.rs +2 -1
- data/crates/itsi_server/Cargo.toml +2 -1
- data/crates/itsi_server/src/lib.rs +15 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +9 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +95 -0
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +22 -1
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +100 -0
- data/crates/itsi_server/src/server/binds/listener.rs +9 -24
- data/crates/itsi_server/src/server/binds/tls.rs +372 -67
- data/crates/itsi_server/src/services/itsi_http_service.rs +46 -2
- data/gems/scheduler/Cargo.lock +4011 -527
- data/gems/scheduler/Gemfile +8 -2
- data/gems/scheduler/Gemfile.lock +107 -0
- data/gems/scheduler/Rakefile +33 -9
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +121 -6
- data/gems/scheduler/test/helpers/test_helper.rb +2 -0
- data/gems/scheduler/test/test_address_resolve.rb +8 -2
- data/gems/scheduler/test/test_itsi_scheduler.rb +80 -0
- data/gems/scheduler/test/test_timeout_after.rb +102 -0
- data/gems/server/Cargo.lock +30 -1
- data/gems/server/Gemfile +2 -0
- data/gems/server/Gemfile.lock +123 -0
- data/gems/server/Rakefile +18 -5
- data/gems/server/lib/itsi/http_request.rb +10 -0
- data/gems/server/lib/itsi/server/rack_interface.rb +45 -2
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +24 -0
- data/gems/server/test/acme/local_acme_challenges.rb +190 -0
- data/gems/server/test/helpers/local_acme.rb +218 -0
- data/gems/server/test/helpers/test_helper.rb +7 -9
- data/gems/server/test/middleware/endpoint.rb +9 -6
- data/gems/server/test/rack/test_rack_server.rb +79 -0
- data/lib/itsi/version.rb +1 -1
- metadata +12 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ea9fdbfbdd35894f2c5977d2624a4b90cb9467b3133d055d5ae87d1b267d5ff
|
|
4
|
+
data.tar.gz: ad82474c56c170163d2fcc7e75c6e2351fbc61348e3fd8e60697acda4ffbe65a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47615c9e033b2119fe5a1c5780ed9acb4731500667fdc42f75df7a9768f0f4feda2d6e80f04ff85a1a938e8832934704b20d81cea48b7d96a87406bae22511e4
|
|
7
|
+
data.tar.gz: 33ccb5b47c880ab500dc0551fbb853a8e19a5afd95cd3c8a5e4fc64b3895a602a68e8aaaeaa3b1133ad4bf56e968a4821efc71798b2c46b8d62705a790de78cd
|
data/Cargo.lock
CHANGED
|
@@ -1662,7 +1662,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
|
1662
1662
|
|
|
1663
1663
|
[[package]]
|
|
1664
1664
|
name = "itsi-scheduler"
|
|
1665
|
-
version = "0.2.
|
|
1665
|
+
version = "0.2.27"
|
|
1666
1666
|
dependencies = [
|
|
1667
1667
|
"bytes",
|
|
1668
1668
|
"derive_more",
|
|
@@ -1680,7 +1680,7 @@ dependencies = [
|
|
|
1680
1680
|
|
|
1681
1681
|
[[package]]
|
|
1682
1682
|
name = "itsi-server"
|
|
1683
|
-
version = "0.2.
|
|
1683
|
+
version = "0.2.27"
|
|
1684
1684
|
dependencies = [
|
|
1685
1685
|
"argon2",
|
|
1686
1686
|
"async-channel",
|
|
@@ -1742,6 +1742,7 @@ dependencies = [
|
|
|
1742
1742
|
"tokio-util",
|
|
1743
1743
|
"tracing",
|
|
1744
1744
|
"url",
|
|
1745
|
+
"webpki-roots",
|
|
1745
1746
|
]
|
|
1746
1747
|
|
|
1747
1748
|
[[package]]
|
|
@@ -1757,6 +1758,7 @@ dependencies = [
|
|
|
1757
1758
|
"futures",
|
|
1758
1759
|
"log",
|
|
1759
1760
|
"num-bigint",
|
|
1761
|
+
"parking_lot",
|
|
1760
1762
|
"pem",
|
|
1761
1763
|
"proc-macro2",
|
|
1762
1764
|
"rcgen",
|
|
@@ -2606,7 +2608,9 @@ dependencies = [
|
|
|
2606
2608
|
|
|
2607
2609
|
[[package]]
|
|
2608
2610
|
name = "rb-sys-build"
|
|
2609
|
-
version = "0.9.
|
|
2611
|
+
version = "0.9.126"
|
|
2612
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2613
|
+
checksum = "855fc1ad8943d12c89ef12f9147f1cc531f5bf19fb744112fdd317bb6ee7b5c5"
|
|
2610
2614
|
dependencies = [
|
|
2611
2615
|
"bindgen 0.72.1",
|
|
2612
2616
|
"lazy_static",
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'bundler/gem_tasks'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
def test_tasks_requested?
|
|
6
|
+
requested = Rake.application.top_level_tasks
|
|
7
|
+
return true if requested.empty?
|
|
8
|
+
|
|
9
|
+
requested.any? do |task_name|
|
|
10
|
+
task_name == "test" ||
|
|
11
|
+
task_name.end_with?(":test") ||
|
|
12
|
+
task_name.start_with?("test:")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require 'minitest/test_task' if test_tasks_requested?
|
|
5
17
|
|
|
6
18
|
# Ensure the nested gems' `lib` directories are included in the LOAD_PATH
|
|
7
19
|
$LOAD_PATH.unshift(File.expand_path('scheduler/lib', __dir__))
|
|
@@ -25,17 +37,26 @@ GEMS = [
|
|
|
25
37
|
]
|
|
26
38
|
SHARED_TASKS = %i[compile compile:dev test]
|
|
27
39
|
|
|
40
|
+
def quiet_ruby_env
|
|
41
|
+
rubyopt = [ENV["RUBYOPT"], "-W0"].compact.join(" ").strip
|
|
42
|
+
rubyopt.empty? ? {} : { "RUBYOPT" => rubyopt }
|
|
43
|
+
end
|
|
44
|
+
|
|
28
45
|
GEMS.each do |gem|
|
|
29
46
|
namespace gem[:shortname] do
|
|
30
47
|
desc "Run tasks in the #{gem[:dir]} directory"
|
|
31
48
|
task :default do
|
|
32
|
-
|
|
49
|
+
Dir.chdir(gem[:dir]) do
|
|
50
|
+
sh quiet_ruby_env, "bundle", "exec", "rake", verbose: false
|
|
51
|
+
end
|
|
33
52
|
end
|
|
34
53
|
|
|
35
54
|
SHARED_TASKS.each do |task|
|
|
36
55
|
task task do
|
|
37
56
|
Rake::Task[:sync_crates].invoke
|
|
38
|
-
|
|
57
|
+
Dir.chdir(gem[:dir]) do
|
|
58
|
+
sh quiet_ruby_env, "bundle", "exec", "rake", task.to_s, verbose: false
|
|
59
|
+
end
|
|
39
60
|
end
|
|
40
61
|
end
|
|
41
62
|
end
|
data/crates/itsi_acme/Cargo.toml
CHANGED
|
@@ -31,13 +31,14 @@ async-trait = "0.1.53"
|
|
|
31
31
|
rustls = { version = "0.23", default-features = false, features = ["ring"] }
|
|
32
32
|
time = "0.3.36" # force the transitive dependency to a more recent minimal version. The build fails with 0.3.20
|
|
33
33
|
|
|
34
|
-
tokio = { version = "1.20.1", default-features = false }
|
|
34
|
+
tokio = { version = "1.20.1", default-features = false, features = ["fs", "io-util", "rt", "sync", "time"] }
|
|
35
35
|
tokio-rustls = { version = "0.26", default-features = false, features = [
|
|
36
36
|
"tls12",
|
|
37
37
|
] }
|
|
38
38
|
reqwest = { version = "0.12", default-features = false, features = [
|
|
39
39
|
"rustls-tls",
|
|
40
40
|
] }
|
|
41
|
+
parking_lot = "0.12"
|
|
41
42
|
|
|
42
43
|
# Axum
|
|
43
44
|
axum-server = { version = "0.7", features = ["tokio-rustls"], optional = true }
|
|
@@ -16,7 +16,7 @@ pub struct AcmeAcceptor {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
impl AcmeAcceptor {
|
|
19
|
-
pub
|
|
19
|
+
pub fn new(resolver: Arc<ResolvesServerCertAcme>) -> Self {
|
|
20
20
|
let mut config = ServerConfig::builder()
|
|
21
21
|
.with_no_client_auth()
|
|
22
22
|
.with_cert_resolver(resolver);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
3
|
use crate::https_helper::{https, HttpsRequestError, Method, Response};
|
|
4
|
-
use crate::jose::{key_authorization_sha256, sign, sign_eab, JoseError};
|
|
4
|
+
use crate::jose::{key_authorization, key_authorization_sha256, sign, sign_eab, JoseError};
|
|
5
5
|
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
|
6
6
|
use base64::Engine;
|
|
7
7
|
use rcgen::{CustomExtension, Error as RcgenError, PKCS_ECDSA_P256_SHA256};
|
|
@@ -191,7 +191,11 @@ impl Account {
|
|
|
191
191
|
None => return Err(AcmeError::NoTlsAlpn01Challenge),
|
|
192
192
|
};
|
|
193
193
|
let mut params = rcgen::CertificateParams::new(vec![domain])?;
|
|
194
|
-
let
|
|
194
|
+
let token = challenge
|
|
195
|
+
.token
|
|
196
|
+
.as_deref()
|
|
197
|
+
.ok_or(AcmeError::MissingChallengeToken)?;
|
|
198
|
+
let key_auth = key_authorization_sha256(&self.key_pair, token)?;
|
|
195
199
|
params.custom_extensions = vec![CustomExtension::new_acme_identifier(key_auth.as_ref())];
|
|
196
200
|
|
|
197
201
|
let key_pair = rcgen::KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
|
|
@@ -204,6 +208,24 @@ impl Account {
|
|
|
204
208
|
let certified_key = CertifiedKey::new(vec![cert.der().clone()], pk);
|
|
205
209
|
Ok((challenge, certified_key))
|
|
206
210
|
}
|
|
211
|
+
|
|
212
|
+
pub fn http_01<'a>(
|
|
213
|
+
&self,
|
|
214
|
+
challenges: &'a [Challenge],
|
|
215
|
+
) -> Result<(&'a Challenge, String), AcmeError> {
|
|
216
|
+
let challenge = challenges.iter().find(|c| c.typ == ChallengeType::Http01);
|
|
217
|
+
|
|
218
|
+
let challenge = match challenge {
|
|
219
|
+
Some(challenge) => challenge,
|
|
220
|
+
None => return Err(AcmeError::NoHttp01Challenge),
|
|
221
|
+
};
|
|
222
|
+
let token = challenge
|
|
223
|
+
.token
|
|
224
|
+
.as_deref()
|
|
225
|
+
.ok_or(AcmeError::MissingChallengeToken)?;
|
|
226
|
+
|
|
227
|
+
Ok((challenge, key_authorization(&self.key_pair, token)?))
|
|
228
|
+
}
|
|
207
229
|
}
|
|
208
230
|
|
|
209
231
|
#[derive(Debug, Clone, Deserialize)]
|
|
@@ -253,6 +275,8 @@ pub enum ChallengeType {
|
|
|
253
275
|
Dns01,
|
|
254
276
|
#[serde(rename = "tls-alpn-01")]
|
|
255
277
|
TlsAlpn01,
|
|
278
|
+
#[serde(other)]
|
|
279
|
+
Unknown,
|
|
256
280
|
}
|
|
257
281
|
|
|
258
282
|
#[derive(Debug, Deserialize)]
|
|
@@ -305,7 +329,7 @@ pub struct Challenge {
|
|
|
305
329
|
#[serde(rename = "type")]
|
|
306
330
|
pub typ: ChallengeType,
|
|
307
331
|
pub url: String,
|
|
308
|
-
pub token: String
|
|
332
|
+
pub token: Option<String>,
|
|
309
333
|
pub error: Option<Problem>,
|
|
310
334
|
}
|
|
311
335
|
|
|
@@ -335,6 +359,10 @@ pub enum AcmeError {
|
|
|
335
359
|
Crypto(#[from] Unspecified),
|
|
336
360
|
#[error("acme service response is missing {0} header")]
|
|
337
361
|
MissingHeader(&'static str),
|
|
362
|
+
#[error("selected challenge is missing token")]
|
|
363
|
+
MissingChallengeToken,
|
|
364
|
+
#[error("no http-01 challenge found")]
|
|
365
|
+
NoHttp01Challenge,
|
|
338
366
|
#[error("no tls-alpn-01 challenge found")]
|
|
339
367
|
NoTlsAlpn01Challenge,
|
|
340
368
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
use parking_lot::RwLock;
|
|
2
|
+
use std::collections::HashMap;
|
|
3
|
+
use std::sync::Arc;
|
|
4
|
+
|
|
5
|
+
const ACME_CHALLENGE_PREFIX: &str = "/.well-known/acme-challenge/";
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Clone, Default)]
|
|
8
|
+
pub struct Http01Handler {
|
|
9
|
+
challenges: Arc<RwLock<HashMap<String, String>>>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl Http01Handler {
|
|
13
|
+
pub fn new() -> Self {
|
|
14
|
+
Self::default()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn add_challenge(&self, token: String, key_authorization: String) {
|
|
18
|
+
self.challenges.write().insert(token, key_authorization);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn remove_challenge(&self, token: &str) {
|
|
22
|
+
self.challenges.write().remove(token);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pub fn handle_challenge_request(&self, path: &str) -> Option<String> {
|
|
26
|
+
let token = path.strip_prefix(ACME_CHALLENGE_PREFIX)?;
|
|
27
|
+
if token.is_empty()
|
|
28
|
+
|| !token
|
|
29
|
+
.chars()
|
|
30
|
+
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
|
31
|
+
{
|
|
32
|
+
return None;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.challenges.read().get(token).cloned()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[cfg(test)]
|
|
40
|
+
mod tests {
|
|
41
|
+
use super::Http01Handler;
|
|
42
|
+
|
|
43
|
+
#[test]
|
|
44
|
+
fn serves_registered_key_authorization() {
|
|
45
|
+
let handler = Http01Handler::new();
|
|
46
|
+
handler.add_challenge("token_123".to_string(), "token_123.thumbprint".to_string());
|
|
47
|
+
|
|
48
|
+
assert_eq!(
|
|
49
|
+
handler.handle_challenge_request("/.well-known/acme-challenge/token_123"),
|
|
50
|
+
Some("token_123.thumbprint".to_string())
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#[test]
|
|
55
|
+
fn rejects_invalid_paths_and_tokens() {
|
|
56
|
+
let handler = Http01Handler::new();
|
|
57
|
+
handler.add_challenge("token_123".to_string(), "token_123.thumbprint".to_string());
|
|
58
|
+
|
|
59
|
+
assert_eq!(handler.handle_challenge_request("/not-acme"), None);
|
|
60
|
+
assert_eq!(
|
|
61
|
+
handler.handle_challenge_request("/.well-known/acme-challenge/"),
|
|
62
|
+
None
|
|
63
|
+
);
|
|
64
|
+
assert_eq!(
|
|
65
|
+
handler.handle_challenge_request("/.well-known/acme-challenge/invalid token"),
|
|
66
|
+
None
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[test]
|
|
71
|
+
fn removes_tokens() {
|
|
72
|
+
let handler = Http01Handler::new();
|
|
73
|
+
handler.add_challenge("token_123".to_string(), "token_123.thumbprint".to_string());
|
|
74
|
+
handler.remove_challenge("token_123");
|
|
75
|
+
|
|
76
|
+
assert_eq!(
|
|
77
|
+
handler.handle_challenge_request("/.well-known/acme-challenge/token_123"),
|
|
78
|
+
None
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -30,7 +30,9 @@ pub(crate) async fn https(
|
|
|
30
30
|
let client = reqwest::ClientBuilder::new()
|
|
31
31
|
.use_preconfigured_tls(client_config.clone())
|
|
32
32
|
.build()?;
|
|
33
|
-
let mut request = client
|
|
33
|
+
let mut request = client
|
|
34
|
+
.request(method, url.as_ref())
|
|
35
|
+
.header("User-Agent", concat!("itsi-acme/", env!("CARGO_PKG_VERSION")));
|
|
34
36
|
if let Some(body) = body {
|
|
35
37
|
request = request
|
|
36
38
|
.body(body)
|
|
@@ -53,11 +53,15 @@ pub(crate) fn key_authorization_sha256(
|
|
|
53
53
|
key: &EcdsaKeyPair,
|
|
54
54
|
token: &str,
|
|
55
55
|
) -> Result<Digest, JoseError> {
|
|
56
|
-
let
|
|
57
|
-
let key_authorization = format!("{}.{}", token, jwk.thumb_sha256_base64()?);
|
|
56
|
+
let key_authorization = key_authorization(key, token)?;
|
|
58
57
|
Ok(digest(&SHA256, key_authorization.as_bytes()))
|
|
59
58
|
}
|
|
60
59
|
|
|
60
|
+
pub(crate) fn key_authorization(key: &EcdsaKeyPair, token: &str) -> Result<String, JoseError> {
|
|
61
|
+
let jwk = Jwk::new(key);
|
|
62
|
+
Ok(format!("{}.{}", token, jwk.thumb_sha256_base64()?))
|
|
63
|
+
}
|
|
64
|
+
|
|
61
65
|
#[derive(Serialize)]
|
|
62
66
|
pub(crate) struct Body {
|
|
63
67
|
protected: String,
|
data/crates/itsi_acme/src/lib.rs
CHANGED
|
@@ -126,6 +126,7 @@ pub mod axum;
|
|
|
126
126
|
mod cache;
|
|
127
127
|
pub mod caches;
|
|
128
128
|
mod config;
|
|
129
|
+
mod http_challenge;
|
|
129
130
|
mod https_helper;
|
|
130
131
|
mod incoming;
|
|
131
132
|
mod jose;
|
|
@@ -137,6 +138,7 @@ pub use tokio_rustls;
|
|
|
137
138
|
pub use acceptor::*;
|
|
138
139
|
pub use cache::*;
|
|
139
140
|
pub use config::*;
|
|
141
|
+
pub use http_challenge::*;
|
|
140
142
|
pub use incoming::*;
|
|
141
143
|
pub use resolver::*;
|
|
142
144
|
pub use state::*;
|
|
@@ -13,24 +13,40 @@ pub struct ResolvesServerCertAcme {
|
|
|
13
13
|
#[derive(Debug)]
|
|
14
14
|
struct Inner {
|
|
15
15
|
cert: Option<Arc<CertifiedKey>>,
|
|
16
|
+
certs: BTreeMap<String, Arc<CertifiedKey>>,
|
|
16
17
|
auth_keys: BTreeMap<String, Arc<CertifiedKey>>,
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
impl ResolvesServerCertAcme {
|
|
20
|
-
pub
|
|
21
|
+
pub fn new() -> Arc<Self> {
|
|
21
22
|
Arc::new(Self {
|
|
22
23
|
inner: Mutex::new(Inner {
|
|
23
24
|
cert: None,
|
|
25
|
+
certs: Default::default(),
|
|
24
26
|
auth_keys: Default::default(),
|
|
25
27
|
}),
|
|
26
28
|
})
|
|
27
29
|
}
|
|
28
|
-
pub
|
|
30
|
+
pub fn set_cert(&self, cert: Arc<CertifiedKey>) {
|
|
29
31
|
self.inner.lock().unwrap().cert = Some(cert);
|
|
30
32
|
}
|
|
31
|
-
pub
|
|
33
|
+
pub fn set_cert_for_domain(&self, domain: String, cert: Arc<CertifiedKey>) {
|
|
34
|
+
let mut inner = self.inner.lock().unwrap();
|
|
35
|
+
if inner.cert.is_none() {
|
|
36
|
+
inner.cert = Some(cert.clone());
|
|
37
|
+
}
|
|
38
|
+
inner.certs.insert(domain, cert);
|
|
39
|
+
}
|
|
40
|
+
pub fn remove_cert_for_domain(&self, domain: &str) {
|
|
41
|
+
self.inner.lock().unwrap().certs.remove(domain);
|
|
42
|
+
}
|
|
43
|
+
pub fn set_auth_key(&self, domain: String, cert: Arc<CertifiedKey>) {
|
|
32
44
|
self.inner.lock().unwrap().auth_keys.insert(domain, cert);
|
|
33
45
|
}
|
|
46
|
+
|
|
47
|
+
pub fn remove_auth_key(&self, domain: &str) {
|
|
48
|
+
self.inner.lock().unwrap().auth_keys.remove(domain);
|
|
49
|
+
}
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
impl ResolvesServerCert for ResolvesServerCertAcme {
|
|
@@ -53,7 +69,14 @@ impl ResolvesServerCert for ResolvesServerCertAcme {
|
|
|
53
69
|
}
|
|
54
70
|
}
|
|
55
71
|
} else {
|
|
56
|
-
self.inner.lock().unwrap()
|
|
72
|
+
let inner = self.inner.lock().unwrap();
|
|
73
|
+
match client_hello.server_name() {
|
|
74
|
+
Some(domain) => {
|
|
75
|
+
let domain = AsRef::<str>::as_ref(&domain);
|
|
76
|
+
inner.certs.get(domain).cloned().or_else(|| inner.cert.clone())
|
|
77
|
+
}
|
|
78
|
+
None => inner.cert.clone(),
|
|
79
|
+
}
|
|
57
80
|
}
|
|
58
81
|
}
|
|
59
82
|
}
|