Skip to content

Commit

Permalink
Add l5d-client-id on inbound requests if meshed TLS
Browse files Browse the repository at this point in the history
Signed-off-by: Sean McArthur <[email protected]>
  • Loading branch information
seanmonstar committed Jan 31, 2019
1 parent d3e7f8b commit 1e0787a
Show file tree
Hide file tree
Showing 17 changed files with 285 additions and 68 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ dependencies = [
"trust-dns-resolver 0.10.2 (git+https://github.com/bluejekyll/trust-dns?rev=7c8a0739dad495bf5a4fddfe86b8bbe2aa52d060)",
"try-lock 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (git+https://github.com/seanmonstar/webpki?branch=cert-dns-names)",
]

[[package]]
Expand Down Expand Up @@ -1114,7 +1114,7 @@ dependencies = [
"ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (git+https://github.com/seanmonstar/webpki?branch=cert-dns-names)",
]

[[package]]
Expand Down Expand Up @@ -1330,7 +1330,7 @@ dependencies = [
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
"rustls 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.19.1 (git+https://github.com/seanmonstar/webpki?branch=cert-dns-names)",
]

[[package]]
Expand Down Expand Up @@ -1663,7 +1663,7 @@ dependencies = [
[[package]]
name = "webpki"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/seanmonstar/webpki?branch=cert-dns-names#aae34c01a75495620767ee1fed40ac4c0a34fe74"
dependencies = [
"ring 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
Expand Down Expand Up @@ -1916,7 +1916,7 @@ dependencies = [
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
"checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082"
"checksum webpki 0.19.1 (git+https://github.com/seanmonstar/webpki?branch=cert-dns-names)" = "<none>"
"checksum which 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49c4f580e93079b70ac522e7bdebbe1568c8afa7d8d05ee534ee737ca37d2f51"
"checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ flate2 = { version = "1.0.1", default-features = false, features = ["rust_backen
# the `read` function.
tokio-io = "0.1.6"

[patch.crates-io]
webpki = { git = "https://github.com/seanmonstar/webpki", branch = "cert-dns-names" }

# Debug symbols end up chewing up several GB of disk space, so better to just
# disable them.

[profile.dev]
debug = false
[profile.test]
Expand Down
137 changes: 134 additions & 3 deletions src/app/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl tap::Inspect for Endpoint {
fn src_tls<B>(&self, req: &http::Request<B>) -> tls::Status {
req.extensions()
.get::<Source>()
.map(|s| s.tls_status)
.map(|s| tls::Status::from(&s.tls_peer))
.unwrap_or_else(|| Conditional::None(tls::ReasonForNoTls::Disabled))
}

Expand Down Expand Up @@ -105,7 +105,7 @@ impl<A> router::Recognize<http::Request<A>> for RecognizeEndpoint {
.or(self.default_addr)?;

let source_tls_status = src
.map(|s| s.tls_status.clone())
.map(|s| tls::Status::from(&s.tls_peer))
.unwrap_or_else(|| Conditional::None(tls::ReasonForNoTls::Disabled));

let dst_name = req
Expand Down Expand Up @@ -254,6 +254,137 @@ pub mod rewrite_loopback_addr {
}
}

/// Adds `l5d-client-id` headers to http::Requests derived from the
/// TlsIdentity of a `Source`.
pub mod client_id {
use std::marker::PhantomData;

use futures::Poll;
use http::{self, header::HeaderValue};

use proxy::server::Source;
use Conditional;
use svc;

#[derive(Debug)]
pub struct Layer<B>(PhantomData<fn() -> B>);

#[derive(Debug)]
pub struct Stack<M, B> {
inner: M,
_marker: PhantomData<fn() -> B>,
}

#[derive(Debug)]
pub struct Service<S, B> {
inner: S,
value: HeaderValue,
_marker: PhantomData<fn() -> B>,
}

pub fn layer<B>() -> Layer<B> {
Layer(PhantomData)
}

impl<B> Clone for Layer<B> {
fn clone(&self) -> Self {
Layer(PhantomData)
}
}

impl<M, B> svc::Layer<Source, Source, M> for Layer<B>
where
M: svc::Stack<Source>,
{
type Value = <Stack<M, B> as svc::Stack<Source>>::Value;
type Error = <Stack<M, B> as svc::Stack<Source>>::Error;
type Stack = Stack<M, B>;

fn bind(&self, inner: M) -> Self::Stack {
Stack {
inner,
_marker: PhantomData,
}
}
}

// === impl Stack ===

impl<M: Clone, B> Clone for Stack<M, B> {
fn clone(&self) -> Self {
Stack {
inner: self.inner.clone(),
_marker: PhantomData,
}
}
}

impl<M, B> svc::Stack<Source> for Stack<M, B>
where
M: svc::Stack<Source>,
{
type Value = svc::Either<Service<M::Value, B>, M::Value>;
type Error = M::Error;

fn make(&self, source: &Source) -> Result<Self::Value, Self::Error> {
let svc = self.inner.make(source)?;

if let Conditional::Some(ref id) = source.tls_peer {
match HeaderValue::from_str(id.as_ref()) {
Ok(value) => {
debug!("l5d-client-id enabled for {:?}", source);
return Ok(svc::Either::A(Service {
inner: svc,
value,
_marker: PhantomData,
}));
},
Err(_err) => {
warn!("l5d-client-id identity header is invalid: {:?}", source);
}
}
}

trace!("l5d-client-id not enabled for {:?}", source);
Ok(svc::Either::B(svc))
}
}

// === impl Service ===

impl<S: Clone, B> Clone for Service<S, B> {
fn clone(&self) -> Self {
Service {
inner: self.inner.clone(),
value: self.value.clone(),
_marker: PhantomData,
}
}
}

impl<S, B> svc::Service<http::Request<B>> for Service<S, B>
where
S: svc::Service<http::Request<B>>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;

fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.inner.poll_ready()
}

fn call(&mut self, mut req: http::Request<B>) -> Self::Future {
req.headers_mut().insert(
super::super::L5D_CLIENT_ID,
self.value.clone()
);

self.inner.call(req)
}
}
}

#[cfg(test)]
mod tests {
use http;
Expand All @@ -274,7 +405,7 @@ mod tests {
}
}

const TLS_DISABLED: Conditional<(), tls::ReasonForNoTls> =
const TLS_DISABLED: tls::ConditionalIdentity =
Conditional::None(tls::ReasonForNoTls::Disabled);

quickcheck! {
Expand Down
5 changes: 4 additions & 1 deletion src/app/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ where
let addr_router = addr_stack
.push(buffer::layer(MAX_IN_FLIGHT))
.push(limit::layer(MAX_IN_FLIGHT))
.push(strip_header::request::layer(super::L5D_CLIENT_ID))
.push(strip_header::request::layer(super::DST_OVERRIDE_HEADER))
.push(router::layer(|req: &http::Request<_>| {
super::http_request_l5d_override_dst_addr(req)
Expand Down Expand Up @@ -478,7 +479,7 @@ where

let inbound = {
use super::inbound::{
orig_proto_downgrade, rewrite_loopback_addr, Endpoint, RecognizeEndpoint,
client_id, orig_proto_downgrade, rewrite_loopback_addr, Endpoint, RecognizeEndpoint,
};

let capacity = config.inbound_router_capacity;
Expand Down Expand Up @@ -595,7 +596,9 @@ where
let source_stack = dst_router
.push(orig_proto_downgrade::layer())
.push(insert_target::layer())
.push(client_id::layer())
.push(strip_header::response::layer(super::L5D_SERVER_ID))
.push(strip_header::request::layer(super::L5D_CLIENT_ID))
.push(strip_header::request::layer(super::DST_OVERRIDE_HEADER));

// As the inbound proxy accepts connections, we don't do any
Expand Down
1 change: 1 addition & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use addr::{self, Addr};
const CANONICAL_DST_HEADER: &'static str = "l5d-dst-canonical";
pub const DST_OVERRIDE_HEADER: &'static str = "l5d-dst-override";
const L5D_SERVER_ID: &'static str = "l5d-server-id";
const L5D_CLIENT_ID: &'static str = "l5d-client-id";

pub fn init() -> Result<config::Config, config::Error> {
use convert::TryFrom;
Expand Down
32 changes: 11 additions & 21 deletions src/app/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,28 +346,18 @@ pub mod server_id {
fn make(&self, endpoint: &Endpoint) -> Result<Self::Value, Self::Error> {
let svc = self.inner.make(endpoint)?;

if endpoint.connect.tls.is_some() {
match endpoint.metadata.tls_identity() {
Conditional::Some(id) => match HeaderValue::from_str(id.as_ref()) {
Ok(value) => {
debug!("l5d-server-id enabled for {:?}", endpoint);
return Ok(svc::Either::A(Service {
inner: svc,
value,
_marker: PhantomData,
}));
},
Err(_err) => {
warn!("l5d-server-id identity header is invalid: {:?}", endpoint);
}
if let Conditional::Some(id) = endpoint.connect.tls_server_identity() {
match HeaderValue::from_str(id.as_ref()) {
Ok(value) => {
debug!("l5d-server-id enabled for {:?}", endpoint);
return Ok(svc::Either::A(Service {
inner: svc,
value,
_marker: PhantomData,
}));
},
Conditional::None(why) => {
error!("endpoint tls is some, but no identity ({:?}): {:?}", why, endpoint);

// This is a bug, so panic in tests!
if cfg!(debug_assertions) {
panic!("endpoint tls is some, but no identity ({:?}): {:?}", why, endpoint);
}
Err(_err) => {
warn!("l5d-server-id identity header is invalid: {:?}", endpoint);
},
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/conditional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,15 @@ where
!self.is_none()
}
}

impl<'a, C, R> Conditional<&'a C, R>
where
C: Clone,
{
pub fn cloned(self) -> Conditional<C, R> {
match self {
Conditional::Some(c) => Conditional::Some(c.clone()),
Conditional::None(r) => Conditional::None(r),
}
}
}
8 changes: 4 additions & 4 deletions src/proxy/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub struct Source {
pub remote: SocketAddr,
pub local: SocketAddr,
pub orig_dst: Option<SocketAddr>,
pub tls_status: tls::Status,
pub tls_peer: tls::ConditionalIdentity,
_p: (),
}

Expand Down Expand Up @@ -115,13 +115,13 @@ impl Source {
remote: SocketAddr,
local: SocketAddr,
orig_dst: Option<SocketAddr>,
tls_status: tls::Status
tls_peer: tls::ConditionalIdentity,
) -> Self {
Self {
remote,
local,
orig_dst,
tls_status,
tls_peer,
_p: (),
}
}
Expand Down Expand Up @@ -238,7 +238,7 @@ where
remote: remote_addr,
local: connection.local_addr().unwrap_or(self.listen_addr),
orig_dst,
tls_status: connection.tls_status(),
tls_peer: connection.tls_peer_identity().cloned(),
_p: (),
};

Expand Down
5 changes: 5 additions & 0 deletions src/transport/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use self::tokio_connect::Connect;
use std::net::SocketAddr;
use std::{hash, io};

use Conditional;
use never::Never;
use svc;
use transport::{connection, tls};
Expand Down Expand Up @@ -33,6 +34,10 @@ impl Target {
pub fn tls_status(&self) -> tls::Status {
self.tls.as_ref().map(|_| {})
}

pub fn tls_server_identity(&self) -> Conditional<&tls::Identity, tls::ReasonForNoTls> {
self.tls.as_ref().map(|config| &config.server_identity)
}
}

impl Connect for Target {
Expand Down
Loading

0 comments on commit 1e0787a

Please sign in to comment.