diff --git a/Cargo.lock b/Cargo.lock index 1f7e5c6f2..3ebe982ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5360,6 +5360,7 @@ dependencies = [ "tokio", "tower", "tower-http 0.4.0", + "tower-sanitize-path", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -6365,6 +6366,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +[[package]] +name = "tower-sanitize-path" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4accf4be86b13057d30cd97f606c707b6ec8df01c2e91ee735f89de8216f21" +dependencies = [ + "http", + "tower-layer", + "tower-service", + "url-escape", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -6677,6 +6690,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "url-escape" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218" +dependencies = [ + "percent-encoding", +] + [[package]] name = "urlencoding" version = "2.1.2" diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index cdd932ff5..ba25c1323 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -50,6 +50,7 @@ utoipa = { workspace = true } utoipa-swagger-ui = { workspace = true } uuid = { workspace = true, features = ["v4"] } x509-parser = "0.14.0" +tower-sanitize-path = "0.1.2" [dependencies.shuttle-common] workspace = true diff --git a/gateway/src/proxy.rs b/gateway/src/proxy.rs index 986ed46d6..eab26e649 100644 --- a/gateway/src/proxy.rs +++ b/gateway/src/proxy.rs @@ -25,6 +25,7 @@ use opentelemetry_http::HeaderInjector; use shuttle_common::backends::headers::XShuttleProject; use tokio::sync::mpsc::Sender; use tower::{Service, ServiceBuilder}; +use tower_sanitize_path::SanitizePath; use tracing::{debug_span, error, field, trace}; use tracing_opentelemetry::OpenTelemetrySpanExt; @@ -84,6 +85,18 @@ impl<'r> AsResponderTo<&'r AddrStream> for UserProxy { } } +impl AsResponderTo for SanitizePath +where + S: AsResponderTo + Clone, +{ + fn as_responder_to(&self, req: R) -> Self { + let responder = self.clone(); + responder.inner().as_responder_to(req); + + responder + } +} + impl UserProxy { async fn proxy( self, @@ -300,12 +313,13 @@ impl UserServiceBuilder { .user_binds_to .expect("a socket address to bind to is required"); - let user_proxy = UserProxy { + let user_proxy = SanitizePath::sanitize_paths(UserProxy { gateway: service.clone(), task_sender, remote_addr: "127.0.0.1:80".parse().unwrap(), public: public.clone(), - }; + }) + .into_make_service(); let bouncer = self.bouncer_binds_to.as_ref().map(|_| Bouncer { gateway: service.clone(), @@ -335,7 +349,7 @@ impl UserServiceBuilder { let user_with_tls = axum_server::Server::bind(user_binds_to) .acceptor(tls_acceptor) - .serve(user_proxy.into_make_service()) + .serve(user_proxy) .map(|handle| ("user proxy (with TLS)", handle)) .boxed(); futs.push(user_with_tls); @@ -351,7 +365,7 @@ impl UserServiceBuilder { } let user_without_tls = axum_server::Server::bind(user_binds_to) - .serve(user_proxy.into_make_service()) + .serve(user_proxy) .map(|handle| ("user proxy (no TLS)", handle)) .boxed(); futs.push(user_without_tls);