Honor proxied_domains' https_disabled when doing https redirects

This commit is contained in:
Brian Picciano 2023-08-25 19:01:30 +02:00
parent 0b5b2cb3f3
commit 6cabb9b742

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::{collections, future, net, sync}; use std::{collections, future, net, sync};
use crate::error::unexpected; use crate::error::unexpected::{self, Mappable};
use crate::{domain, service, task_stack}; use crate::{domain, service, task_stack};
#[derive(Serialize)] #[derive(Serialize)]
@ -157,6 +157,39 @@ impl Service {
) )
} }
fn https_redirect(&self, domain: domain::Name, req: Request<Body>) -> Response<Body> {
let https_addr = self.config.http.https_addr.unwrap();
(|| {
let mut uri_parts = http::uri::Parts::default();
uri_parts.scheme = Some(http::uri::Scheme::HTTPS);
uri_parts.authority = Some(
http::uri::Authority::from_maybe_shared(format!(
"{}:{}",
&domain,
https_addr.port()
))
.or_unexpected_while("constructing authority")?,
);
uri_parts.path_and_query = req.uri().path_and_query().cloned();
let uri: http::uri::Uri = uri_parts
.try_into()
.or_unexpected_while("constructing new URI")?;
Response::builder()
.status(http::status::StatusCode::PERMANENT_REDIRECT)
.header("Location", uri.to_string())
.body(Body::empty())
.or_unexpected_while("building redirect")
})()
.unwrap_or_else(|err| {
self.internal_error(
format!("failed to redirect from {} to https: {}", req.uri(), err).as_str(),
)
})
}
fn render_page<T>(&self, name: &str, data: T) -> Response<Body> fn render_page<T>(&self, name: &str, data: T) -> Response<Body>
where where
T: Serialize, T: Serialize,
@ -504,51 +537,40 @@ impl Service {
} }
} }
// If HTTPS is enabled then only .well-known endpoints are allowed over HTTP (because they // We only allow HTTP requests when HTTPS is enabled in specific cases:
// require it). Otherwise we redirect all HTTP requests to HTTPS. // - /.well-known urls
if self.config.http.https_addr.is_some() && !req_is_https { // - proxied domains with https_disabled set on them
let https_addr = self.config.http.https_addr.unwrap(); // everything else must use https if possible.
let https_upgradable = self.config.http.https_addr.is_some() && !req_is_https;
let mut uri_parts = http::uri::Parts::default();
uri_parts.scheme = Some(http::uri::Scheme::HTTPS);
uri_parts.authority = Some(
http::uri::Authority::from_maybe_shared(format!(
"{}:{}",
&domain,
https_addr.port()
))
.unwrap(),
);
uri_parts.path_and_query = req.uri().path_and_query().cloned();
let uri: http::uri::Uri = uri_parts.try_into().unwrap();
return Response::builder()
.status(http::status::StatusCode::PERMANENT_REDIRECT)
.header("Location", uri.to_string())
.body(Body::empty())
.unwrap();
}
if let Some(config) = self.proxied_domains.get(&domain) { if let Some(config) = self.proxied_domains.get(&domain) {
if let Some(ref http_url) = config.http_url { if config.http_url.is_none() {
return service::http::proxy::serve_http_request(
http_url.original_url.as_str(),
&config.http_request_headers.0,
client_ip,
req,
req_is_https,
)
.await
.unwrap_or_else(|e| {
self.internal_error(
format!("serving {domain} via proxy {}: {e}", http_url.original_url)
.as_str(),
)
});
} else {
return self.render_error_page(404, "Domain not found"); return self.render_error_page(404, "Domain not found");
} }
let http_url = config.http_url.as_ref().unwrap();
if https_upgradable && !config.https_disabled {
return self.https_redirect(domain, req);
}
return service::http::proxy::serve_http_request(
http_url.original_url.as_str(),
&config.http_request_headers.0,
client_ip,
req,
req_is_https,
)
.await
.unwrap_or_else(|e| {
self.internal_error(
format!("serving {domain} via proxy {}: {e}", http_url.original_url).as_str(),
)
});
}
if https_upgradable {
return self.https_redirect(domain, req);
} }
if Some(&domain) == self.interface_domain.as_ref() { if Some(&domain) == self.interface_domain.as_ref() {