diff --git a/proxy/src/conditional.rs b/proxy/src/conditional.rs index 55c14d54e..431b42a46 100644 --- a/proxy/src/conditional.rs +++ b/proxy/src/conditional.rs @@ -2,11 +2,11 @@ use std; /// Like `std::option::Option` but `None` carries a reason why the value /// isn't available. -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum Conditional where - C: Clone + std::fmt::Debug, - R: Clone + std::fmt::Debug, + C: Clone, + R: Clone, { Some(C), None(R), @@ -19,17 +19,30 @@ where { } +impl std::fmt::Debug for Conditional +where + C: Clone + std::fmt::Debug, + R: Clone + std::fmt::Debug +{ + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self { + Conditional::Some(s) => f.debug_tuple("Some").field(s).finish(), + Conditional::None(r) => f.debug_tuple("None").field(r).finish(), + } + } +} + impl Eq for Conditional where - C: Eq + Clone + std::fmt::Debug, - R: Eq + Clone + std::fmt::Debug, + C: Eq + Clone, + R: Eq + Clone, { } impl PartialEq for Conditional where - C: PartialEq + Clone + std::fmt::Debug, - R: PartialEq + Clone + std::fmt::Debug, + C: PartialEq + Clone, + R: PartialEq + Clone, { fn eq(&self, other: &Conditional) -> bool { use self::Conditional::*; @@ -43,8 +56,8 @@ where impl std::hash::Hash for Conditional where - C: std::hash::Hash + Clone + std::fmt::Debug, - R: std::hash::Hash + Clone + std::fmt::Debug, + C: std::hash::Hash + Clone, + R: std::hash::Hash + Clone, { fn hash(&self, state: &mut H) { match self { @@ -56,8 +69,8 @@ where impl Conditional where - C: Clone + std::fmt::Debug, - R: Copy + Clone + std::fmt::Debug, + C: Clone, + R: Copy + Clone, { pub fn as_ref<'a>(&'a self) -> Conditional<&'a C, R> { match self { diff --git a/proxy/src/config.rs b/proxy/src/config.rs index 9e6a99e04..4f0ef13f2 100644 --- a/proxy/src/config.rs +++ b/proxy/src/config.rs @@ -12,6 +12,7 @@ use trust_dns_resolver::config::ResolverOpts; use transport::{Host, HostAndPort, HostAndPortError, tls}; use convert::TryFrom; +use conditional::Conditional; // TODO: // @@ -53,7 +54,7 @@ pub struct Config { pub outbound_router_max_idle_age: Duration, - pub tls_settings: Option, + pub tls_settings: Conditional, /// The path to "/etc/resolv.conf" pub resolv_conf_path: PathBuf, @@ -306,14 +307,14 @@ impl<'a> TryFrom<&'a Strings> for Config { Some(pod_name)) => { let service_identity = tls::Identity::try_from_pod_name(&namespaces, pod_name) .map_err(|_| Error::InvalidEnvVar)?; // Already logged. - Ok(Some(tls::CommonSettings { + Ok(Conditional::Some(tls::CommonSettings { trust_anchors, end_entity_cert, private_key, service_identity, })) }, - (None, None, None, _) => Ok(None), // No TLS. + (None, None, None, _) => Ok(Conditional::None(tls::ReasonForNoTls::Disabled)), (trust_anchors, end_entity_cert, private_key, pod_name) => { if trust_anchors.is_none() { error!("{} is not set; it is required when {} and {} are set.", diff --git a/proxy/src/connection.rs b/proxy/src/connection.rs index affbbda9f..327b9177c 100644 --- a/proxy/src/connection.rs +++ b/proxy/src/connection.rs @@ -111,7 +111,7 @@ impl BoundPort { // TLS when needed. pub fn listen_and_fold( self, - tls: Option<(tls::Identity, tls::ServerConfigWatch)>, + tls: tls::ConditionalConnectionConfig, initial: T, f: F) -> impl Future + Send + 'static @@ -133,6 +133,7 @@ impl BoundPort { .and_then(move |socket| { let remote_addr = socket.peer_addr() .expect("couldn't get remote addr!"); + // TODO: On Linux and most other platforms it would be better // to set the `TCP_NODELAY` option on the bound socket and // then have the listening sockets inherit it. However, that @@ -140,25 +141,23 @@ impl BoundPort { // libraries don't have the necessary API for that, so just // do it here. set_nodelay_or_warn(&socket); - let why_no_tls = if let Some((_identity, config_watch)) = &tls { - // TODO: use `identity` to differentiate between TLS - // that the proxy should terminate vs. TLS that should - // be passed through. - if let Some(config) = &*config_watch.borrow() { - let f = tls::Connection::accept(socket, config.clone()) - .map(move |tls| { - (Connection::tls(tls), remote_addr) - }); - return Either::A(f); - } else { - // No valid TLS configuration. - tls::ReasonForNoTls::NoConfig - } - } else { - tls::ReasonForNoTls::Disabled + + let conn = match tls::current_connection_config(&tls) { + Conditional::Some(config) => { + // TODO: use `config.identity` to differentiate + // between TLS that the proxy should terminate vs. + // TLS that should be passed through. + let f = tls::Connection::accept(socket, config.config) + .map(move |tls| Connection::tls(tls)); + Either::A(f) + }, + Conditional::None(why_no_tls) => { + let f = future::ok(socket) + .map(move |plain| Connection::plain(plain, why_no_tls)); + Either::B(f) + }, }; - let conn = Connection::plain(socket, why_no_tls); - Either::B(future::ok((conn, remote_addr))) + conn.map(move |conn| (conn, remote_addr)) }) .then(|r| { future::ok(match r { diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index 348790252..b2f4e7fbd 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -260,9 +260,16 @@ where config.inbound_router_capacity, config.inbound_router_max_idle_age, ); + let tls_settings = match &config.tls_settings { + Conditional::Some(settings) => Conditional::Some(tls::ConnectionConfig { + identity: settings.service_identity.clone(), + config: tls_server_config + }), + Conditional::None(r) => Conditional::None(*r), + }; serve( inbound_listener, - config.tls_settings.map(|settings| (settings.service_identity, tls_server_config)), + tls_settings, router, config.private_connect_timeout, config.inbound_ports_disable_protocol_detection, @@ -286,7 +293,7 @@ where ); serve( outbound_listener, - None, // No TLS between service & proxy. + Conditional::None(tls::ReasonForNoTls::InternalTraffic), router, config.public_connect_timeout, config.outbound_ports_disable_protocol_detection, @@ -353,7 +360,7 @@ where fn serve( bound_port: BoundPort, - tls_config: Option<(tls::Identity, tls::ServerConfigWatch)>, + tls_config: tls::ConditionalConnectionConfig, router: Router, tcp_connect_timeout: Duration, disable_protocol_detection_ports: IndexSet, @@ -511,8 +518,9 @@ where ); let fut = { let log = log.clone(); + // TODO: serve over TLS. bound_port.listen_and_fold( - None, // TODO: serve over TLS. + Conditional::None(tls::ReasonForNoIdentity::NotImplementedForTap.into()), server, move |server, (session, remote)| { let log = log.clone().with_remote(remote); diff --git a/proxy/src/telemetry/control.rs b/proxy/src/telemetry/control.rs index 44f910007..110c6fdcf 100644 --- a/proxy/src/telemetry/control.rs +++ b/proxy/src/telemetry/control.rs @@ -12,6 +12,8 @@ use super::tap::Taps; use connection; use ctx; use task; +use conditional::Conditional; +use tls; /// A `Control` which has been configured but not initialized. #[derive(Debug)] @@ -102,7 +104,8 @@ impl Control { let fut = { let log = log.clone(); bound_port.listen_and_fold( - None, // TODO: Serve over TLS. + // TODO: Serve over TLS. + Conditional::None(tls::ReasonForNoIdentity::NotImplementedForMetrics.into()), hyper::server::conn::Http::new(), move |hyper, (conn, remote)| { let service = service.clone(); diff --git a/proxy/src/telemetry/metrics/labels.rs b/proxy/src/telemetry/metrics/labels.rs index 223eb308e..0e6aebefc 100644 --- a/proxy/src/telemetry/metrics/labels.rs +++ b/proxy/src/telemetry/metrics/labels.rs @@ -382,6 +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::InternalTraffic) | Conditional::None(tls::ReasonForNoTls::NoIdentity(_)) => Ok(()), } } diff --git a/proxy/src/transport/tls/config.rs b/proxy/src/transport/tls/config.rs index 469afaa4f..d307cd075 100644 --- a/proxy/src/transport/tls/config.rs +++ b/proxy/src/transport/tls/config.rs @@ -78,7 +78,7 @@ pub type ServerConfigWatch = Watch>; /// The configuration in effect for a client (`ClientConfig`) or server /// (`ServerConfig`) TLS connection. #[derive(Clone, Debug)] -pub struct ConnectionConfig where C: Clone + std::fmt::Debug { +pub struct ConnectionConfig where C: Clone { pub identity: Identity, pub config: C, } @@ -94,6 +94,9 @@ pub enum ReasonForNoTls { /// The endpoint's TLS identity is unknown. Without knowing its identity /// we can't validate its certificate. NoIdentity(ReasonForNoIdentity), + + /// The connection is between the proxy and the service + InternalTraffic, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -113,6 +116,14 @@ pub enum ReasonForNoIdentity { /// We haven't implemented the mechanism to construct a TLS identity for /// the controller yet. NotImplementedForController, + + /// We haven't implemented the mechanism to construct a TLs identity for + /// the tap psuedo-service yet. + NotImplementedForTap, + + /// We haven't implemented the mechanism to construct a TLs identity for + /// the metrics psuedo-service yet. + NotImplementedForMetrics, } impl From for ReasonForNoTls { @@ -238,10 +249,10 @@ impl CommonConfig { } -pub fn watch_for_config_changes(settings: Option<&CommonSettings>) +pub fn watch_for_config_changes(settings: Conditional<&CommonSettings, ReasonForNoTls>) -> (ClientConfigWatch, ServerConfigWatch, Box + Send>) { - let settings = if let Some(settings) = settings { + let settings = if let Conditional::Some(settings) = settings { settings.clone() } else { let (client_watch, _) = Watch::new(None); @@ -340,7 +351,7 @@ impl ServerConfig { } pub fn current_connection_config(watch: &ConditionalConnectionConfig>>) - -> ConditionalConnectionConfig where C: Clone + std::fmt::Debug + -> ConditionalConnectionConfig where C: Clone { match watch { Conditional::Some(c) => {