Proxy: Implement TLS conditional accept more like TLS conditional connect. (#1166)

* Proxy: Implement TLS conditional accept more like TLS conditional connect.

Clean up the accept side of the TLS configuration logic.

Signed-off-by: Brian Smith <brian@briansmith.org>
This commit is contained in:
Brian Smith 2018-06-20 19:46:31 -10:00 committed by GitHub
parent 757ccd8fc9
commit 71ea2c1b59
7 changed files with 78 additions and 42 deletions

View File

@ -2,11 +2,11 @@ use std;
/// Like `std::option::Option<C>` but `None` carries a reason why the value
/// isn't available.
#[derive(Clone, Debug)]
#[derive(Clone)]
pub enum Conditional<C, R>
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<C, R> std::fmt::Debug for Conditional<C, R>
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<C, R> Eq for Conditional<C, R>
where
C: Eq + Clone + std::fmt::Debug,
R: Eq + Clone + std::fmt::Debug,
C: Eq + Clone,
R: Eq + Clone,
{
}
impl<C, R> PartialEq for Conditional<C, R>
where
C: PartialEq + Clone + std::fmt::Debug,
R: PartialEq + Clone + std::fmt::Debug,
C: PartialEq + Clone,
R: PartialEq + Clone,
{
fn eq(&self, other: &Conditional<C, R>) -> bool {
use self::Conditional::*;
@ -43,8 +56,8 @@ where
impl<C, R> std::hash::Hash for Conditional<C, R>
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<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
@ -56,8 +69,8 @@ where
impl<C, R> Conditional<C, R>
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 {

View File

@ -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<tls::CommonSettings>,
pub tls_settings: Conditional<tls::CommonSettings, tls::ReasonForNoTls>,
/// 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.",

View File

@ -111,7 +111,7 @@ impl BoundPort {
// TLS when needed.
pub fn listen_and_fold<T, F, Fut>(
self,
tls: Option<(tls::Identity, tls::ServerConfigWatch)>,
tls: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>,
initial: T,
f: F)
-> impl Future<Item = (), Error = io::Error> + 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 {

View File

@ -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<R, B, E, F, G>(
bound_port: BoundPort,
tls_config: Option<(tls::Identity, tls::ServerConfigWatch)>,
tls_config: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>,
router: Router<R>,
tcp_connect_timeout: Duration,
disable_protocol_detection_ports: IndexSet<u16>,
@ -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);

View File

@ -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();

View File

@ -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(()),
}
}

View File

@ -78,7 +78,7 @@ pub type ServerConfigWatch = Watch<Option<ServerConfig>>;
/// The configuration in effect for a client (`ClientConfig`) or server
/// (`ServerConfig`) TLS connection.
#[derive(Clone, Debug)]
pub struct ConnectionConfig<C> where C: Clone + std::fmt::Debug {
pub struct ConnectionConfig<C> 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<ReasonForNoIdentity> 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<Future<Item = (), Error = ()> + 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<C>(watch: &ConditionalConnectionConfig<Watch<Option<C>>>)
-> ConditionalConnectionConfig<C> where C: Clone + std::fmt::Debug
-> ConditionalConnectionConfig<C> where C: Clone
{
match watch {
Conditional::Some(c) => {