v0.34.6
This commit is contained in:
1 parent
e037190c14
commit
5cb3b37627
37 files changed
+1031
-138
No files matched your search
+39
-15
@@ -88,10 +88,7 @@ async fn forward_request(
|
||||
.get::<Arc<crate::config::Snapshot>>()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| state.config.load_full());
|
||||
let internal_dispatch = request
|
||||
.extensions()
|
||||
.get::<InternalFallbackRoute>()
|
||||
.cloned();
|
||||
let internal_dispatch = request.extensions().get::<InternalFallbackRoute>().cloned();
|
||||
// Retain the original URI's shared backing bytes independently of `request`.
|
||||
// A normalized, already-safe path can then be borrowed for the entire
|
||||
// forwarding operation, even after the request URI/headers are rewritten.
|
||||
@@ -115,6 +112,14 @@ async fn forward_request(
|
||||
// rerunning the observation middleware.
|
||||
snapshot.frontend(&host, local.0, tls)
|
||||
};
|
||||
// Host-level denylist: before cache, custom locations, redirects, local
|
||||
// Web Server/PHP-FPM, or any origin connection. No request body is parsed.
|
||||
if frontend.is_some_and(|item| item.content_type_policy.blocks(request.headers())) {
|
||||
return error(
|
||||
StatusCode::FORBIDDEN,
|
||||
"RustProxy: request Content-Type denied by vhost policy\n",
|
||||
);
|
||||
}
|
||||
if snapshot.user_agent_blocking.blocks(request.headers())
|
||||
|| frontend.is_some_and(|frontend| frontend.user_agent_blocking.blocks(request.headers()))
|
||||
{
|
||||
@@ -181,9 +186,9 @@ async fn forward_request(
|
||||
return response;
|
||||
}
|
||||
if !tls {
|
||||
if let Some(response) = frontend.and_then(|frontend| {
|
||||
force_ssl_redirect(frontend, &host, request.uri())
|
||||
}) {
|
||||
if let Some(response) =
|
||||
frontend.and_then(|frontend| force_ssl_redirect(frontend, &host, request.uri()))
|
||||
{
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +201,9 @@ async fn forward_request(
|
||||
&original_host,
|
||||
request.uri(),
|
||||
frontend.is_some_and(|frontend| frontend.tinyauth.is_some()),
|
||||
request.extensions().get::<Arc<crate::forwarding::PeerFormatting>>()
|
||||
request
|
||||
.extensions()
|
||||
.get::<Arc<crate::forwarding::PeerFormatting>>()
|
||||
.map(Arc::as_ref),
|
||||
host_already_validated,
|
||||
) {
|
||||
@@ -237,7 +244,10 @@ async fn forward_request(
|
||||
let mut country: Option<String> = None;
|
||||
if let Some(frontend) = frontend {
|
||||
if capture_country
|
||||
|| frontend.profile_policies.iter().any(|profile| profile.countries.needs_geoip())
|
||||
|| frontend
|
||||
.profile_policies
|
||||
.iter()
|
||||
.any(|profile| profile.countries.needs_geoip())
|
||||
|| frontend.rate_limit.needs_geoip()
|
||||
{
|
||||
country = cloudflare_country
|
||||
@@ -271,7 +281,10 @@ async fn forward_request(
|
||||
&frontend.id,
|
||||
context.client_ip,
|
||||
country.as_deref(),
|
||||
request.headers().get(http::header::USER_AGENT).and_then(|value| value.to_str().ok()),
|
||||
request
|
||||
.headers()
|
||||
.get(http::header::USER_AGENT)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
&frontend.rate_limit,
|
||||
) {
|
||||
let mut response = error(
|
||||
@@ -295,7 +308,17 @@ async fn forward_request(
|
||||
);
|
||||
}
|
||||
if cache_http_invalidation_method(request.method()) {
|
||||
return cache_http_invalidation(&state, &snapshot, frontend, &host, request, context.client_ip, peer.0.ip(), context.scheme).await;
|
||||
return cache_http_invalidation(
|
||||
&state,
|
||||
&snapshot,
|
||||
frontend,
|
||||
&host,
|
||||
request,
|
||||
context.client_ip,
|
||||
peer.0.ip(),
|
||||
context.scheme,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if let Some((_redirect, rule)) = redirect {
|
||||
let authority = original_host.to_str().unwrap_or(&host);
|
||||
@@ -434,10 +457,11 @@ async fn forward_request(
|
||||
)
|
||||
});
|
||||
};
|
||||
let fallback_body = match prepare_error_fallback_body(&mut request, route.error_fallback.is_some()).await {
|
||||
Ok(body) => body,
|
||||
Err(response) => return response,
|
||||
};
|
||||
let fallback_body =
|
||||
match prepare_error_fallback_body(&mut request, route.error_fallback.is_some()).await {
|
||||
Ok(body) => body,
|
||||
Err(response) => return response,
|
||||
};
|
||||
let (
|
||||
backend,
|
||||
backend_lease,
|
||||
|
||||
@@ -86,6 +86,45 @@ mod cache_fast_path_tests {
|
||||
config
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn content_type_policy_denies_before_local_web_server_and_cache_and_updates_live() {
|
||||
let directory = TempDir::new().unwrap();
|
||||
let mut config = web_config(&directory, true);
|
||||
config.vhosts[0].content_type_policy = crate::vhost::ContentTypePolicy {
|
||||
enabled: true,
|
||||
blocked: vec!["multipart/form-data".into(), "image/*".into()],
|
||||
};
|
||||
let fixture = Fixture::new(config).await;
|
||||
// Warm the cache before testing a header-denied cache hit.
|
||||
assert_eq!(fixture.get("/style.css", &[]).await.status, StatusCode::OK);
|
||||
assert_eq!(fixture.get("/style.css", &[]).await.status, StatusCode::OK);
|
||||
assert_eq!(fixture.get("/style.css", &[("content-type", "multipart/form-data; boundary=example")]).await.status, StatusCode::FORBIDDEN);
|
||||
assert_eq!(fixture.get("/style.css", &[("content-type", "IMAGE/PNG")]).await.status, StatusCode::FORBIDDEN);
|
||||
assert_eq!(fixture.send(Method::POST, "/style.css", &[("content-type", "multipart/form-data; boundary=x")], Body::from("sample")).await.status, StatusCode::FORBIDDEN);
|
||||
assert_eq!(fixture.get("/style.css", &[("content-type", "text/plain")]).await.status, StatusCode::OK);
|
||||
assert_eq!(fixture.get("/style.css", &[("content-type", "application/json, multipart/form-data")]).await.status, StatusCode::FORBIDDEN);
|
||||
fixture.change(|config| config.vhosts[0].content_type_policy.enabled = false).await;
|
||||
assert_eq!(fixture.get("/style.css", &[("content-type", "multipart/form-data")]).await.status, StatusCode::OK);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn content_type_policy_denies_before_reverse_proxy_upstream_request() {
|
||||
let origin = origin().await;
|
||||
let mut config = config_for(origin.addr, false);
|
||||
config.vhosts[0].content_type_policy = crate::vhost::ContentTypePolicy {
|
||||
enabled: true, blocked: vec!["multipart/form-data".into()],
|
||||
};
|
||||
let fixture = Fixture::new(config).await;
|
||||
let blocked = fixture.send(Method::POST, "/", &[("content-type", "Multipart/Form-Data; boundary=abc")], Body::from("--abc\r\n")).await;
|
||||
assert_eq!(blocked.status, StatusCode::FORBIDDEN);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 0);
|
||||
let allowed = fixture.get("/", &[("content-type", "application/json")]).await;
|
||||
assert_eq!(allowed.status, StatusCode::OK);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 1);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn web_cache_disabled_keeps_get_head_range_conditionals_and_observability() {
|
||||
let directory = TempDir::new().unwrap();
|
||||
|
||||
Reference in new issue
Block a user