Skip to content

Commit 6d10dd6

Browse files
authored
Set l5d-remote-ip on inbound requests and outbound responses (#200)
## Problem [Context](linkerd/linkerd2#2310) ## Solution We now add the `l5d-remote-ip` header to all inbound requests and outbound responses. We also make sure to strip the header before setting so that clients may not set this header. We were already adding a header in similar style, so in order to deduplicate code, `add_header.rs` holds the generic logic for adding a header to a `http::Request` or `http::Response`. `l5d-client-id` and `l5d-server-id` now also use the same logic in order to add their respective headers. ## Validation An additional test module was added to `tests/discovery.rs` that tests the following: - outbound responses strip `l5d-remote-ip` - inbound requests strip `l5d-remote-ip` - outbound responses set `l5d-remote-ip` to the endpoint IP - inbound requests set `l5d-remote-ip` to the source IP Closes linkerd/linkerd2#2310 Signed-off-by: Kevin Leimkuhler <kevinl@buoyant.io>
1 parent dc00685 commit 6d10dd6

File tree

7 files changed

+361
-250
lines changed

7 files changed

+361
-250
lines changed

src/app/inbound.rs

Lines changed: 30 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -261,130 +261,50 @@ pub mod rewrite_loopback_addr {
261261

262262
/// Adds `l5d-client-id` headers to http::Requests derived from the
263263
/// TlsIdentity of a `Source`.
264-
pub mod client_id {
265-
use std::marker::PhantomData;
266-
267-
use futures::Poll;
268-
use http::{self, header::HeaderValue};
269-
270-
use proxy::server::Source;
271-
use svc;
264+
pub mod set_client_id_on_req {
265+
use super::super::L5D_CLIENT_ID;
266+
use http::header::HeaderValue;
267+
268+
use proxy::{
269+
http::add_header::{self, request::ReqHeader, Layer},
270+
server::Source,
271+
};
272272
use Conditional;
273273

274-
#[derive(Debug)]
275-
pub struct Layer<B>(PhantomData<fn() -> B>);
276-
277-
#[derive(Debug)]
278-
pub struct Stack<M, B> {
279-
inner: M,
280-
_marker: PhantomData<fn() -> B>,
281-
}
282-
283-
#[derive(Debug)]
284-
pub struct Service<S, B> {
285-
inner: S,
286-
value: HeaderValue,
287-
_marker: PhantomData<fn() -> B>,
288-
}
289-
290-
pub fn layer<B>() -> Layer<B> {
291-
Layer(PhantomData)
292-
}
293-
294-
impl<B> Clone for Layer<B> {
295-
fn clone(&self) -> Self {
296-
Layer(PhantomData)
297-
}
298-
}
299-
300-
impl<M, B> svc::Layer<Source, Source, M> for Layer<B>
301-
where
302-
M: svc::Stack<Source>,
303-
{
304-
type Value = <Stack<M, B> as svc::Stack<Source>>::Value;
305-
type Error = <Stack<M, B> as svc::Stack<Source>>::Error;
306-
type Stack = Stack<M, B>;
307-
308-
fn bind(&self, inner: M) -> Self::Stack {
309-
Stack {
310-
inner,
311-
_marker: PhantomData,
312-
}
313-
}
314-
}
315-
316-
// === impl Stack ===
317-
318-
impl<M: Clone, B> Clone for Stack<M, B> {
319-
fn clone(&self) -> Self {
320-
Stack {
321-
inner: self.inner.clone(),
322-
_marker: PhantomData,
323-
}
324-
}
325-
}
326-
327-
impl<M, B> svc::Stack<Source> for Stack<M, B>
328-
where
329-
M: svc::Stack<Source>,
330-
{
331-
type Value = svc::Either<Service<M::Value, B>, M::Value>;
332-
type Error = M::Error;
333-
334-
fn make(&self, source: &Source) -> Result<Self::Value, Self::Error> {
335-
let svc = self.inner.make(source)?;
336-
274+
pub fn layer() -> Layer<&'static str, Source, ReqHeader> {
275+
add_header::request::layer(L5D_CLIENT_ID, |source: &Source| {
337276
if let Conditional::Some(ref id) = source.tls_peer {
338277
match HeaderValue::from_str(id.as_ref()) {
339278
Ok(value) => {
340279
debug!("l5d-client-id enabled for {:?}", source);
341-
return Ok(svc::Either::A(Service {
342-
inner: svc,
343-
value,
344-
_marker: PhantomData,
345-
}));
280+
return Some(value);
346281
}
347282
Err(_err) => {
348283
warn!("l5d-client-id identity header is invalid: {:?}", source);
349284
}
350-
}
285+
};
351286
}
352287

353-
trace!("l5d-client-id not enabled for {:?}", source);
354-
Ok(svc::Either::B(svc))
355-
}
356-
}
357-
358-
// === impl Service ===
359-
360-
impl<S: Clone, B> Clone for Service<S, B> {
361-
fn clone(&self) -> Self {
362-
Service {
363-
inner: self.inner.clone(),
364-
value: self.value.clone(),
365-
_marker: PhantomData,
366-
}
367-
}
288+
None
289+
})
368290
}
291+
}
369292

370-
impl<S, B> svc::Service<http::Request<B>> for Service<S, B>
371-
where
372-
S: svc::Service<http::Request<B>>,
373-
{
374-
type Response = S::Response;
375-
type Error = S::Error;
376-
type Future = S::Future;
377-
378-
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
379-
self.inner.poll_ready()
380-
}
381-
382-
fn call(&mut self, mut req: http::Request<B>) -> Self::Future {
383-
req.headers_mut()
384-
.insert(super::super::L5D_CLIENT_ID, self.value.clone());
385-
386-
self.inner.call(req)
387-
}
293+
/// Adds `l5d-remote-ip` headers to http::Requests derived from the
294+
/// `remote` of a `Source`.
295+
pub mod set_remote_ip_on_req {
296+
use super::super::L5D_REMOTE_IP;
297+
use bytes::Bytes;
298+
use http::header::HeaderValue;
299+
use proxy::{
300+
http::add_header::{self, request::ReqHeader, Layer},
301+
server::Source,
302+
};
303+
304+
pub fn layer() -> Layer<&'static str, Source, ReqHeader> {
305+
add_header::request::layer(L5D_REMOTE_IP, |source: &Source| {
306+
HeaderValue::from_shared(Bytes::from(source.remote.ip().to_string())).ok()
307+
})
388308
}
389309
}
390310

src/app/main.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ where
302302

303303
let outbound = {
304304
use super::outbound::{
305-
discovery::Resolve, orig_proto_upgrade, server_id, Endpoint,
305+
add_remote_ip_on_rsp, add_server_id_on_rsp, discovery::Resolve,
306+
orig_proto_upgrade, Endpoint,
306307
};
307308
use proxy::{
308309
canonicalize,
@@ -348,8 +349,10 @@ where
348349
let endpoint_stack = client_stack
349350
.push(buffer::layer(MAX_IN_FLIGHT))
350351
.push(strip_header::response::layer(super::L5D_SERVER_ID))
352+
.push(strip_header::response::layer(super::L5D_REMOTE_IP))
351353
.push(settings::router::layer::<Endpoint, _>())
352-
.push(server_id::layer())
354+
.push(add_server_id_on_rsp::layer())
355+
.push(add_remote_ip_on_rsp::layer())
353356
.push(orig_proto_upgrade::layer())
354357
.push(tap_layer.clone())
355358
.push(metrics::layer::<_, classify::Response>(
@@ -490,8 +493,8 @@ where
490493

491494
let inbound = {
492495
use super::inbound::{
493-
client_id, orig_proto_downgrade, rewrite_loopback_addr, Endpoint,
494-
RecognizeEndpoint,
496+
orig_proto_downgrade, rewrite_loopback_addr, set_client_id_on_req,
497+
set_remote_ip_on_req, Endpoint, RecognizeEndpoint,
495498
};
496499

497500
let capacity = config.inbound_router_capacity;
@@ -609,9 +612,11 @@ where
609612
let source_stack = dst_router
610613
.push(orig_proto_downgrade::layer())
611614
.push(insert_target::layer())
612-
.push(client_id::layer())
613-
.push(strip_header::response::layer(super::L5D_SERVER_ID))
615+
.push(set_remote_ip_on_req::layer())
616+
.push(strip_header::request::layer(super::L5D_REMOTE_IP))
617+
.push(set_client_id_on_req::layer())
614618
.push(strip_header::request::layer(super::L5D_CLIENT_ID))
619+
.push(strip_header::response::layer(super::L5D_SERVER_ID))
615620
.push(strip_header::request::layer(super::DST_OVERRIDE_HEADER));
616621

617622
// As the inbound proxy accepts connections, we don't do any

src/app/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use addr::{self, Addr};
1717

1818
const CANONICAL_DST_HEADER: &'static str = "l5d-dst-canonical";
1919
pub const DST_OVERRIDE_HEADER: &'static str = "l5d-dst-override";
20+
const L5D_REMOTE_IP: &'static str = "l5d-remote-ip";
2021
const L5D_SERVER_ID: &'static str = "l5d-server-id";
2122
const L5D_CLIENT_ID: &'static str = "l5d-client-id";
2223

src/app/outbound.rs

Lines changed: 24 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -271,154 +271,44 @@ pub mod orig_proto_upgrade {
271271

272272
/// Adds `l5d-server-id` headers to http::Responses derived from the
273273
/// TlsIdentity of an `Endpoint`.
274-
pub mod server_id {
275-
use std::marker::PhantomData;
276-
277-
use futures::{Future, Poll};
278-
use http::{self, header::HeaderValue};
279-
274+
pub mod add_server_id_on_rsp {
275+
use super::super::L5D_SERVER_ID;
280276
use super::Endpoint;
281-
use proxy::http::ClientUsedTls;
282-
use svc;
277+
use http::header::HeaderValue;
278+
use proxy::http::add_header::{self, response::ResHeader, Layer};
283279
use Conditional;
284280

285-
#[derive(Debug)]
286-
pub struct Layer<B>(PhantomData<fn() -> B>);
287-
288-
#[derive(Debug)]
289-
pub struct Stack<M, B> {
290-
inner: M,
291-
_marker: PhantomData<fn() -> B>,
292-
}
293-
294-
#[derive(Debug)]
295-
pub struct Service<S, B> {
296-
inner: S,
297-
value: HeaderValue,
298-
_marker: PhantomData<fn() -> B>,
299-
}
300-
301-
pub struct ResponseFuture<F> {
302-
inner: F,
303-
value: HeaderValue,
304-
}
305-
306-
pub fn layer<B>() -> Layer<B> {
307-
Layer(PhantomData)
308-
}
309-
310-
impl<B> Clone for Layer<B> {
311-
fn clone(&self) -> Self {
312-
Layer(PhantomData)
313-
}
314-
}
315-
316-
impl<M, B> svc::Layer<Endpoint, Endpoint, M> for Layer<B>
317-
where
318-
M: svc::Stack<Endpoint>,
319-
{
320-
type Value = <Stack<M, B> as svc::Stack<Endpoint>>::Value;
321-
type Error = <Stack<M, B> as svc::Stack<Endpoint>>::Error;
322-
type Stack = Stack<M, B>;
323-
324-
fn bind(&self, inner: M) -> Self::Stack {
325-
Stack {
326-
inner,
327-
_marker: PhantomData,
328-
}
329-
}
330-
}
331-
332-
// === impl Stack ===
333-
334-
impl<M: Clone, B> Clone for Stack<M, B> {
335-
fn clone(&self) -> Self {
336-
Stack {
337-
inner: self.inner.clone(),
338-
_marker: PhantomData,
339-
}
340-
}
341-
}
342-
343-
impl<M, B> svc::Stack<Endpoint> for Stack<M, B>
344-
where
345-
M: svc::Stack<Endpoint>,
346-
{
347-
type Value = svc::Either<Service<M::Value, B>, M::Value>;
348-
type Error = M::Error;
349-
350-
fn make(&self, endpoint: &Endpoint) -> Result<Self::Value, Self::Error> {
351-
let svc = self.inner.make(endpoint)?;
352-
281+
pub fn layer() -> Layer<&'static str, Endpoint, ResHeader> {
282+
add_header::response::layer(L5D_SERVER_ID, |endpoint: &Endpoint| {
353283
if let Conditional::Some(id) = endpoint.connect.tls_server_identity() {
354284
match HeaderValue::from_str(id.as_ref()) {
355285
Ok(value) => {
356286
debug!("l5d-server-id enabled for {:?}", endpoint);
357-
return Ok(svc::Either::A(Service {
358-
inner: svc,
359-
value,
360-
_marker: PhantomData,
361-
}));
287+
return Some(value);
362288
}
363289
Err(_err) => {
364290
warn!("l5d-server-id identity header is invalid: {:?}", endpoint);
365291
}
366-
}
367-
}
368-
369-
trace!("l5d-server-id not enabled for {:?}", endpoint);
370-
Ok(svc::Either::B(svc))
371-
}
372-
}
373-
374-
// === impl Service ===
375-
376-
impl<S: Clone, B> Clone for Service<S, B> {
377-
fn clone(&self) -> Self {
378-
Service {
379-
inner: self.inner.clone(),
380-
value: self.value.clone(),
381-
_marker: PhantomData,
292+
};
382293
}
383-
}
384-
}
385-
386-
impl<S, B, Req> svc::Service<Req> for Service<S, B>
387-
where
388-
S: svc::Service<Req, Response = http::Response<B>>,
389-
{
390-
type Response = S::Response;
391-
type Error = S::Error;
392-
type Future = ResponseFuture<S::Future>;
393-
394-
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
395-
self.inner.poll_ready()
396-
}
397294

398-
fn call(&mut self, req: Req) -> Self::Future {
399-
let fut = self.inner.call(req);
400-
401-
ResponseFuture {
402-
inner: fut,
403-
value: self.value.clone(),
404-
}
405-
}
295+
None
296+
})
406297
}
298+
}
407299

408-
impl<F, B> Future for ResponseFuture<F>
409-
where
410-
F: Future<Item = http::Response<B>>,
411-
{
412-
type Item = F::Item;
413-
type Error = F::Error;
414-
415-
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
416-
let mut res = try_ready!(self.inner.poll());
417-
if res.extensions().get::<ClientUsedTls>().is_some() {
418-
res.headers_mut()
419-
.insert(super::super::L5D_SERVER_ID, self.value.clone());
420-
}
421-
Ok(res.into())
422-
}
300+
/// Adds `l5d-remote-ip` headers to http::Responses derived from the
301+
/// `remote` of a `Source`.
302+
pub mod add_remote_ip_on_rsp {
303+
use super::super::L5D_REMOTE_IP;
304+
use super::Endpoint;
305+
use bytes::Bytes;
306+
use http::header::HeaderValue;
307+
use proxy::http::add_header::{self, response::ResHeader, Layer};
308+
309+
pub fn layer() -> Layer<&'static str, Endpoint, ResHeader> {
310+
add_header::response::layer(L5D_REMOTE_IP, |endpoint: &Endpoint| {
311+
HeaderValue::from_shared(Bytes::from(endpoint.connect.addr.ip().to_string())).ok()
312+
})
423313
}
424314
}

0 commit comments

Comments
 (0)