This commit is contained in:
Mateusz Gruszczyński committed 2026-10-10 00:18:06 +02:00
1 parent ad95bfb050
commit 1431dd47f0
62 files changed
+1360 -198

No files matched your search

+19
View File
@@ -57,6 +57,10 @@ pub fn compile_with_active(
}
ensure!(config.web_services.len() <= 128, "too many Web Services (max 128)");
let mut web_services = HashMap::new();
// PHP-FPM can retain FastCGI PHP_VALUE/PHP_ADMIN_VALUE directives in reused
// workers. Different directive sets on one socket can leak to the next vhost.
// Report unsafe legacy configurations but keep them loadable for migration.
let mut php_endpoint_policies: HashMap<String, (String, std::collections::BTreeMap<String, String>, std::collections::BTreeMap<String, String>)> = HashMap::new();
for service in &mut config.web_services {
service.validate().with_context(|| format!("Web Service {}", service.id))?;
// A profile changes effective runtime settings, not the saved standalone
@@ -68,6 +72,21 @@ pub fn compile_with_active(
})?.clone();
effective.validate().with_context(|| format!("Web Service {} PHP-FPM profile {}", service.id, id))?;
}
if effective.php.enabled {
let php = &effective.php;
if let Some((owner, values, admin_values)) = php_endpoint_policies.get(&php.endpoint) {
if values != &php.php_values || admin_values != &php.php_admin_values {
tracing::warn!(
service = %effective.id, other_service = %owner, endpoint = %php.endpoint,
"PHP-FPM endpoint is shared by Web Services with different PHP_VALUE/PHP_ADMIN_VALUE settings; FPM worker settings can leak across requests. Use distinct PHP-FPM pools/sockets (especially for open_basedir)."
);
}
} else {
php_endpoint_policies.insert(php.endpoint.clone(), (
effective.id.clone(), php.php_values.clone(), php.php_admin_values.clone(),
));
}
}
let compiled = Arc::new(effective.compile().with_context(|| format!("Web Service {}", service.id))?);
ensure!(web_services.insert(service.id.clone(), compiled).is_none(), "duplicate Web Service id");
}
+4 -1
View File
@@ -122,7 +122,10 @@ fn compile_routing(
// Blank health-check Host means the first exact DNS name of the vhost.
// This is important when the backend URL is an IP shared by several vhosts.
if pool_settings.health_check.host.is_none()
&& pool_settings.health_check.mode == crate::balance::HealthMode::Http
&& (pool_settings.health_check.mode == crate::balance::HealthMode::Http
|| host.backends.iter().any(|member| member.health_check.as_ref()
.is_some_and(|health| health.mode == crate::balance::HealthMode::Http
&& health.host.is_none())))
{
pool_settings.health_check.host = host
.frontend
+25
View File
@@ -6,6 +6,19 @@ fn path_boundaries() {
assert!(prefix_matches("/", "/anything"));
}
#[test]
fn invalid_per_backend_health_policy_rejected_without_global_override() {
let mut c = demo_config("127.0.0.1:9000".parse().unwrap());
c.vhosts[0].backends[0].health_check = Some(crate::balance::HealthCheck {
mode: crate::balance::HealthMode::Http,
interval_ms: 300,
timeout_ms: 400,
..Default::default()
});
assert!(compile(c.clone(), 1, &policy()).is_err());
c.vhosts[0].backends[0].health_check.as_mut().unwrap().timeout_ms = 200;
assert!(compile(c, 1, &policy()).is_ok());
}
#[test]
fn private_requires_opt_in_and_metadata_always_denied() {
let mut c = demo_config("127.0.0.1:9000".parse().unwrap());
assert!(compile(c.clone(), 1, &policy()).is_ok());
@@ -279,6 +292,18 @@ fn http_health_check_defaults_host_to_first_exact_vhost_dns_name() {
);
}
#[test]
fn per_member_http_health_inherits_vhost_dns_even_when_global_health_is_tcp() {
let mut c = demo_config("127.0.0.1:9000".parse().unwrap());
c.vhosts[0].frontend.hosts = vec!["app.example.test".into()];
assert_eq!(c.vhosts[0].load_balancing.health_check.mode, crate::balance::HealthMode::Tcp);
c.vhosts[0].backends[0].health_check = Some(crate::balance::HealthCheck {
mode: crate::balance::HealthMode::Http, path: "/healthz".into(), ..Default::default()
});
let snapshot = compile(c, 1, &policy()).unwrap();
let pool = &snapshot.pools[0];
assert_eq!(pool.settings.health_for(&pool.members[0].definition).host.as_deref(), Some("app.example.test"));
}
#[test]
fn explicit_http_health_check_host_override_wins_over_vhost_hostname() {
let mut c = demo_config("127.0.0.1:9000".parse().unwrap());