Skip to content

Commit

Permalink
Proxy: More carefully keep track of the reason TLS isn't used.
Browse files Browse the repository at this point in the history
There is only one case where we dynamically don't know whether we'll
have an identity to construct a TLS connection configuration. Refactor
the code with that in mind, better documenting all the reasons why an
identity isn't available.

Signed-off-by: Brian Smith <brian@briansmith.org>
  • Loading branch information
briansmith committed Jun 20, 2018
1 parent 8ece9c4 commit 45191f8
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 34 deletions.
18 changes: 16 additions & 2 deletions proxy/src/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use transparency::{self, HttpBody, h1};
use transport;
use tls;
use ctx::transport::TlsStatus;
use conditional::Conditional;

/// Binds a `Service` from a `SocketAddr`.
///
Expand Down Expand Up @@ -208,8 +209,21 @@ where
debug!("bind_stack endpoint={:?}, protocol={:?}", ep, protocol);
let addr = ep.address();

let tls = tls::current_connection_config(ep.tls_identity(),
&self.ctx.tls_client_config_watch());
// Like `tls::current_connection_config()` with optional identity.
let tls = match ep.tls_identity() {
Conditional::Some(identity) => {
match *self.ctx.tls_client_config_watch().borrow() {
Some(ref config) =>
Conditional::Some(tls::ConnectionConfig {
identity: identity.clone(),
config: config.clone()
}),
None => Conditional::None(tls::ReasonForNoTls::NoConfig),
}
},
Conditional::None(why_no_identity) =>
Conditional::None(tls::ReasonForNoTls::NoIdentity(why_no_identity)),
};

let client_ctx = ctx::transport::Client::new(
&self.ctx,
Expand Down
13 changes: 13 additions & 0 deletions proxy/src/conditional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,16 @@ where
}
}
}

impl<C, R> Conditional<C, R>
where
C: Clone + std::fmt::Debug,
R: Copy + Clone + std::fmt::Debug,
{
pub fn as_ref<'a>(&'a self) -> Conditional<&'a C, R> {
match self {
Conditional::Some(c) => Conditional::Some(&c),
Conditional::None(r) => Conditional::None(*r),
}
}
}
17 changes: 12 additions & 5 deletions proxy/src/control/destination/background.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use telemetry::metrics::DstLabels;
use transport::{DnsNameAndPort, HostAndPort, LookupAddressAndConnect};
use timeout::Timeout;
use transport::tls;
use conditional::Conditional;

