mirror of https://github.com/linkerd/linkerd2.git
Proxy: Add TLS client infrastructure. (#1158)
Move TLS cipher suite configuration to tls::config. Use the same configuration to act as a client and a server. Signed-off-by: Brian Smith <brian@briansmith.org>
This commit is contained in:
parent
9bfd8898e0
commit
8ece9c4508
|
@ -7,6 +7,7 @@ extern crate test;
|
|||
|
||||
use conduit_proxy::{
|
||||
ctx,
|
||||
conditional::Conditional,
|
||||
control::destination,
|
||||
telemetry::{
|
||||
event,
|
||||
|
@ -18,28 +19,26 @@ use std::{
|
|||
fmt,
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant, SystemTime},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use test::Bencher;
|
||||
use conduit_proxy::tls;
|
||||
|
||||
const REQUESTS: usize = 100;
|
||||
|
||||
const TLS_DISABLED: Conditional<(), tls::ReasonForNoTls> =
|
||||
Conditional::None(tls::ReasonForNoTls::Disabled);
|
||||
|
||||
fn addr() -> SocketAddr {
|
||||
([1, 2, 3, 4], 5678).into()
|
||||
}
|
||||
|
||||
fn process() -> Arc<ctx::Process> {
|
||||
Arc::new(ctx::Process {
|
||||
scheduled_namespace: "test".into(),
|
||||
start_time: SystemTime::now(),
|
||||
})
|
||||
ctx::Process::test("test")
|
||||
}
|
||||
|
||||
fn server(proxy: &Arc<ctx::Proxy>) -> Arc<ctx::transport::Server> {
|
||||
ctx::transport::Server::new(
|
||||
&proxy, &addr(), &addr(), &Some(addr()),
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
)
|
||||
ctx::transport::Server::new(&proxy, &addr(), &addr(), &Some(addr()), TLS_DISABLED)
|
||||
}
|
||||
|
||||
fn client<L, S>(proxy: &Arc<ctx::Proxy>, labels: L) -> Arc<ctx::transport::Client>
|
||||
|
@ -51,7 +50,7 @@ where
|
|||
&proxy,
|
||||
&addr(),
|
||||
destination::Metadata::new(metrics::DstLabels::new(labels), None),
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
TLS_DISABLED,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ use ctx;
|
|||
use telemetry::{self, sensor};
|
||||
use transparency::{self, HttpBody, h1};
|
||||
use transport;
|
||||
use tls;
|
||||
use ctx::transport::TlsStatus;
|
||||
|
||||
/// Binds a `Service` from a `SocketAddr`.
|
||||
///
|
||||
|
@ -205,18 +207,20 @@ where
|
|||
fn bind_stack(&self, ep: &Endpoint, protocol: &Protocol) -> Stack<B> {
|
||||
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());
|
||||
|
||||
let client_ctx = ctx::transport::Client::new(
|
||||
&self.ctx,
|
||||
&addr,
|
||||
ep.metadata().clone(),
|
||||
// TODO: when we can use TLS for client connections, indicate
|
||||
// whether or not the connection was TLS here.
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
TlsStatus::from(&tls),
|
||||
);
|
||||
|
||||
// Map a socket address to a connection.
|
||||
let connect = self.sensors.connect(
|
||||
transport::Connect::new(addr, ep.tls_identity().cloned()),
|
||||
transport::Connect::new(addr, tls),
|
||||
&client_ctx,
|
||||
);
|
||||
|
||||
|
@ -266,7 +270,9 @@ where
|
|||
|
||||
|
||||
impl<C, B> Bind<C, B> {
|
||||
pub fn with_protocol(self, protocol: Protocol) -> BindProtocol<C, B> {
|
||||
pub fn with_protocol(self, protocol: Protocol)
|
||||
-> BindProtocol<C, B>
|
||||
{
|
||||
BindProtocol {
|
||||
bind: self,
|
||||
protocol,
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
use std;
|
||||
|
||||
/// Like `std::option::Option<C>` but `None` carries a reason why the value
|
||||
/// isn't available.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Conditional<C, R>
|
||||
where
|
||||
C: Clone + std::fmt::Debug,
|
||||
R: Clone + std::fmt::Debug,
|
||||
{
|
||||
Some(C),
|
||||
None(R),
|
||||
}
|
||||
|
||||
impl<C, R> Copy for Conditional<C, R>
|
||||
where
|
||||
C: Copy + Clone + std::fmt::Debug,
|
||||
R: Copy + Clone + std::fmt::Debug,
|
||||
{
|
||||
}
|
||||
|
||||
impl<C, R> Eq for Conditional<C, R>
|
||||
where
|
||||
C: Eq + Clone + std::fmt::Debug,
|
||||
R: Eq + Clone + std::fmt::Debug,
|
||||
{
|
||||
}
|
||||
|
||||
impl<C, R> PartialEq for Conditional<C, R>
|
||||
where
|
||||
C: PartialEq + Clone + std::fmt::Debug,
|
||||
R: PartialEq + Clone + std::fmt::Debug,
|
||||
{
|
||||
fn eq(&self, other: &Conditional<C, R>) -> bool {
|
||||
use self::Conditional::*;
|
||||
match (self, other) {
|
||||
(Some(a), Some(b)) => a.eq(b),
|
||||
(None(a), None(b)) => a.eq(b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Conditional::Some(c) => c.hash(state),
|
||||
Conditional::None(r) => r.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,32 +9,34 @@ use tokio::{
|
|||
net::{TcpListener, TcpStream, ConnectFuture},
|
||||
reactor::Handle,
|
||||
};
|
||||
|
||||
use conditional::Conditional;
|
||||
use ctx::transport::TlsStatus;
|
||||
use config::Addr;
|
||||
use transport::{GetOriginalDst, Io, tls};
|
||||
|
||||
pub type PlaintextSocket = TcpStream;
|
||||
|
||||
pub struct BoundPort {
|
||||
inner: std::net::TcpListener,
|
||||
local_addr: SocketAddr,
|
||||
}
|
||||
|
||||
/// Initiates a client connection to the given address.
|
||||
pub fn connect(addr: &SocketAddr) -> Connecting {
|
||||
Connecting {
|
||||
inner: PlaintextSocket::connect(addr),
|
||||
// TODO: when we can open TLS client connections, this is where we will
|
||||
// indicate that for telemetry.
|
||||
tls_status: TlsStatus::Disabled,
|
||||
pub fn connect(addr: &SocketAddr,
|
||||
tls: tls::ConditionalConnectionConfig<tls::ClientConfig>)
|
||||
-> Connecting
|
||||
{
|
||||
Connecting::Plaintext {
|
||||
connect: TcpStream::connect(addr),
|
||||
tls: Some(tls),
|
||||
}
|
||||
}
|
||||
|
||||
/// A socket that is in the process of connecting.
|
||||
pub struct Connecting {
|
||||
inner: ConnectFuture,
|
||||
tls_status: TlsStatus,
|
||||
pub enum Connecting {
|
||||
Plaintext {
|
||||
connect: ConnectFuture,
|
||||
tls: Option<tls::ConditionalConnectionConfig<tls::ClientConfig>>,
|
||||
},
|
||||
UpgradeToTls(tls::UpgradeClientToTls),
|
||||
}
|
||||
|
||||
/// Abstracts a plaintext socket vs. a TLS decorated one.
|
||||
|
@ -138,7 +140,7 @@ impl BoundPort {
|
|||
// libraries don't have the necessary API for that, so just
|
||||
// do it here.
|
||||
set_nodelay_or_warn(&socket);
|
||||
let tls_status = if let Some((_identity, config_watch)) = &tls {
|
||||
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.
|
||||
|
@ -150,12 +152,12 @@ impl BoundPort {
|
|||
return Either::A(f);
|
||||
} else {
|
||||
// No valid TLS configuration.
|
||||
TlsStatus::NoConfig
|
||||
tls::ReasonForNoTls::NoConfig
|
||||
}
|
||||
} else {
|
||||
TlsStatus::Disabled
|
||||
tls::ReasonForNoTls::Disabled
|
||||
};
|
||||
let conn = Connection::new(socket, tls_status);
|
||||
let conn = Connection::plain(socket, why_no_tls);
|
||||
Either::B(future::ok((conn, remote_addr)))
|
||||
})
|
||||
.then(|r| {
|
||||
|
@ -181,28 +183,47 @@ impl Future for Connecting {
|
|||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let socket = try_ready!(self.inner.poll());
|
||||
set_nodelay_or_warn(&socket);
|
||||
Ok(Async::Ready(Connection::new(socket, self.tls_status)))
|
||||
loop {
|
||||
*self = match self {
|
||||
Connecting::Plaintext { connect, tls } => {
|
||||
let plaintext_stream = try_ready!(connect.poll());
|
||||
set_nodelay_or_warn(&plaintext_stream);
|
||||
match tls.take().expect("Polled after ready") {
|
||||
Conditional::Some(config) => {
|
||||
let upgrade_to_tls = tls::Connection::connect(
|
||||
plaintext_stream, &config.identity, config.config);
|
||||
Connecting::UpgradeToTls(upgrade_to_tls)
|
||||
},
|
||||
Conditional::None(why) => {
|
||||
return Ok(Async::Ready(Connection::plain(plaintext_stream, why)));
|
||||
},
|
||||
}
|
||||
},
|
||||
Connecting::UpgradeToTls(upgrading) => {
|
||||
let tls_stream = try_ready!(upgrading.poll());
|
||||
return Ok(Async::Ready(Connection::tls(tls_stream)));
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Connection =====
|
||||
|
||||
impl Connection {
|
||||
fn new<I: Io + 'static>(io: I, tls_status: TlsStatus) -> Self {
|
||||
fn plain(io: TcpStream, why_no_tls: tls::ReasonForNoTls) -> Self {
|
||||
Connection {
|
||||
io: Box::new(io),
|
||||
peek_buf: BytesMut::new(),
|
||||
tls_status,
|
||||
tls_status: Conditional::None(why_no_tls),
|
||||
}
|
||||
}
|
||||
|
||||
fn tls(tls: tls::Connection) -> Self {
|
||||
Connection {
|
||||
fn tls<S: tls::Session + std::fmt::Debug + 'static>(tls: tls::Connection<S>) -> Self {
|
||||
Connection {
|
||||
io: Box::new(tls),
|
||||
peek_buf: BytesMut::new(),
|
||||
tls_status: TlsStatus::Success,
|
||||
tls_status: Conditional::Some(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +333,7 @@ impl<T: Peek> Future for PeekFuture<T> {
|
|||
|
||||
// Misc.
|
||||
|
||||
fn set_nodelay_or_warn(socket: &PlaintextSocket) {
|
||||
fn set_nodelay_or_warn(socket: &TcpStream) {
|
||||
if let Err(e) = socket.set_nodelay(true) {
|
||||
warn!(
|
||||
"could not set TCP_NODELAY on {:?}/{:?}: {}",
|
||||
|
|
|
@ -10,16 +10,20 @@
|
|||
use config;
|
||||
use std::time::SystemTime;
|
||||
use std::sync::Arc;
|
||||
use transport::tls;
|
||||
|
||||
pub mod http;
|
||||
pub mod transport;
|
||||
|
||||
/// Describes a single running proxy instance.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Process {
|
||||
/// Identifies the Kubernetes namespace in which this proxy is process.
|
||||
pub scheduled_namespace: String,
|
||||
|
||||
pub start_time: SystemTime,
|
||||
|
||||
tls_client_config: tls::ClientConfigWatch,
|
||||
}
|
||||
|
||||
/// Indicates the orientation of traffic, relative to a sidecar proxy.
|
||||
|
@ -29,27 +33,30 @@ pub struct Process {
|
|||
/// local instance.
|
||||
/// - The _outbound_ proxy receives traffic from the local instance and forwards it to a
|
||||
/// remove service.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Proxy {
|
||||
Inbound(Arc<Process>),
|
||||
Outbound(Arc<Process>),
|
||||
}
|
||||
|
||||
impl Process {
|
||||
#[cfg(test)]
|
||||
// Test-only, but we can't use `#[cfg(test)]` because it is used by the
|
||||
// benchmarks
|
||||
pub fn test(ns: &str) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
scheduled_namespace: ns.into(),
|
||||
start_time: SystemTime::now(),
|
||||
tls_client_config: tls::ClientConfig::no_tls(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a new `Process` from environment variables.
|
||||
pub fn new(config: &config::Config) -> Arc<Self> {
|
||||
pub fn new(config: &config::Config, tls_client_config: tls::ClientConfigWatch) -> Arc<Self> {
|
||||
let start_time = SystemTime::now();
|
||||
Arc::new(Self {
|
||||
scheduled_namespace: config.namespaces.pod.clone(),
|
||||
start_time,
|
||||
tls_client_config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +80,12 @@ impl Proxy {
|
|||
pub fn is_outbound(&self) -> bool {
|
||||
!self.is_inbound()
|
||||
}
|
||||
|
||||
pub fn tls_client_config_watch(&self) -> &tls::ClientConfigWatch {
|
||||
match self {
|
||||
Proxy::Inbound(process) | Proxy::Outbound(process) => &process.tls_client_config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -82,7 +95,6 @@ pub mod test_util {
|
|||
fmt,
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use ctx;
|
||||
|
@ -94,10 +106,7 @@ pub mod test_util {
|
|||
}
|
||||
|
||||
pub fn process() -> Arc<ctx::Process> {
|
||||
Arc::new(ctx::Process {
|
||||
scheduled_namespace: "test".into(),
|
||||
start_time: SystemTime::now(),
|
||||
})
|
||||
ctx::Process::test("test")
|
||||
}
|
||||
|
||||
pub fn server(
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::{
|
||||
self,
|
||||
net::{IpAddr, SocketAddr},
|
||||
sync::Arc,
|
||||
};
|
||||
use ctx;
|
||||
use control::destination;
|
||||
use telemetry::metrics::DstLabels;
|
||||
use transport::tls;
|
||||
use conditional::Conditional;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Ctx {
|
||||
|
@ -33,19 +36,20 @@ pub struct Client {
|
|||
|
||||
/// Identifies whether or not a connection was secured with TLS,
|
||||
/// and, if it was not, the reason why.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum TlsStatus {
|
||||
/// The TLS handshake was successful.
|
||||
Success,
|
||||
/// TLS was not enabled for this connection.
|
||||
Disabled,
|
||||
/// TLS was enabled for this connection, but we have no valid
|
||||
/// config.
|
||||
NoConfig,
|
||||
// TODO: When the proxy falls back to plaintext on handshake
|
||||
// failures, we'll want to add a variant for that here as well.
|
||||
pub type TlsStatus = Conditional<(), tls::ReasonForNoTls>;
|
||||
|
||||
impl TlsStatus {
|
||||
pub fn from<C>(c: &Conditional<C, tls::ReasonForNoTls>) -> Self
|
||||
where C: Clone + std::fmt::Debug
|
||||
{
|
||||
match c {
|
||||
Conditional::Some(_) => Conditional::Some(()),
|
||||
Conditional::None(r) => Conditional::None(*r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Ctx {
|
||||
pub fn proxy(&self) -> &Arc<ctx::Proxy> {
|
||||
match *self {
|
||||
|
|
|
@ -109,6 +109,8 @@ mod tests {
|
|||
use super::Inbound;
|
||||
use bind::{self, Bind, Host};
|
||||
use ctx;
|
||||
use conditional::Conditional;
|
||||
use tls;
|
||||
|
||||
fn new_inbound(default: Option<net::SocketAddr>, ctx: &Arc<ctx::Proxy>) -> Inbound<()> {
|
||||
let bind = Bind::new().with_ctx(ctx.clone());
|
||||
|
@ -123,6 +125,9 @@ mod tests {
|
|||
(addr, protocol)
|
||||
}
|
||||
|
||||
const TLS_DISABLED: Conditional<(), tls::ReasonForNoTls> =
|
||||
Conditional::None(tls::ReasonForNoTls::Disabled);
|
||||
|
||||
quickcheck! {
|
||||
fn recognize_orig_dst(
|
||||
orig_dst: net::SocketAddr,
|
||||
|
@ -134,9 +139,7 @@ mod tests {
|
|||
let inbound = new_inbound(None, &ctx);
|
||||
|
||||
let srv_ctx = ctx::transport::Server::new(
|
||||
&ctx, &local, &remote, &Some(orig_dst),
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
);
|
||||
&ctx, &local, &remote, &Some(orig_dst), TLS_DISABLED);
|
||||
|
||||
let rec = srv_ctx.orig_dst_if_not_local().map(make_key_http1);
|
||||
|
||||
|
@ -163,7 +166,7 @@ mod tests {
|
|||
&local,
|
||||
&remote,
|
||||
&None,
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
TLS_DISABLED,
|
||||
));
|
||||
|
||||
inbound.recognize(&req) == default.map(make_key_http1)
|
||||
|
@ -195,7 +198,7 @@ mod tests {
|
|||
&local,
|
||||
&remote,
|
||||
&Some(local),
|
||||
ctx::transport::TlsStatus::Disabled,
|
||||
TLS_DISABLED,
|
||||
));
|
||||
|
||||
inbound.recognize(&req) == default.map(make_key_http1)
|
||||
|
|
|
@ -70,6 +70,7 @@ pub mod app;
|
|||
mod bind;
|
||||
pub mod config;
|
||||
mod connection;
|
||||
pub mod conditional;
|
||||
pub mod control;
|
||||
pub mod convert;
|
||||
pub mod ctx;
|
||||
|
@ -179,7 +180,10 @@ where
|
|||
where
|
||||
F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
{
|
||||
let process_ctx = ctx::Process::new(&self.config);
|
||||
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 Main {
|
||||
config,
|
||||
|
@ -237,9 +241,6 @@ where
|
|||
|
||||
let bind = Bind::new().with_sensors(sensors.clone());
|
||||
|
||||
let (_tls_client_config, tls_server_config, tls_cfg_bg) =
|
||||
tls::watch_for_config_changes(config.tls_settings.as_ref());
|
||||
|
||||
// Setup the public listener. This will listen on a publicly accessible
|
||||
// address and listen for inbound connections that should be forwarded
|
||||
// to the managed application (private destination).
|
||||
|
|
|
@ -7,6 +7,8 @@ use http;
|
|||
|
||||
use ctx;
|
||||
use telemetry::event;
|
||||
use tls;
|
||||
use conditional::Conditional;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct RequestLabels {
|
||||
|
@ -376,11 +378,12 @@ impl fmt::Display for TransportCloseLabels {
|
|||
// TODO: There's got to be a nicer way to handle this.
|
||||
impl fmt::Display for ctx::transport::TlsStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use ctx::transport::TlsStatus;
|
||||
match *self {
|
||||
TlsStatus::Disabled => Ok(()),
|
||||
TlsStatus::NoConfig => f.pad(",tls=\"no_config\""),
|
||||
TlsStatus::Success => f.pad(",tls=\"true\""),
|
||||
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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,11 @@ mod tests {
|
|||
use ctx::test_util::*;
|
||||
use telemetry::event;
|
||||
use super::*;
|
||||
use conditional::Conditional;
|
||||
use tls;
|
||||
|
||||
const TLS_DISABLED: Conditional<(), tls::ReasonForNoTls> =
|
||||
Conditional::None(tls::ReasonForNoTls::Disabled);
|
||||
|
||||
fn mock_route(
|
||||
root: &mut Root,
|
||||
|
@ -285,7 +290,7 @@ mod tests {
|
|||
server: &Arc<ctx::transport::Server>,
|
||||
team: &str
|
||||
) {
|
||||
let client = client(&proxy, vec![("team", team)], ctx::transport::TlsStatus::Disabled);
|
||||
let client = client(&proxy, vec![("team", team)], TLS_DISABLED);
|
||||
let (req, rsp) = request("http://nba.com", &server, &client);
|
||||
|
||||
let client_transport = Arc::new(ctx::transport::Ctx::Client(client));
|
||||
|
@ -310,7 +315,7 @@ mod tests {
|
|||
let process = process();
|
||||
let proxy = ctx::Proxy::outbound(&process);
|
||||
|
||||
let server = server(&proxy, ctx::transport::TlsStatus::Disabled);
|
||||
let server = server(&proxy, TLS_DISABLED);
|
||||
let server_transport = Arc::new(ctx::transport::Ctx::Server(server.clone()));
|
||||
|
||||
let mut root = Root::default();
|
||||
|
|
|
@ -95,7 +95,12 @@ mod test {
|
|||
};
|
||||
use ctx::{self, test_util::*, transport::TlsStatus};
|
||||
use std::time::{Duration, Instant};
|
||||
use conditional::Conditional;
|
||||
use tls;
|
||||
|
||||
const TLS_ENABLED: Conditional<(), tls::ReasonForNoTls> = Conditional::Some(());
|
||||
const TLS_DISABLED: Conditional<(), tls::ReasonForNoTls> =
|
||||
Conditional::None(tls::ReasonForNoTls::Disabled);
|
||||
|
||||
fn test_record_response_end_outbound(client_tls: TlsStatus, server_tls: TlsStatus) {
|
||||
let process = process();
|
||||
|
@ -325,41 +330,41 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn record_one_conn_request_outbound_client_tls() {
|
||||
test_record_one_conn_request_outbound(TlsStatus::Success, TlsStatus::Disabled)
|
||||
test_record_one_conn_request_outbound(TLS_ENABLED, TLS_DISABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_one_conn_request_outbound_server_tls() {
|
||||
test_record_one_conn_request_outbound(TlsStatus::Disabled, TlsStatus::Success)
|
||||
test_record_one_conn_request_outbound(TLS_DISABLED, TLS_ENABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_one_conn_request_outbound_both_tls() {
|
||||
test_record_one_conn_request_outbound(TlsStatus::Success, TlsStatus::Success)
|
||||
test_record_one_conn_request_outbound(TLS_ENABLED, TLS_ENABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_one_conn_request_outbound_no_tls() {
|
||||
test_record_one_conn_request_outbound(TlsStatus::Disabled, TlsStatus::Disabled)
|
||||
test_record_one_conn_request_outbound(TLS_DISABLED, TLS_DISABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_response_end_outbound_client_tls() {
|
||||
test_record_response_end_outbound(TlsStatus::Success, TlsStatus::Disabled)
|
||||
test_record_response_end_outbound(TLS_ENABLED, TLS_DISABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_response_end_outbound_server_tls() {
|
||||
test_record_response_end_outbound(TlsStatus::Disabled, TlsStatus::Success)
|
||||
test_record_response_end_outbound(TLS_DISABLED, TLS_ENABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_response_end_outbound_both_tls() {
|
||||
test_record_response_end_outbound(TlsStatus::Success, TlsStatus::Success)
|
||||
test_record_response_end_outbound(TLS_ENABLED, TLS_ENABLED)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_response_end_outbound_no_tls() {
|
||||
test_record_response_end_outbound(TlsStatus::Disabled, TlsStatus::Disabled)
|
||||
test_record_response_end_outbound(TLS_DISABLED, TLS_DISABLED)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,16 @@ use futures::{future, Async, Future, Poll};
|
|||
use tokio_connect::Connect;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use conditional::Conditional;
|
||||
use control::destination;
|
||||
use ctx::transport::{
|
||||
Client as ClientCtx,
|
||||
Server as ServerCtx,
|
||||
TlsStatus,
|
||||
};
|
||||
use telemetry::Sensors;
|
||||
use timeout::Timeout;
|
||||
use transport;
|
||||
use transport::{self, tls};
|
||||
use ctx::transport::TlsStatus;
|
||||
|
||||
/// TCP Server Proxy
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -59,19 +60,16 @@ impl Proxy {
|
|||
return future::Either::B(future::ok(()));
|
||||
};
|
||||
|
||||
let tls = Conditional::None(tls::ReasonForNoTls::NotImplementedForNonHttp); // TODO
|
||||
|
||||
let client_ctx = ClientCtx::new(
|
||||
&srv_ctx.proxy,
|
||||
&orig_dst,
|
||||
destination::Metadata::no_metadata(),
|
||||
// A raw TCP client connection may be or may not be TLS traffic,
|
||||
// but the `TlsStatus` field indicates whether _the proxy_ is
|
||||
// responsible for the encryption, so set this to "Disabled".
|
||||
// XXX: Should raw TCP connections have a different TLS status
|
||||
// from HTTP connections for which TLS is disabled?
|
||||
TlsStatus::Disabled,
|
||||
TlsStatus::from(&tls),
|
||||
);
|
||||
let c = Timeout::new(
|
||||
transport::Connect::new(orig_dst, None), // No TLS.
|
||||
transport::Connect::new(orig_dst, tls),
|
||||
self.connect_timeout,
|
||||
);
|
||||
let connect = self.sensors.connect(c, &client_ctx);
|
||||
|
|
|
@ -11,10 +11,12 @@ use connection;
|
|||
use convert::TryFrom;
|
||||
use dns;
|
||||
use transport::tls;
|
||||
use conditional::Conditional;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Connect {
|
||||
addr: SocketAddr,
|
||||
tls: tls::ConditionalConnectionConfig<tls::ClientConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -102,12 +104,11 @@ impl Connect {
|
|||
/// Returns a `Connect` to `addr`.
|
||||
pub fn new(
|
||||
addr: SocketAddr,
|
||||
tls_identity: Option<tls::Identity>,
|
||||
tls: tls::ConditionalConnectionConfig<tls::ClientConfig>,
|
||||
) -> Self {
|
||||
// TODO: this is currently unused.
|
||||
let _ = tls_identity;
|
||||
Self {
|
||||
addr,
|
||||
tls,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +119,7 @@ impl tokio_connect::Connect for Connect {
|
|||
type Future = connection::Connecting;
|
||||
|
||||
fn connect(&self) -> Self::Future {
|
||||
connection::connect(&self.addr)
|
||||
connection::connect(&self.addr, self.tls.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,6 +145,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 c = self.dns_resolver
|
||||
.resolve_one_ip(&self.host_and_port.host)
|
||||
.map_err(|_| {
|
||||
|
@ -153,7 +155,7 @@ impl tokio_connect::Connect for LookupAddressAndConnect {
|
|||
info!("DNS resolved {:?} to {}", host, ip_addr);
|
||||
let addr = SocketAddr::from((ip_addr, port));
|
||||
trace!("connect {}", addr);
|
||||
connection::connect(&addr)
|
||||
connection::connect(&addr, tls)
|
||||
});
|
||||
Box::new(c)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ use super::{
|
|||
};
|
||||
use ring::{self, rand, signature};
|
||||
|
||||
/// Manages the use of the private key and certificate.
|
||||
///
|
||||
/// Authentication is symmetric with respect to the client/server roles, so the
|
||||
/// same certificate and private key is used for both roles.
|
||||
pub struct CertResolver {
|
||||
certified_key: rustls::sign::CertifiedKey,
|
||||
}
|
||||
|
@ -48,7 +52,8 @@ impl CertResolver {
|
|||
private_key: untrusted::Input)
|
||||
-> Result<Self, config::Error>
|
||||
{
|
||||
let private_key = signature::key_pair_from_pkcs8(SIGNATURE_ALG_RING_SIGNING, private_key)
|
||||
let private_key =
|
||||
signature::key_pair_from_pkcs8(config::SIGNATURE_ALG_RING_SIGNING, private_key)
|
||||
.map_err(|ring::error::Unspecified| config::Error::InvalidPrivateKey)?;
|
||||
|
||||
let signer = Signer { private_key: Arc::new(private_key) };
|
||||
|
@ -57,6 +62,17 @@ impl CertResolver {
|
|||
cert_chain, Arc::new(Box::new(signing_key)));
|
||||
Ok(Self { certified_key })
|
||||
}
|
||||
|
||||
fn resolve_(&self, sigschemes: &[rustls::SignatureScheme]) -> Option<rustls::sign::CertifiedKey>
|
||||
{
|
||||
if !sigschemes.contains(&config::SIGNATURE_ALG_RUSTLS_SCHEME) {
|
||||
debug!("signature scheme not supported -> no certificate");
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.certified_key.clone())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn parse_end_entity_cert<'a>(cert_chain: &'a[rustls::Certificate])
|
||||
|
@ -68,6 +84,20 @@ fn parse_end_entity_cert<'a>(cert_chain: &'a[rustls::Certificate])
|
|||
webpki::EndEntityCert::from(untrusted::Input::from(cert))
|
||||
}
|
||||
|
||||
impl rustls::ResolvesClientCert for CertResolver {
|
||||
fn resolve(&self, _acceptable_issuers: &[&[u8]], sigschemes: &[rustls::SignatureScheme])
|
||||
-> Option<rustls::sign::CertifiedKey>
|
||||
{
|
||||
// Conduit's server side doesn't send the list of acceptable issuers so
|
||||
// don't bother looking at `_acceptable_issuers`.
|
||||
self.resolve_(sigschemes)
|
||||
}
|
||||
|
||||
fn has_certs(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::ResolvesServerCert for CertResolver {
|
||||
fn resolve(&self, server_name: Option<webpki::DNSNameRef>,
|
||||
sigschemes: &[rustls::SignatureScheme]) -> Option<rustls::sign::CertifiedKey> {
|
||||
|
@ -78,11 +108,6 @@ impl rustls::ResolvesServerCert for CertResolver {
|
|||
return None;
|
||||
};
|
||||
|
||||
if !sigschemes.contains(&SIGNATURE_ALG_RUSTLS_SCHEME) {
|
||||
debug!("signature scheme not supported -> no certificate");
|
||||
return None;
|
||||
}
|
||||
|
||||
// Verify that our certificate is valid for the given SNI name.
|
||||
if let Err(err) = parse_end_entity_cert(&self.certified_key.cert)
|
||||
.and_then(|cert| cert.verify_is_valid_for_dns_name(server_name)) {
|
||||
|
@ -90,7 +115,7 @@ impl rustls::ResolvesServerCert for CertResolver {
|
|||
return None;
|
||||
}
|
||||
|
||||
Some(self.certified_key.clone())
|
||||
self.resolve_(sigschemes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +123,7 @@ impl rustls::sign::SigningKey for SigningKey {
|
|||
fn choose_scheme(&self, offered: &[rustls::SignatureScheme])
|
||||
-> Option<Box<rustls::sign::Signer>>
|
||||
{
|
||||
if offered.contains(&SIGNATURE_ALG_RUSTLS_SCHEME) {
|
||||
if offered.contains(&config::SIGNATURE_ALG_RUSTLS_SCHEME) {
|
||||
Some(Box::new(self.signer.clone()))
|
||||
} else {
|
||||
None
|
||||
|
@ -106,7 +131,7 @@ impl rustls::sign::SigningKey for SigningKey {
|
|||
}
|
||||
|
||||
fn algorithm(&self) -> rustls::internal::msgs::enums::SignatureAlgorithm {
|
||||
SIGNATURE_ALG_RUSTLS_ALGORITHM
|
||||
config::SIGNATURE_ALG_RUSTLS_ALGORITHM
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,14 +145,6 @@ impl rustls::sign::Signer for Signer {
|
|||
}
|
||||
|
||||
fn get_scheme(&self) -> rustls::SignatureScheme {
|
||||
SIGNATURE_ALG_RUSTLS_SCHEME
|
||||
config::SIGNATURE_ALG_RUSTLS_SCHEME
|
||||
}
|
||||
}
|
||||
|
||||
// Keep these in sync.
|
||||
static SIGNATURE_ALG_RING_SIGNING: &signature::SigningAlgorithm =
|
||||
&signature::ECDSA_P256_SHA256_ASN1_SIGNING;
|
||||
const SIGNATURE_ALG_RUSTLS_SCHEME: rustls::SignatureScheme =
|
||||
rustls::SignatureScheme::ECDSA_NISTP256_SHA256;
|
||||
const SIGNATURE_ALG_RUSTLS_ALGORITHM: rustls::internal::msgs::enums::SignatureAlgorithm =
|
||||
rustls::internal::msgs::enums::SignatureAlgorithm::ECDSA;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
self,
|
||||
fs::File,
|
||||
io::{self, Cursor, Read},
|
||||
path::PathBuf,
|
||||
|
@ -14,9 +15,10 @@ use super::{
|
|||
untrusted,
|
||||
webpki,
|
||||
};
|
||||
|
||||
use conditional::Conditional;
|
||||
use futures::{future, stream, Future, Stream};
|
||||
use futures_watch::Watch;
|
||||
use ring::signature;
|
||||
|
||||
/// Not-yet-validated settings that are used for both TLS clients and TLS
|
||||
/// servers.
|
||||
|
@ -53,11 +55,18 @@ struct CommonConfig {
|
|||
cert_resolver: Arc<CertResolver>,
|
||||
}
|
||||
|
||||
/// Validated configuration for TLS clients.
|
||||
///
|
||||
/// TODO: Fill this in with the actual configuration.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClientConfig(Arc<()>);
|
||||
|
||||
/// Validated configuration for TLS servers.
|
||||
#[derive(Clone)]
|
||||
pub struct ClientConfig(pub(super) Arc<rustls::ClientConfig>);
|
||||
|
||||
/// XXX: `rustls::ClientConfig` doesn't implement `Debug` yet.
|
||||
impl std::fmt::Debug for ClientConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
f.debug_struct("ClientConfig")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Validated configuration for TLS servers.
|
||||
#[derive(Clone)]
|
||||
|
@ -66,6 +75,33 @@ pub struct ServerConfig(pub(super) Arc<rustls::ServerConfig>);
|
|||
pub type ClientConfigWatch = Watch<Option<ClientConfig>>;
|
||||
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 identity: Identity,
|
||||
pub config: C,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum ReasonForNoTls {
|
||||
/// TLS is disabled.
|
||||
Disabled,
|
||||
|
||||
/// 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,
|
||||
|
||||
}
|
||||
|
||||
pub type ConditionalConnectionConfig<C> = Conditional<ConnectionConfig<C>, ReasonForNoTls>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(PathBuf, io::Error),
|
||||
|
@ -206,7 +242,7 @@ pub fn watch_for_config_changes(settings: Option<&CommonSettings>)
|
|||
(client_store, server_store),
|
||||
|(mut client_store, mut server_store), ref config| {
|
||||
client_store
|
||||
.store(Some(ClientConfig(Arc::new(()))))
|
||||
.store(Some(ClientConfig::from(config)))
|
||||
.map_err(|_| trace!("all client config watchers dropped"))?;
|
||||
server_store
|
||||
.store(Some(ServerConfig::from(config)))
|
||||
|
@ -225,6 +261,38 @@ pub fn watch_for_config_changes(settings: Option<&CommonSettings>)
|
|||
(client_watch, server_watch, Box::new(f))
|
||||
}
|
||||
|
||||
impl ClientConfig {
|
||||
fn from(common: &CommonConfig) -> Self {
|
||||
let mut config = rustls::ClientConfig::new();
|
||||
set_common_settings(&mut config.versions);
|
||||
|
||||
// XXX: Rustls's built-in verifiers don't let us tweak things as fully
|
||||
// as we'd like (e.g. controlling the set of trusted signature
|
||||
// algorithms), but they provide good enough defaults for now.
|
||||
// TODO: lock down the verification further.
|
||||
// TODO: Change Rustls's API to Avoid needing to clone `root_cert_store`.
|
||||
config.root_store = common.root_cert_store.clone();
|
||||
|
||||
// Disable session resumption for the time-being until resumption is
|
||||
// more tested.
|
||||
config.enable_tickets = false;
|
||||
|
||||
// Enable client authentication if and only if we were configured for
|
||||
// it.
|
||||
config.client_auth_cert_resolver = common.cert_resolver.clone();
|
||||
|
||||
ClientConfig(Arc::new(config))
|
||||
}
|
||||
|
||||
/// Some tests aren't set up to do TLS yet, but we require a
|
||||
/// `ClientConfigWatch`. We can't use `#[cfg(test)]` here because the
|
||||
/// benchmarks use this.
|
||||
pub fn no_tls() -> ClientConfigWatch {
|
||||
let (watch, _) = Watch::new(None);
|
||||
watch
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
fn from(common: &CommonConfig) -> Self {
|
||||
// Ask TLS clients for a certificate and accept any certificate issued
|
||||
|
@ -250,6 +318,24 @@ impl ServerConfig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn current_connection_config<C>(identity: Option<&Identity>, watch: &Watch<Option<C>>)
|
||||
-> ConditionalConnectionConfig<C> where C: Clone + std::fmt::Debug
|
||||
{
|
||||
match identity {
|
||||
Some(identity) => {
|
||||
match *watch.borrow() {
|
||||
Some(ref config) =>
|
||||
Conditional::Some(ConnectionConfig {
|
||||
identity: identity.clone(),
|
||||
config: config.clone()
|
||||
}),
|
||||
None => Conditional::None(ReasonForNoTls::NoConfig),
|
||||
}
|
||||
},
|
||||
None => Conditional::None(ReasonForNoTls::Disabled),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_file_contents(path: &PathBuf) -> Result<Vec<u8>, Error> {
|
||||
fn load_file(path: &PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||
let mut result = Vec::new();
|
||||
|
@ -279,12 +365,24 @@ fn load_file_contents(path: &PathBuf) -> Result<Vec<u8>, Error> {
|
|||
fn set_common_settings(versions: &mut Vec<rustls::ProtocolVersion>) {
|
||||
// Only enable TLS 1.2 until TLS 1.3 is stable.
|
||||
*versions = vec![rustls::ProtocolVersion::TLSv1_2]
|
||||
|
||||
// XXX: Rustls doesn't provide a good way to customize the cipher suite
|
||||
// support, so just use its defaults, which are still pretty good.
|
||||
// TODO: Expand Rustls's API to allow us to clearly whitelist the cipher
|
||||
// suites we want to enable.
|
||||
}
|
||||
|
||||
// Keep these in sync.
|
||||
pub(super) static SIGNATURE_ALG_RING_SIGNING: &signature::SigningAlgorithm =
|
||||
&signature::ECDSA_P256_SHA256_ASN1_SIGNING;
|
||||
pub(super) const SIGNATURE_ALG_RUSTLS_SCHEME: rustls::SignatureScheme =
|
||||
rustls::SignatureScheme::ECDSA_NISTP256_SHA256;
|
||||
pub(super) const SIGNATURE_ALG_RUSTLS_ALGORITHM: rustls::internal::msgs::enums::SignatureAlgorithm =
|
||||
rustls::internal::msgs::enums::SignatureAlgorithm::ECDSA;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tls::{CommonSettings, Identity, ServerConfig};
|
||||
use tls::{ClientConfig, CommonSettings, Identity, ServerConfig};
|
||||
use super::{CommonConfig, Error};
|
||||
use config::Namespaces;
|
||||
use std::path::PathBuf;
|
||||
|
@ -314,7 +412,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_construct_server_config_from_valid_settings() {
|
||||
fn can_construct_client_and_server_config_from_valid_settings() {
|
||||
let settings = settings(&Strings {
|
||||
pod_name: "foo",
|
||||
pod_ns: "ns1",
|
||||
|
@ -324,6 +422,7 @@ mod tests {
|
|||
private_key: "foo-ns1-ca1.p8",
|
||||
});
|
||||
let config = CommonConfig::load_from_disk(&settings).unwrap();
|
||||
let _: ClientConfig = ClientConfig::from(&config); // Infallible.
|
||||
let _: ServerConfig = ServerConfig::from(&config); // Infallible.
|
||||
}
|
||||
|
||||
|
|
|
@ -9,39 +9,74 @@ use tokio::net::TcpStream;
|
|||
use transport::{AddrInfo, Io};
|
||||
|
||||
use super::{
|
||||
rustls::ServerSession,
|
||||
tokio_rustls::{ServerConfigExt, TlsStream},
|
||||
identity::Identity,
|
||||
rustls,
|
||||
tokio_rustls::{self, ClientConfigExt, ServerConfigExt, TlsStream},
|
||||
|
||||
ClientConfig,
|
||||
ServerConfig,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use self::rustls::Session;
|
||||
|
||||
// In theory we could replace `TcpStream` with `Io`. However, it is likely that
|
||||
// in the future we'll need to do things specific to `TcpStream`, so optimize
|
||||
// for that unless/until there is some benefit to doing otherwise.
|
||||
#[derive(Debug)]
|
||||
pub struct Connection(TlsStream<TcpStream, ServerSession>);
|
||||
pub struct Connection<S: Session>(TlsStream<TcpStream, S>);
|
||||
|
||||
impl Connection {
|
||||
pub fn accept(socket: TcpStream, ServerConfig(config): ServerConfig)
|
||||
-> impl Future<Item = Connection, Error = io::Error>
|
||||
{
|
||||
config.accept_async(socket).map(Connection)
|
||||
pub struct UpgradeToTls<S, F>(F)
|
||||
where S: Session,
|
||||
F: Future<Item = TlsStream<TcpStream, S>, Error = io::Error>;
|
||||
|
||||
impl<S, F> Future for UpgradeToTls<S, F>
|
||||
where S: Session,
|
||||
F: Future<Item = TlsStream<TcpStream, S>, Error = io::Error>
|
||||
{
|
||||
type Item = Connection<S>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
|
||||
let tls_stream = try_ready!(self.0.poll());
|
||||
return Ok(Async::Ready(Connection(tls_stream)));
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for Connection {
|
||||
pub type UpgradeClientToTls =
|
||||
UpgradeToTls<rustls::ClientSession, tokio_rustls::ConnectAsync<TcpStream>>;
|
||||
|
||||
pub type UpgradeServerToTls =
|
||||
UpgradeToTls<rustls::ServerSession, tokio_rustls::AcceptAsync<TcpStream>>;
|
||||
|
||||
impl Connection<rustls::ClientSession> {
|
||||
pub fn connect(socket: TcpStream, identity: &Identity, ClientConfig(config): ClientConfig)
|
||||
-> UpgradeClientToTls
|
||||
{
|
||||
UpgradeToTls(config.connect_async(identity.as_dns_name_ref(), socket))
|
||||
}
|
||||
}
|
||||
|
||||
impl Connection<rustls::ServerSession> {
|
||||
pub fn accept(socket: TcpStream, ServerConfig(config): ServerConfig) -> UpgradeServerToTls
|
||||
{
|
||||
UpgradeToTls(config.accept_async(socket))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Session> io::Read for Connection<S> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Connection {
|
||||
impl<S: Session> AsyncRead for Connection<S> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Connection {
|
||||
impl<S: Session> io::Write for Connection<S> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
@ -51,7 +86,7 @@ impl io::Write for Connection {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Connection {
|
||||
impl<S: Session> AsyncWrite for Connection<S> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
|
@ -61,7 +96,7 @@ impl AsyncWrite for Connection {
|
|||
}
|
||||
}
|
||||
|
||||
impl AddrInfo for Connection {
|
||||
impl<S: Session + Debug> AddrInfo for Connection<S> {
|
||||
fn local_addr(&self) -> Result<SocketAddr, io::Error> {
|
||||
self.0.get_ref().0.local_addr()
|
||||
}
|
||||
|
@ -71,7 +106,7 @@ impl AddrInfo for Connection {
|
|||
}
|
||||
}
|
||||
|
||||
impl Io for Connection {
|
||||
impl<S: Session + Debug> Io for Connection<S> {
|
||||
fn shutdown_write(&mut self) -> Result<(), io::Error> {
|
||||
self.0.get_mut().0.shutdown_write()
|
||||
}
|
||||
|
|
|
@ -11,8 +11,19 @@ mod dns_name;
|
|||
mod identity;
|
||||
|
||||
pub use self::{
|
||||
config::{CommonSettings, ServerConfig, ServerConfigWatch, watch_for_config_changes},
|
||||
connection::Connection,
|
||||
config::{
|
||||
ClientConfig,
|
||||
ClientConfigWatch,
|
||||
CommonSettings,
|
||||
ConditionalConnectionConfig,
|
||||
ConnectionConfig,
|
||||
ReasonForNoTls,
|
||||
ServerConfig,
|
||||
ServerConfigWatch,
|
||||
current_connection_config,
|
||||
watch_for_config_changes,
|
||||
},
|
||||
connection::{Connection, Session, UpgradeClientToTls},
|
||||
dns_name::{DnsName, InvalidDnsName},
|
||||
identity::Identity,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue