v0.34.4
This commit is contained in:
1 parent
1fd81eaf5f
commit
ee174bc283
36 files changed
+908
-183
No files matched your search
@@ -285,6 +285,11 @@ pub fn for_all_frontends(
|
||||
if let Some(id) = &vhost.certificate_id {
|
||||
let hosts = vhost.routing_hosts()?;
|
||||
assign(&hosts, id, "vhost")?;
|
||||
if vhost.allow_certificate_mismatch {
|
||||
for host in hosts {
|
||||
result.forced_mismatches.insert((id.clone(), host));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for redirect in redirects {
|
||||
|
||||
@@ -372,10 +372,15 @@ pub fn compile(settings: &Settings, directory: Option<&Path>) -> Result<Option<A
|
||||
.strip_prefix("*.")
|
||||
.map(|base| format!("rustproxy-wildcard-check.{base}"))
|
||||
.unwrap_or_else(|| host.clone());
|
||||
let mut checker = ResolvesServerCertUsingSni::new();
|
||||
checker
|
||||
.add(&validation_name, key.clone())
|
||||
.with_context(|| format!("certificate does not cover {host}"))?;
|
||||
if !settings.forced_mismatches.contains(&(cert.id.clone(), host.clone())) {
|
||||
let mut checker = ResolvesServerCertUsingSni::new();
|
||||
checker
|
||||
.add(&validation_name, key.clone())
|
||||
.with_context(|| format!("certificate does not cover {host}; explicit Vhost consent is required to override"))?;
|
||||
} else {
|
||||
tracing::warn!(certificate = %cert.id, hostname = %host,
|
||||
"Explicitly serving a TLS certificate without hostname verification; clients may reject it");
|
||||
}
|
||||
names.insert(host, Arc::new(key.clone()));
|
||||
}
|
||||
if settings.default_certificate.as_deref() == Some(cert.id.as_str()) {
|
||||
|
||||
@@ -431,4 +431,46 @@ mod tests {
|
||||
);
|
||||
assert!(certificate_covered_hosts(dir.path(), "../legacy.pem").is_err());
|
||||
}
|
||||
#[test]
|
||||
fn mismatched_named_certificate_requires_explicit_per_vhost_consent() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
initialize_default(dir.path()).unwrap();
|
||||
generate(dir.path(), "named", vec!["right.example.test".into()]).unwrap();
|
||||
let settings = Settings {
|
||||
certificates: vec![Certificate {
|
||||
id: "named".into(),
|
||||
issuer: None,
|
||||
covered_hosts: vec!["right.example.test".into()],
|
||||
hosts: vec![],
|
||||
cert_file: "named.pem".into(),
|
||||
key_file: "named.key".into(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
let mut config = crate::config::demo_config("127.0.0.1:9000".parse().unwrap());
|
||||
let vhost = &mut config.vhosts[0];
|
||||
vhost.certificate_id = Some("named".into());
|
||||
vhost.frontend.hosts = vec!["other.example.test".into()];
|
||||
let blocked = for_vhosts(&settings, &config.vhosts).unwrap();
|
||||
assert!(compile(&blocked, Some(dir.path())).is_err());
|
||||
assert!(blocked.forced_mismatches.is_empty());
|
||||
vhost.allow_certificate_mismatch = true;
|
||||
let forced = for_vhosts(&settings, &config.vhosts).unwrap();
|
||||
assert!(forced.forced_mismatches.contains(&("named".into(), "other.example.test".into())));
|
||||
assert!(compile(&forced, Some(dir.path())).is_ok());
|
||||
let saved = serde_json::to_value(&forced).unwrap();
|
||||
assert!(saved.get("forced_mismatches").is_none());
|
||||
let restored: Settings = serde_json::from_value(saved).unwrap();
|
||||
assert!(restored.forced_mismatches.is_empty());
|
||||
assert!(compile(&restored, Some(dir.path())).is_err());
|
||||
|
||||
let mut other = config.vhosts[0].clone();
|
||||
other.id = "another".into();
|
||||
other.frontend.hosts = vec!["third.example.test".into()];
|
||||
other.allow_certificate_mismatch = false;
|
||||
config.vhosts.push(other);
|
||||
let partial = for_vhosts(&settings, &config.vhosts).unwrap();
|
||||
assert!(compile(&partial, Some(dir.path())).is_err());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,9 @@ pub struct Settings {
|
||||
#[serde(default)]
|
||||
pub certificates: Vec<Certificate>,
|
||||
pub policy: ProtocolPolicy,
|
||||
/// Derived from explicitly confirmed Vhosts; never accepted from TLS JSON.
|
||||
#[serde(skip)]
|
||||
pub forced_mismatches: HashSet<(String, String)>,
|
||||
}
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
@@ -32,6 +35,7 @@ impl Default for Settings {
|
||||
default_certificate: None,
|
||||
certificates: Vec::new(),
|
||||
policy: ProtocolPolicy::default(),
|
||||
forced_mismatches: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user