Skip to content

Commit c72c5f5

Browse files
authored
Use authority override from metadata (#458)
The destination controller may set a per-endpoint `override_authority` field that sets a new `Host`/`:authority` value to be used on the outbound request. This change introduces an `override_authority` middleware that uses this metadata to modify outbound requests when an override is set.
1 parent b64e4eb commit c72c5f5

File tree

8 files changed

+195
-22
lines changed

8 files changed

+195
-22
lines changed

linkerd/app/inbound/src/endpoint.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ impl connect::ConnectAddr for HttpEndpoint {
5151
}
5252

5353
impl http::settings::HasSettings for HttpEndpoint {
54-
fn http_settings(&self) -> &http::Settings {
55-
&self.settings
54+
fn http_settings(&self) -> http::Settings {
55+
self.settings
5656
}
5757
}
5858

@@ -149,8 +149,8 @@ impl http::normalize_uri::ShouldNormalizeUri for Target {
149149
}
150150

151151
impl http::settings::HasSettings for Target {
152-
fn http_settings(&self) -> &http::Settings {
153-
&self.http_settings
152+
fn http_settings(&self) -> http::Settings {
153+
self.http_settings
154154
}
155155
}
156156

linkerd/app/outbound/src/endpoint.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use crate::http::uri::Authority;
12
use indexmap::IndexMap;
23
use linkerd2_app_core::{
34
dst, metric_labels,
45
metric_labels::{prefix_labels, EndpointLabels},
56
profiles,
67
proxy::{
78
api_resolve::{Metadata, ProtocolHint},
8-
http::{self, identity_from_header},
9+
http::override_authority::CanOverrideAuthority,
10+
http::{self, identity_from_header, Settings},
911
identity,
1012
resolve::map_endpoint::MapEndpoint,
1113
tap,
@@ -107,7 +109,7 @@ impl<T> profiles::OverrideDestination for Target<T> {
107109
}
108110

109111
impl<T: http::settings::HasSettings> http::settings::HasSettings for Target<T> {
110-
fn http_settings(&self) -> &http::Settings {
112+
fn http_settings(&self) -> http::Settings {
111113
self.inner.http_settings()
112114
}
113115
}
@@ -168,12 +170,11 @@ impl HttpEndpoint {
168170
return false;
169171
}
170172

173+
// Look at the original settings, ignoring any authority overrides.
171174
match self.settings {
172175
http::Settings::Http2 => false,
173176
http::Settings::Http1 {
174-
keep_alive: _,
175-
wants_h1_upgrade,
176-
was_absolute_form: _,
177+
wants_h1_upgrade, ..
177178
} => !wants_h1_upgrade,
178179
}
179180
}
@@ -189,7 +190,7 @@ impl std::hash::Hash for HttpEndpoint {
189190
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
190191
self.addr.hash(state);
191192
self.identity.hash(state);
192-
self.settings.hash(state);
193+
http::settings::HasSettings::http_settings(self).hash(state);
193194
// Ignore metadata.
194195
}
195196
}
@@ -207,8 +208,21 @@ impl connect::ConnectAddr for HttpEndpoint {
207208
}
208209

209210
impl http::settings::HasSettings for HttpEndpoint {
210-
fn http_settings(&self) -> &http::Settings {
211-
&self.settings
211+
fn http_settings(&self) -> http::Settings {
212+
match self.settings {
213+
Settings::Http1 {
214+
keep_alive,
215+
wants_h1_upgrade,
216+
was_absolute_form,
217+
} => Settings::Http1 {
218+
keep_alive,
219+
wants_h1_upgrade,
220+
// Always use absolute form when an onverride is present.
221+
was_absolute_form: self.metadata.authority_override().is_some()
222+
|| was_absolute_form,
223+
},
224+
settings => settings,
225+
}
212226
}
213227
}
214228

@@ -283,6 +297,12 @@ impl MapEndpoint<Concrete<http::Settings>, Metadata> for FromMetadata {
283297
}
284298
}
285299

300+
impl CanOverrideAuthority for Target<HttpEndpoint> {
301+
fn override_authority(&self) -> Option<Authority> {
302+
self.inner.metadata.authority_override().cloned()
303+
}
304+
}
305+
286306
impl Into<EndpointLabels> for Target<HttpEndpoint> {
287307
fn into(self) -> EndpointLabels {
288308
use linkerd2_app_core::metric_labels::{Direction, TlsId};

linkerd/app/outbound/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub use self::endpoint::{
99
Concrete, HttpEndpoint, Logical, LogicalPerRequest, Profile, ProfilePerTarget, Target,
1010
TcpEndpoint,
1111
};
12+
use ::http::header::HOST;
1213
use futures::future;
1314
use linkerd2_app_core::{
1415
classify,
@@ -157,6 +158,7 @@ impl Config {
157158
}))
158159
.push(observability.clone())
159160
.push(identity_headers.clone())
161+
.push(http::override_authority::Layer::new(vec![HOST.as_str(), CANONICAL_DST_HEADER]))
160162
// Ensures that the request's URI is in the proper form.
161163
.push(http::normalize_uri::layer())
162164
// Upgrades HTTP/1 requests to be transported over HTTP/2 connections.

linkerd/app/outbound/src/orig_proto_upgrade.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::proxy::http::{orig_proto, settings::Settings};
1+
use crate::proxy::http::{
2+
orig_proto,
3+
settings::{HasSettings, Settings},
4+
};
25
use crate::svc::stack;
36
use crate::{HttpEndpoint, Target};
47
use futures::{try_ready, Future, Poll};
@@ -47,7 +50,7 @@ where
4750
return Either::B(self.inner.new_service(endpoint));
4851
}
4952

50-
let was_absolute = endpoint.inner.settings.was_absolute_form();
53+
let was_absolute = endpoint.http_settings().was_absolute_form();
5154
trace!(
5255
header = %orig_proto::L5D_ORIG_PROTO,
5356
was_absolute,
@@ -75,7 +78,8 @@ where
7578
fn call(&mut self, mut endpoint: Target<HttpEndpoint>) -> Self::Future {
7679
let can_upgrade = endpoint.inner.can_use_orig_proto();
7780

78-
let was_absolute = endpoint.inner.settings.was_absolute_form();
81+
let was_absolute = endpoint.http_settings().was_absolute_form();
82+
7983
if can_upgrade {
8084
trace!(
8185
header = %orig_proto::L5D_ORIG_PROTO,

linkerd/proxy/http/src/client.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,20 +120,21 @@ where
120120
fn call(&mut self, target: T) -> Self::Future {
121121
trace!("Building HTTP client");
122122
let connect = self.connect.clone();
123-
match *target.http_settings() {
123+
match target.http_settings() {
124124
Settings::Http1 {
125125
keep_alive,
126126
wants_h1_upgrade: _,
127127
was_absolute_form,
128128
} => {
129129
let exec =
130130
tokio::executor::DefaultExecutor::current().instrument(info_span!("http1"));
131+
131132
let h1 = hyper::Client::builder()
132133
.executor(exec)
133134
.keep_alive(keep_alive)
134-
// hyper should never try to automatically set the Host
135-
// header, instead always just passing whatever we received.
136-
.set_host(false)
135+
// hyper should only try to automatically
136+
// set the host if the request was in absolute_form
137+
.set_host(was_absolute_form)
137138
.build(HyperConnect::new(connect, target, was_absolute_form));
138139
MakeFuture::Http1(Some(h1))
139140
}

linkerd/proxy/http/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod header_from_target;
1717
pub mod insert;
1818
pub mod normalize_uri;
1919
pub mod orig_proto;
20+
pub mod override_authority;
2021
pub mod settings;
2122
pub mod strip_header;
2223
pub mod timeout;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use super::h1;
2+
use futures::{try_ready, Future, Poll};
3+
use http::{self, header::AsHeaderName, uri::Authority};
4+
use std::fmt;
5+
use tracing::debug;
6+
7+
pub trait CanOverrideAuthority {
8+
fn override_authority(&self) -> Option<Authority>;
9+
}
10+
11+
#[derive(Debug, Clone)]
12+
pub struct Layer<H> {
13+
headers_to_strip: Vec<H>,
14+
}
15+
16+
#[derive(Clone, Debug)]
17+
pub struct MakeSvc<H, M> {
18+
headers_to_strip: Vec<H>,
19+
inner: M,
20+
}
21+
22+
pub struct MakeSvcFut<M, H> {
23+
authority: Option<Authority>,
24+
headers_to_strip: Vec<H>,
25+
inner: M,
26+
}
27+
28+
#[derive(Clone, Debug)]
29+
pub struct Service<S, H> {
30+
authority: Option<Authority>,
31+
headers_to_strip: Vec<H>,
32+
inner: S,
33+
}
34+
35+
// === impl Layer ===
36+
37+
impl<H> Layer<H>
38+
where
39+
H: AsHeaderName + Clone,
40+
{
41+
pub fn new(headers_to_strip: Vec<H>) -> Self {
42+
Self { headers_to_strip }
43+
}
44+
}
45+
46+
impl<H> Default for Layer<H> {
47+
fn default() -> Self {
48+
Self {
49+
headers_to_strip: Vec::default(),
50+
}
51+
}
52+
}
53+
54+
impl<H, M> tower::layer::Layer<M> for Layer<H>
55+
where
56+
H: AsHeaderName + Clone,
57+
{
58+
type Service = MakeSvc<H, M>;
59+
60+
fn layer(&self, inner: M) -> Self::Service {
61+
Self::Service {
62+
headers_to_strip: self.headers_to_strip.clone(),
63+
inner,
64+
}
65+
}
66+
}
67+
68+
impl<H, T, M> tower::Service<T> for MakeSvc<H, M>
69+
where
70+
T: CanOverrideAuthority + Clone + Send + Sync + 'static,
71+
M: tower::Service<T>,
72+
H: AsHeaderName + Clone,
73+
{
74+
type Response = Service<M::Response, H>;
75+
type Error = M::Error;
76+
type Future = MakeSvcFut<M::Future, H>;
77+
78+
fn poll_ready(&mut self) -> Poll<(), M::Error> {
79+
self.inner.poll_ready()
80+
}
81+
82+
fn call(&mut self, t: T) -> Self::Future {
83+
let authority = t.override_authority();
84+
let inner = self.inner.call(t);
85+
MakeSvcFut {
86+
authority,
87+
headers_to_strip: self.headers_to_strip.clone(),
88+
inner,
89+
}
90+
}
91+
}
92+
93+
impl<F, H> Future for MakeSvcFut<F, H>
94+
where
95+
F: Future,
96+
H: AsHeaderName + Clone,
97+
{
98+
type Item = Service<F::Item, H>;
99+
type Error = F::Error;
100+
101+
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
102+
let inner = try_ready!(self.inner.poll());
103+
Ok(Service {
104+
authority: self.authority.clone(),
105+
headers_to_strip: self.headers_to_strip.clone(),
106+
inner,
107+
}
108+
.into())
109+
}
110+
}
111+
112+
// === impl Service ===
113+
114+
impl<S, H, B> tower::Service<http::Request<B>> for Service<S, H>
115+
where
116+
S: tower::Service<http::Request<B>>,
117+
H: AsHeaderName + fmt::Display + Clone,
118+
{
119+
type Response = S::Response;
120+
type Error = S::Error;
121+
type Future = S::Future;
122+
123+
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
124+
self.inner.poll_ready()
125+
}
126+
127+
fn call(&mut self, mut req: http::Request<B>) -> Self::Future {
128+
if let Some(authority) = self.authority.clone() {
129+
for header in self.headers_to_strip.iter() {
130+
if let Some(value) = req.headers_mut().remove(header.clone()) {
131+
debug!(
132+
%header,
133+
?value,
134+
"Stripped header",
135+
);
136+
};
137+
}
138+
139+
debug!(%authority, "Overriding");
140+
h1::set_authority(req.uri_mut(), authority);
141+
}
142+
143+
self.inner.call(req)
144+
}
145+
}

linkerd/proxy/http/src/settings.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ pub enum Settings {
2020
}
2121

2222
pub trait HasSettings {
23-
fn http_settings(&self) -> &Settings;
23+
fn http_settings(&self) -> Settings;
2424
}
2525

2626
impl HasSettings for Settings {
27-
fn http_settings(&self) -> &Settings {
28-
self
27+
fn http_settings(&self) -> Settings {
28+
*self
2929
}
3030
}
3131

0 commit comments

Comments
 (0)