type DestinationServiceQuery<T> = Remote<PbUpdate, T>;
type UpdateRx<T> = Receiver<PbUpdate, T>;
Expand Down Expand Up @@ -78,14 +79,16 @@ pub(super) fn task(
dns_resolver: dns::Resolver,
namespaces: Namespaces,
host_and_port: Option<HostAndPort>,
controller_tls: tls::ConditionalConnectionConfig<tls::ClientConfigWatch>
) -> impl Future<Item = (), Error = ()>
{
// Build up the Controller Client Stack
let mut client = host_and_port.map(|host_and_port| {
let scheme = http::uri::Scheme::from_shared(Bytes::from_static(b"http")).unwrap();
let authority = http::uri::Authority::from(&host_and_port);
let connect = Timeout::new(
LookupAddressAndConnect::new(host_and_port.clone(), dns_resolver.clone()),
LookupAddressAndConnect::new(host_and_port.clone(), dns_resolver.clone(),
controller_tls),
Duration::from_secs(3),
);

Expand Down Expand Up @@ -581,18 +584,22 @@ fn pb_to_addr_meta(
.collect::<Vec<_>>();
labels.sort_by(|(k0, _), (k1, _)| k0.cmp(k1));

let tls_identity = pb.tls_identity.and_then(|pb| {
let mut tls_identity =
Conditional::None(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery);
if let Some(pb) = pb.tls_identity {
match tls::Identity::maybe_from_protobuf(tls_controller_namespace, pb) {
Ok(maybe_tls) => maybe_tls,
Ok(Some(identity)) => {
tls_identity = Conditional::Some(identity);
},
Ok(None) => (),
Err(e) => {
error!("Failed to parse TLS identity: {:?}", e);
// XXX: Wallpaper over the error and keep going without TLS.
// TODO: Hard fail here once the TLS infrastructure has been
// validated.
None
},
}
});
};

let meta = Metadata::new(DstLabels::new(labels.into_iter()), tls_identity);
Some((addr, meta))
Expand Down
3 changes: 2 additions & 1 deletion proxy/src/control/destination/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::net::SocketAddr;
use telemetry::metrics::DstLabels;
use super::Metadata;
use tls;
use conditional::Conditional;

/// An individual traffic target.
///
Expand Down Expand Up @@ -35,7 +36,7 @@ impl Endpoint {
self.metadata.dst_labels()
}

pub fn tls_identity(&self) -> Option<&tls::Identity> {
pub fn tls_identity(&self) -> Conditional<&tls::Identity, tls::ReasonForNoIdentity> {
self.metadata.tls_identity()
}
}
Expand Down
12 changes: 8 additions & 4 deletions proxy/src/control/destination/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod endpoint;

pub use self::endpoint::Endpoint;
use config::Namespaces;
use conditional::Conditional;

/// A handle to request resolutions from the background discovery task.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -95,7 +96,7 @@ pub struct Metadata {
dst_labels: Option<DstLabels>,

/// How to verify TLS for the endpoint.
tls_identity: Option<tls::Identity>,
tls_identity: Conditional<tls::Identity, tls::ReasonForNoIdentity>,
}


Expand Down Expand Up @@ -143,6 +144,7 @@ pub fn new(
dns_resolver: dns::Resolver,
namespaces: Namespaces,
host_and_port: Option<HostAndPort>,
controller_tls: tls::ConditionalConnectionConfig<tls::ClientConfigWatch>,
) -> (Resolver, impl Future<Item = (), Error = ()>) {
let (request_tx, rx) = mpsc::unbounded();
let disco = Resolver { request_tx };
Expand All @@ -151,6 +153,7 @@ pub fn new(
dns_resolver,
namespaces,
host_and_port,
controller_tls,
);
(disco, bg)
}
Expand Down Expand Up @@ -240,13 +243,14 @@ impl Metadata {
Metadata {
dst_labels: None,
// If we have no metadata on an endpoint, assume it does not support TLS.
tls_identity: None,
tls_identity:
Conditional::None(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery),
}
}

pub fn new(
dst_labels: Option<DstLabels>,
tls_identity: Option<tls::Identity>
tls_identity: Conditional<tls::Identity, tls::ReasonForNoIdentity>
) -> Self {
Metadata {
dst_labels,
Expand All @@ -259,7 +263,7 @@ impl Metadata {
self.dst_labels.as_ref()
}

pub fn tls_identity(&self) -> Option<&tls::Identity> {
pub fn tls_identity(&self) -> Conditional<&tls::Identity, tls::ReasonForNoIdentity> {
self.tls_identity.as_ref()
}
}
3 changes: 2 additions & 1 deletion proxy/src/ctx/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use ctx;
use telemetry::metrics::DstLabels;
use std::sync::atomic::Ordering;
use transport::tls;
use conditional::Conditional;


/// A `RequestId` can be mapped to a `u64`. No `RequestId`s will map to the
Expand Down Expand Up @@ -76,7 +77,7 @@ impl Request {
Arc::new(r)
}

pub fn tls_identity(&self) -> Option<&tls::Identity> {
pub fn tls_identity(&self) -> Conditional<&tls::Identity, tls::ReasonForNoIdentity> {
self.client.tls_identity()
}

Expand Down
5 changes: 4 additions & 1 deletion proxy/src/ctx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub mod test_util {
use ctx;
use control::destination;
use telemetry::metrics::DstLabels;
use tls;
use conditional::Conditional;

fn addr() -> SocketAddr {
([1, 2, 3, 4], 5678).into()
Expand All @@ -125,7 +127,8 @@ pub mod test_util {
L: IntoIterator<Item=(S, S)>,
S: fmt::Display,
{
let meta = destination::Metadata::new(DstLabels::new(labels), None);
let meta = destination::Metadata::new(DstLabels::new(labels),
Conditional::None(tls::ReasonForNoIdentity::NotProvidedByServiceDiscovery));
ctx::transport::Client::new(&proxy, &addr(), meta, tls)
}

Expand Down
2 changes: 1 addition & 1 deletion proxy/src/ctx/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl Client {
Arc::new(c)
}

pub fn tls_identity(&self) -> Option<&tls::Identity> {
pub fn tls_identity(&self) -> Conditional<&tls::Identity, tls::ReasonForNoIdentity> {
self.metadata.tls_identity()
}

Expand Down
10 changes: 8 additions & 2 deletions proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ use task::MainRuntime;
use transparency::{HttpBody, Server};
pub use transport::{AddrInfo, GetOriginalDst, SoOriginalDst, tls};
use outbound::Outbound;
use conditional::Conditional;

/// Runs a sidecar proxy.
///
Expand Down Expand Up @@ -183,7 +184,7 @@ where
let (tls_client_config, tls_server_config, tls_cfg_bg) =
tls::watch_for_config_changes(self.config.tls_settings.as_ref());

let process_ctx = ctx::Process::new(&self.config, tls_client_config);
let process_ctx = ctx::Process::new(&self.config, tls_client_config.clone());

let Main {
config,
Expand Down Expand Up @@ -225,6 +226,10 @@ where
&taps,
);

let controller_tls =
Conditional::None(tls::ReasonForNoTls::NoIdentity(
tls::ReasonForNoIdentity::NotImplementedForController)); // TODO

let (dns_resolver, dns_bg) = dns::Resolver::from_system_config_and_env(&config)
.unwrap_or_else(|e| {
// TODO: Make DNS configuration infallible.
Expand All @@ -234,7 +239,8 @@ where
let (resolver, resolver_bg) = control::destination::new(
dns_resolver.clone(),
config.namespaces.clone(),
control_host_and_port
control_host_and_port,
controller_tls,
);

let (drain_tx, drain_rx) = drain::channel();
Expand Down
3 changes: 1 addition & 2 deletions proxy/src/telemetry/metrics/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,7 @@ impl fmt::Display for ctx::transport::TlsStatus {
Conditional::Some(()) => f.pad(",tls=\"true\""),
Conditional::None(tls::ReasonForNoTls::NoConfig) => f.pad(",tls=\"no_config\""),
Conditional::None(tls::ReasonForNoTls::Disabled) |
Conditional::None(tls::ReasonForNoTls::NotImplementedForNonHttp) |
Conditional::None(tls::ReasonForNoTls::NotImplementedForControlPlane) => Ok(()),
Conditional::None(tls::ReasonForNoTls::NoIdentity(_)) => Ok(()),
}
}
}
3 changes: 2 additions & 1 deletion proxy/src/transparency/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ impl Proxy {
return future::Either::B(future::ok(()));
};

let tls = Conditional::None(tls::ReasonForNoTls::NotImplementedForNonHttp); // TODO
let tls = Conditional::None(
tls::ReasonForNoTls::NoIdentity(tls::ReasonForNoIdentity::NotHttp)); // TODO

let client_ctx = ClientCtx::new(
&srv_ctx.proxy,
Expand Down
6 changes: 4 additions & 2 deletions proxy/src/transport/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use connection;
use convert::TryFrom;
use dns;
use transport::tls;
use conditional::Conditional;

#[derive(Debug, Clone)]
pub struct Connect {
Expand Down Expand Up @@ -51,6 +50,7 @@ pub enum HostAndPortError {
pub struct LookupAddressAndConnect {
host_and_port: HostAndPort,
dns_resolver: dns::Resolver,
tls: tls::ConditionalConnectionConfig<tls::ClientConfigWatch>,
}

// ===== impl HostAndPort =====
Expand Down Expand Up @@ -129,10 +129,12 @@ impl LookupAddressAndConnect {
pub fn new(
host_and_port: HostAndPort,
dns_resolver: dns::Resolver,
tls: tls::ConditionalConnectionConfig<tls::ClientConfigWatch>,
) -> Self {
Self {
host_and_port,
dns_resolver,
tls,
}
}
}
Expand All @@ -145,7 +147,7 @@ impl tokio_connect::Connect for LookupAddressAndConnect {
fn connect(&self) -> Self::Future {
let port = self.host_and_port.port;
let host = self.host_and_port.host.clone();
let tls = Conditional::None(tls::ReasonForNoTls::NotImplementedForControlPlane); // TODO
let tls = tls::current_connection_config(&self.tls);
let c = self.dns_resolver
.resolve_one_ip(&self.host_and_port.host)
.map_err(|_| {
Expand Down
39 changes: 27 additions & 12 deletions proxy/src/transport/tls/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,28 @@ pub enum ReasonForNoTls {
/// TLS was enabled but the configuration isn't available (yet).
NoConfig,

/// TLS isn't implemented for the connection between the proxy and the
/// control plane yet.
NotImplementedForControlPlane,

/// TLS is only enabled for HTTP (HTTPS) right now.
NotImplementedForNonHttp,
/// The endpoint's TLS identity is unknown. Without knowing its identity
/// we can't validate its certificate.
NoIdentity(ReasonForNoIdentity),
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ReasonForNoIdentity {
/// The connection is a non-HTTP connection so we don't know anything
/// about the destination besides its address.
NotHttp,

/// The connection is for HTTP but the HTTP request doesn't have an
/// authority so we can't extract the identity from it.
NoAuthorityInHttpRequest,

/// The destination service didn't give us the identity, which is its way
/// of telling us that we shouldn't do TLS for this endpoint.
NotProvidedByServiceDiscovery,

/// We haven't implemented the mechanism to construct a TLS identity for
/// the controller yet.
NotImplementedForController,
}

pub type ConditionalConnectionConfig<C> = Conditional<ConnectionConfig<C>, ReasonForNoTls>;
Expand Down Expand Up @@ -318,21 +333,21 @@ impl ServerConfig {
}
}

pub fn current_connection_config<C>(identity: Option<&Identity>, watch: &Watch<Option<C>>)
pub fn current_connection_config<C>(watch: &ConditionalConnectionConfig<Watch<Option<C>>>)
-> ConditionalConnectionConfig<C> where C: Clone + std::fmt::Debug
{
match identity {
Some(identity) => {
match *watch.borrow() {
match watch {
Conditional::Some(c) => {
match *c.config.borrow() {
Some(ref config) =>
Conditional::Some(ConnectionConfig {
identity: identity.clone(),
identity: c.identity.clone(),
config: config.clone()
}),
None => Conditional::None(ReasonForNoTls::NoConfig),
}
},
None => Conditional::None(ReasonForNoTls::Disabled),
Conditional::None(r) => Conditional::None(*r),
}
}

Expand Down
1 change: 1 addition & 0 deletions proxy/src/transport/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use self::{
ConditionalConnectionConfig,
ConnectionConfig,
ReasonForNoTls,
ReasonForNoIdentity,
ServerConfig,
ServerConfigWatch,
current_connection_config,
Expand Down

0 comments on commit 45191f8

Please sign in to comment.