diff --git a/linkerd/app/admin/src/stack.rs b/linkerd/app/admin/src/stack.rs index 3eb65ad6e5..c379d2143e 100644 --- a/linkerd/app/admin/src/stack.rs +++ b/linkerd/app/admin/src/stack.rs @@ -114,7 +114,7 @@ impl Config { .to_layer::(), ) .push(classify::NewClassify::layer_default()) - .push_map_target(|(permit, http)| Permitted { permit, http }) + .push_map_target(Permitted::from) .push(inbound::policy::NewHttpPolicy::layer( metrics.http_authz.clone(), )) @@ -281,6 +281,17 @@ impl Param for Permitted { } } +impl From> for Permitted { + fn from( + inbound::policy::Permitted { permit, target }: inbound::policy::Permitted, + ) -> Self { + Self { + permit, + http: target, + } + } +} + // === TlsParams === impl ExtractParam for TlsParams { diff --git a/linkerd/app/gateway/src/http.rs b/linkerd/app/gateway/src/http.rs index 3de47f565f..e4aff65e41 100644 --- a/linkerd/app/gateway/src/http.rs +++ b/linkerd/app/gateway/src/http.rs @@ -1,5 +1,5 @@ use super::Gateway; -use inbound::{GatewayAddr, GatewayDomainInvalid}; +use inbound::{policy::Permitted, GatewayAddr, GatewayDomainInvalid}; use linkerd_app_core::{ metrics::ServerLabel, profiles, @@ -81,50 +81,57 @@ impl Gateway { R: Resolve, R::Resolution: Unpin, { - let http = self - .outbound - .clone() - .with_stack(inner) - .push_http_cached(resolve) - .into_stack() - // Discard `T` and its associated client-specific metadata. - .push_map_target(Target::discard_parent) - .push(svc::ArcNewService::layer()) - // Add headers to prevent loops. - .push(NewHttpGateway::layer( - self.inbound.identity().local_id().clone(), - )) - .push_on_service(svc::LoadShed::layer()) - .lift_new() - .push(svc::ArcNewService::layer()) - // After protocol-downgrade, we need to build an inner stack for - // each request-level HTTP version. - .push(svc::NewOneshotRoute::layer_via(|t: &Target| { - ByRequestVersion(t.clone()) - })) - // Only permit gateway traffic to endpoints for which we have - // discovery information. - .push_filter(|(_, parent): (_, T)| -> Result<_, GatewayDomainInvalid> { - let routes = { - let mut profile = - svc::Param::>>::param(&parent) + let http = + self.outbound + .clone() + .with_stack(inner) + .push_http_cached(resolve) + .into_stack() + // Discard `T` and its associated client-specific metadata. + .push_map_target(Target::discard_parent) + .push(svc::ArcNewService::layer()) + // Add headers to prevent loops. + .push(NewHttpGateway::layer( + self.inbound.identity().local_id().clone(), + )) + .push_on_service(svc::LoadShed::layer()) + .lift_new() + .push(svc::ArcNewService::layer()) + // After protocol-downgrade, we need to build an inner stack for + // each request-level HTTP version. + .push(svc::NewOneshotRoute::layer_via(|t: &Target| { + ByRequestVersion(t.clone()) + })) + // Only permit gateway traffic to endpoints for which we have + // discovery information. + .push_filter( + |Permitted { + permit: _, + target: parent, + }: Permitted| + -> Result<_, GatewayDomainInvalid> { + let routes = { + let mut profile = svc::Param::< + Option>, + >::param(&parent) .ok_or(GatewayDomainInvalid)?; - let init = - mk_routes(&profile.borrow_and_update()).ok_or(GatewayDomainInvalid)?; - outbound::http::spawn_routes(profile, init, mk_routes) - }; - - Ok(Target { - routes, - addr: parent.param(), - version: parent.param(), - parent, - }) - }) - .push(svc::ArcNewService::layer()) - // Authorize requests to the gateway. - .push(self.inbound.authorize_http()) - .arc_new_clone_http(); + let init = mk_routes(&profile.borrow_and_update()) + .ok_or(GatewayDomainInvalid)?; + outbound::http::spawn_routes(profile, init, mk_routes) + }; + + Ok(Target { + routes, + addr: parent.param(), + version: parent.param(), + parent, + }) + }, + ) + .push(svc::ArcNewService::layer()) + // Authorize requests to the gateway. + .push(self.inbound.authorize_http()) + .arc_new_clone_http(); self.inbound .clone() diff --git a/linkerd/app/inbound/src/http/router.rs b/linkerd/app/inbound/src/http/router.rs index 1705ef6d44..1555322e43 100644 --- a/linkerd/app/inbound/src/http/router.rs +++ b/linkerd/app/inbound/src/http/router.rs @@ -236,12 +236,12 @@ impl Inbound { .push_on_service(svc::LoadShed::layer()) .push(svc::NewMapErr::layer_from_target::()) .lift_new() - .check_new_new::<(policy::HttpRoutePermit, T), Logical>() + .check_new_new::, Logical>() .push(svc::ArcNewService::layer()) - .push(svc::NewOneshotRoute::layer_via(|(permit, t): &(policy::HttpRoutePermit, T)| { - LogicalPerRequest::from((permit.clone(), t.clone())) + .push(svc::NewOneshotRoute::layer_via(|t: &policy::Permitted| { + LogicalPerRequest::from(t) })) - .check_new_service::<(policy::HttpRoutePermit, T), http::Request>() + .check_new_service::, http::Request>() .push(svc::ArcNewService::layer()) .push(policy::NewHttpPolicy::layer(rt.metrics.http_authz.clone())) // Used by tap. @@ -254,12 +254,12 @@ impl Inbound { // === impl LogicalPerRequest === -impl From<(policy::HttpRoutePermit, T)> for LogicalPerRequest +impl<'a, T> From<&'a policy::Permitted> for LogicalPerRequest where T: Param>, T: Param, { - fn from((permit, t): (policy::HttpRoutePermit, T)) -> Self { + fn from(policy::Permitted { permit, target }: &'a policy::Permitted) -> Self { let labels = [ ("srv", &permit.labels.route.server.0), ("route", &permit.labels.route.route), @@ -276,9 +276,9 @@ where .collect::>(); Self { - server: t.param(), - tls: t.param(), - permit, + server: target.param(), + tls: target.param(), + permit: permit.clone(), labels: labels.into(), } } diff --git a/linkerd/app/inbound/src/policy.rs b/linkerd/app/inbound/src/policy.rs index 22b09ddbee..62b36f0d0f 100644 --- a/linkerd/app/inbound/src/policy.rs +++ b/linkerd/app/inbound/src/policy.rs @@ -12,7 +12,7 @@ pub use self::{ config::Config, http::{ HttpInvalidPolicy, HttpRouteInvalidRedirect, HttpRouteNotFound, HttpRouteRedirect, - HttpRouteUnauthorized, NewHttpPolicy, + HttpRouteUnauthorized, NewHttpPolicy, Permitted, }, tcp::NewTcpPolicy, }; diff --git a/linkerd/app/inbound/src/policy/http.rs b/linkerd/app/inbound/src/policy/http.rs index e37e268dea..429cc2f53a 100644 --- a/linkerd/app/inbound/src/policy/http.rs +++ b/linkerd/app/inbound/src/policy/http.rs @@ -46,6 +46,13 @@ struct ConnectionMeta { tls: tls::ConditionalServerTls, } +/// A `T`-typed target with policy enforced by a [`NewHttpPolicy`] layer. +#[derive(Debug)] +pub struct Permitted { + pub permit: HttpRoutePermit, + pub target: T, +} + #[derive(Debug, thiserror::Error)] #[error("no route found for request")] pub struct HttpRouteNotFound(()); @@ -138,7 +145,7 @@ macro_rules! try_fut { impl svc::Service<::http::Request> for HttpPolicyService where T: Clone, - N: svc::NewService<(HttpRoutePermit, T), Service = S>, + N: svc::NewService, Service = S>, S: svc::Service<::http::Request>, S::Error: Into, { @@ -175,7 +182,10 @@ where future::Either::Left( self.inner - .new_service((permit, self.target.clone())) + .new_service(Permitted { + permit, + target: self.target.clone(), + }) .oneshot(req) .err_into::(), ) @@ -383,3 +393,17 @@ fn apply_grpc_filters(route: &grpc::Policy, req: &mut ::http::Request) -> Ok(()) } + +// === impl Permitted === + +/// An authorized `T`-typed target can produce `P`-typed parameters. +impl svc::Param

for Permitted +where + T: svc::Param

, +{ + fn param(&self) -> P { + let Self { target, .. } = self; + + target.param() + } +} diff --git a/linkerd/app/inbound/src/policy/http/tests.rs b/linkerd/app/inbound/src/policy/http/tests.rs index e3a14a0240..f284beddfe 100644 --- a/linkerd/app/inbound/src/policy/http/tests.rs +++ b/linkerd/app/inbound/src/policy/http/tests.rs @@ -39,7 +39,7 @@ macro_rules! new_svc { policy, connection: $conn, metrics: HttpAuthzMetrics::default(), - inner: |(permit, _): (HttpRoutePermit, ())| { + inner: |Permitted { permit, target: () }: Permitted<()>| { let f = $rsp; svc::mk(move |req: ::http::Request| { futures::future::ready((f)(permit.clone(), req))