mirror of https://github.com/linkerd/linkerd2.git
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
9bf1e60a61
commit
bc6eb34469
|
@ -2,11 +2,11 @@ use std;
|
||||||
|
|
||||||
/// Like `std::option::Option<C>` but `None` carries a reason why the value
|
/// Like `std::option::Option<C>` but `None` carries a reason why the value
|
||||||
/// isn't available.
|
/// isn't available.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub enum Conditional<C, R>
|
pub enum Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: Clone + std::fmt::Debug,
|
C: Clone,
|
||||||
R: Clone + std::fmt::Debug,
|
R: Clone,
|
||||||
{
|
{
|
||||||
Some(C),
|
Some(C),
|
||||||
None(R),
|
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>
|
impl<C, R> Eq for Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: Eq + Clone + std::fmt::Debug,
|
C: Eq + Clone,
|
||||||
R: Eq + Clone + std::fmt::Debug,
|
R: Eq + Clone,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, R> PartialEq for Conditional<C, R>
|
impl<C, R> PartialEq for Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: PartialEq + Clone + std::fmt::Debug,
|
C: PartialEq + Clone,
|
||||||
R: PartialEq + Clone + std::fmt::Debug,
|
R: PartialEq + Clone,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Conditional<C, R>) -> bool {
|
fn eq(&self, other: &Conditional<C, R>) -> bool {
|
||||||
use self::Conditional::*;
|
use self::Conditional::*;
|
||||||
|
@ -43,8 +56,8 @@ where
|
||||||
|
|
||||||
impl<C, R> std::hash::Hash for Conditional<C, R>
|
impl<C, R> std::hash::Hash for Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: std::hash::Hash + Clone + std::fmt::Debug,
|
C: std::hash::Hash + Clone,
|
||||||
R: std::hash::Hash + Clone + std::fmt::Debug,
|
R: std::hash::Hash + Clone,
|
||||||
{
|
{
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -56,8 +69,8 @@ where
|
||||||
|
|
||||||
impl<C, R> Conditional<C, R>
|
impl<C, R> Conditional<C, R>
|
||||||
where
|
where
|
||||||
C: Clone + std::fmt::Debug,
|
C: Clone,
|
||||||
R: Copy + Clone + std::fmt::Debug,
|
R: Copy + Clone,
|
||||||
{
|
{
|
||||||
pub fn as_ref<'a>(&'a self) -> Conditional<&'a C, R> {
|
pub fn as_ref<'a>(&'a self) -> Conditional<&'a C, R> {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use trust_dns_resolver::config::ResolverOpts;
|
||||||
|
|
||||||
use transport::{Host, HostAndPort, HostAndPortError, tls};
|
use transport::{Host, HostAndPort, HostAndPortError, tls};
|
||||||
use convert::TryFrom;
|
use convert::TryFrom;
|
||||||
|
use conditional::Conditional;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
//
|
//
|
||||||
|
@ -53,7 +54,7 @@ pub struct Config {
|
||||||
|
|
||||||
pub outbound_router_max_idle_age: Duration,
|
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"
|
/// The path to "/etc/resolv.conf"
|
||||||
pub resolv_conf_path: PathBuf,
|
pub resolv_conf_path: PathBuf,
|
||||||
|
@ -306,14 +307,14 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
||||||
Some(pod_name)) => {
|
Some(pod_name)) => {
|
||||||
let service_identity = tls::Identity::try_from_pod_name(&namespaces, pod_name)
|
let service_identity = tls::Identity::try_from_pod_name(&namespaces, pod_name)
|
||||||
.map_err(|_| Error::InvalidEnvVar)?; // Already logged.
|
.map_err(|_| Error::InvalidEnvVar)?; // Already logged.
|
||||||
Ok(Some(tls::CommonSettings {
|
Ok(Conditional::Some(tls::CommonSettings {
|
||||||
trust_anchors,
|
trust_anchors,
|
||||||
end_entity_cert,
|
end_entity_cert,
|
||||||
private_key,
|
private_key,
|
||||||
service_identity,
|
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) => {
|
(trust_anchors, end_entity_cert, private_key, pod_name) => {
|
||||||
if trust_anchors.is_none() {
|
if trust_anchors.is_none() {
|
||||||
error!("{} is not set; it is required when {} and {} are set.",
|
error!("{} is not set; it is required when {} and {} are set.",
|
||||||
|
|
|
@ -111,7 +111,7 @@ impl BoundPort {
|
||||||
// TLS when needed.
|
// TLS when needed.
|
||||||
pub fn listen_and_fold<T, F, Fut>(
|
pub fn listen_and_fold<T, F, Fut>(
|
||||||
self,
|
self,
|
||||||
tls: Option<(tls::Identity, tls::ServerConfigWatch)>,
|
tls: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>,
|
||||||
initial: T,
|
initial: T,
|
||||||
f: F)
|
f: F)
|
||||||
-> impl Future<Item = (), Error = io::Error> + Send + 'static
|
-> impl Future<Item = (), Error = io::Error> + Send + 'static
|
||||||
|
@ -133,6 +133,7 @@ impl BoundPort {
|
||||||
.and_then(move |socket| {
|
.and_then(move |socket| {
|
||||||
let remote_addr = socket.peer_addr()
|
let remote_addr = socket.peer_addr()
|
||||||
.expect("couldn't get remote addr!");
|
.expect("couldn't get remote addr!");
|
||||||
|
|
||||||
// TODO: On Linux and most other platforms it would be better
|
// TODO: On Linux and most other platforms it would be better
|
||||||
// to set the `TCP_NODELAY` option on the bound socket and
|
// to set the `TCP_NODELAY` option on the bound socket and
|
||||||
// then have the listening sockets inherit it. However, that
|
// 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
|
// libraries don't have the necessary API for that, so just
|
||||||
// do it here.
|
// do it here.
|
||||||
set_nodelay_or_warn(&socket);
|
set_nodelay_or_warn(&socket);
|
||||||
let why_no_tls = if let Some((_identity, config_watch)) = &tls {
|
|
||||||
// TODO: use `identity` to differentiate between TLS
|
let conn = match tls::current_connection_config(&tls) {
|
||||||
// that the proxy should terminate vs. TLS that should
|
Conditional::Some(config) => {
|
||||||
// be passed through.
|
// TODO: use `config.identity` to differentiate
|
||||||
if let Some(config) = &*config_watch.borrow() {
|
// between TLS that the proxy should terminate vs.
|
||||||
let f = tls::Connection::accept(socket, config.clone())
|
// TLS that should be passed through.
|
||||||
.map(move |tls| {
|
let f = tls::Connection::accept(socket, config.config)
|
||||||
(Connection::tls(tls), remote_addr)
|
.map(move |tls| Connection::tls(tls));
|
||||||
});
|
Either::A(f)
|
||||||
return Either::A(f);
|
},
|
||||||
} else {
|
Conditional::None(why_no_tls) => {
|
||||||
// No valid TLS configuration.
|
let f = future::ok(socket)
|
||||||
tls::ReasonForNoTls::NoConfig
|
.map(move |plain| Connection::plain(plain, why_no_tls));
|
||||||
}
|
Either::B(f)
|
||||||
} else {
|
},
|
||||||
tls::ReasonForNoTls::Disabled
|
|
||||||
};
|
};
|
||||||
let conn = Connection::plain(socket, why_no_tls);
|
conn.map(move |conn| (conn, remote_addr))
|
||||||
Either::B(future::ok((conn, remote_addr)))
|
|
||||||
})
|
})
|
||||||
.then(|r| {
|
.then(|r| {
|
||||||
future::ok(match r {
|
future::ok(match r {
|
||||||
|
|
|
@ -260,9 +260,16 @@ where
|
||||||
config.inbound_router_capacity,
|
config.inbound_router_capacity,
|
||||||
config.inbound_router_max_idle_age,
|
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(
|
serve(
|
||||||
inbound_listener,
|
inbound_listener,
|
||||||
config.tls_settings.map(|settings| (settings.service_identity, tls_server_config)),
|
tls_settings,
|
||||||
router,
|
router,
|
||||||
config.private_connect_timeout,
|
config.private_connect_timeout,
|
||||||
config.inbound_ports_disable_protocol_detection,
|
config.inbound_ports_disable_protocol_detection,
|
||||||
|
@ -286,7 +293,7 @@ where
|
||||||
);
|
);
|
||||||
serve(
|
serve(
|
||||||
outbound_listener,
|
outbound_listener,
|
||||||
None, // No TLS between service & proxy.
|
Conditional::None(tls::ReasonForNoTls::InternalTraffic),
|
||||||
router,
|
router,
|
||||||
config.public_connect_timeout,
|
config.public_connect_timeout,
|
||||||
config.outbound_ports_disable_protocol_detection,
|
config.outbound_ports_disable_protocol_detection,
|
||||||
|
@ -353,7 +360,7 @@ where
|
||||||
|
|
||||||
fn serve<R, B, E, F, G>(
|
fn serve<R, B, E, F, G>(
|
||||||
bound_port: BoundPort,
|
bound_port: BoundPort,
|
||||||
tls_config: Option<(tls::Identity, tls::ServerConfigWatch)>,
|
tls_config: tls::ConditionalConnectionConfig<tls::ServerConfigWatch>,
|
||||||
router: Router<R>,
|
router: Router<R>,
|
||||||
tcp_connect_timeout: Duration,
|
tcp_connect_timeout: Duration,
|
||||||
disable_protocol_detection_ports: IndexSet<u16>,
|
disable_protocol_detection_ports: IndexSet<u16>,
|
||||||
|
@ -511,8 +518,9 @@ where
|
||||||
);
|
);
|
||||||
let fut = {
|
let fut = {
|
||||||
let log = log.clone();
|
let log = log.clone();
|
||||||
|
// TODO: serve over TLS.
|
||||||
bound_port.listen_and_fold(
|
bound_port.listen_and_fold(
|
||||||
None, // TODO: serve over TLS.
|
Conditional::None(tls::ReasonForNoIdentity::NotImplementedForTap.into()),
|
||||||
server,
|
server,
|
||||||
move |server, (session, remote)| {
|
move |server, (session, remote)| {
|
||||||
let log = log.clone().with_remote(remote);
|
let log = log.clone().with_remote(remote);
|
||||||
|
|
|
@ -12,6 +12,8 @@ use super::tap::Taps;
|
||||||
use connection;
|
use connection;
|
||||||
use ctx;
|
use ctx;
|
||||||
use task;
|
use task;
|
||||||
|
use conditional::Conditional;
|
||||||
|
use tls;
|
||||||
|
|
||||||
/// A `Control` which has been configured but not initialized.
|
/// A `Control` which has been configured but not initialized.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -102,7 +104,8 @@ impl Control {
|
||||||
let fut = {
|
let fut = {
|
||||||
let log = log.clone();
|
let log = log.clone();
|
||||||
bound_port.listen_and_fold(
|
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(),
|
hyper::server::conn::Http::new(),
|
||||||
move |hyper, (conn, remote)| {
|
move |hyper, (conn, remote)| {
|
||||||
let service = service.clone();
|
let service = service.clone();
|
||||||
|
|
|
@ -382,6 +382,7 @@ impl fmt::Display for ctx::transport::TlsStatus {
|
||||||
Conditional::Some(()) => f.pad(",tls=\"true\""),
|
Conditional::Some(()) => f.pad(",tls=\"true\""),
|
||||||
Conditional::None(tls::ReasonForNoTls::NoConfig) => f.pad(",tls=\"no_config\""),
|
Conditional::None(tls::ReasonForNoTls::NoConfig) => f.pad(",tls=\"no_config\""),
|
||||||
Conditional::None(tls::ReasonForNoTls::Disabled) |
|
Conditional::None(tls::ReasonForNoTls::Disabled) |
|
||||||
|
Conditional::None(tls::ReasonForNoTls::InternalTraffic) |
|
||||||
Conditional::None(tls::ReasonForNoTls::NoIdentity(_)) => Ok(()),
|
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
|
/// The configuration in effect for a client (`ClientConfig`) or server
|
||||||
/// (`ServerConfig`) TLS connection.
|
/// (`ServerConfig`) TLS connection.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ConnectionConfig<C> where C: Clone + std::fmt::Debug {
|
pub struct ConnectionConfig<C> where C: Clone {
|
||||||
pub identity: Identity,
|
pub identity: Identity,
|
||||||
pub config: C,
|
pub config: C,
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,9 @@ pub enum ReasonForNoTls {
|
||||||
/// The endpoint's TLS identity is unknown. Without knowing its identity
|
/// The endpoint's TLS identity is unknown. Without knowing its identity
|
||||||
/// we can't validate its certificate.
|
/// we can't validate its certificate.
|
||||||
NoIdentity(ReasonForNoIdentity),
|
NoIdentity(ReasonForNoIdentity),
|
||||||
|
|
||||||
|
/// The connection is between the proxy and the service
|
||||||
|
InternalTraffic,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[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
|
/// We haven't implemented the mechanism to construct a TLS identity for
|
||||||
/// the controller yet.
|
/// the controller yet.
|
||||||
NotImplementedForController,
|
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 {
|
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>)
|
-> (ClientConfigWatch, ServerConfigWatch, Box<Future<Item = (), Error = ()> + Send>)
|
||||||
{
|
{
|
||||||
let settings = if let Some(settings) = settings {
|
let settings = if let Conditional::Some(settings) = settings {
|
||||||
settings.clone()
|
settings.clone()
|
||||||
} else {
|
} else {
|
||||||
let (client_watch, _) = Watch::new(None);
|
let (client_watch, _) = Watch::new(None);
|
||||||
|
@ -340,7 +351,7 @@ impl ServerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_connection_config<C>(watch: &ConditionalConnectionConfig<Watch<Option<C>>>)
|
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 {
|
match watch {
|
||||||
Conditional::Some(c) => {
|
Conditional::Some(c) => {
|
||||||
|
|
Loading…
Reference in New Issue