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:
parent
757ccd8fc9
commit
71ea2c1b59
|
@ -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 {
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in New Issue