mirror of https://github.com/linkerd/linkerd2.git
Add initial infrastructure for optionally accepting TLS connections (#1047)
* Add initial infrastructure for optinally accepting TLS connections. If the environment gives us the paths to the certificate chain and private key then use TLS for all accepted TCP connections. Otherwise, continue on using plaintext for all accepted TCP connections. The default behavior--no TLS--isn't changed. Later we'll make this smarter by adding protocol detection so that when the TLS configuration is available, we'll accept both TLS and non-TLS connections. Signed-off-by: Brian Smith <brian@briansmith.org>
This commit is contained in:
parent
8b519fb92f
commit
b114ef6819
|
@ -60,10 +60,19 @@ name = "backtrace-sys"
|
|||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.1"
|
||||
|
@ -90,7 +99,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.4"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -142,9 +151,12 @@ dependencies = [
|
|||
"quickcheck 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-connect 0.1.0 (git+https://github.com/carllerche/tokio-connect)",
|
||||
"tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-rustls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tower-balance 0.1.0 (git+https://github.com/tower-rs/tower)",
|
||||
"tower-buffer 0.1.0 (git+https://github.com/tower-rs/tower)",
|
||||
|
@ -156,6 +168,8 @@ dependencies = [
|
|||
"tower-service 0.1.0 (git+https://github.com/tower-rs/tower)",
|
||||
"tower-util 0.1.0 (git+https://github.com/tower-rs/tower)",
|
||||
"trust-dns-resolver 0.8.1 (git+https://github.com/bluejekyll/trust-dns)",
|
||||
"untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webpki 0.18.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -555,7 +569,7 @@ name = "miniz_oxide_c_api"
|
|||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -817,11 +831,40 @@ dependencies = [
|
|||
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.13.0-alpha4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sct 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webpki 0.18.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "0.1.0"
|
||||
|
@ -832,6 +875,15 @@ name = "scopeguard"
|
|||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ring 0.13.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.0"
|
||||
|
@ -998,6 +1050,16 @@ dependencies = [
|
|||
"tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webpki 0.18.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-signal"
|
||||
version = "0.2.0"
|
||||
|
@ -1251,6 +1313,11 @@ dependencies = [
|
|||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.0"
|
||||
|
@ -1281,6 +1348,15 @@ dependencies = [
|
|||
"try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.18.0-alpha3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ring 0.13.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.2.2"
|
||||
|
@ -1356,11 +1432,12 @@ dependencies = [
|
|||
"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f"
|
||||
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
|
||||
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
|
||||
"checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007"
|
||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||
"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
|
||||
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||
"checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59"
|
||||
"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
|
||||
"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
|
||||
"checksum codegen 0.1.0 (git+https://github.com/carllerche/codegen)" = "<none>"
|
||||
|
@ -1437,9 +1514,13 @@ dependencies = [
|
|||
"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b"
|
||||
"checksum remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfc5b3ce5d5ea144bb04ebd093a9e14e9765bcfec866aecda9b6dec43b3d1e24"
|
||||
"checksum resolv-conf 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e1b086bb6a2659d6ba66e4aa21bde8a53ec03587cd5c80b83bdc3a330f35cab"
|
||||
"checksum ring 0.13.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)" = "d835120175f4cfea267529ebf36fa3851ba1de1e44202f6241e5dee517edaebe"
|
||||
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
|
||||
"checksum rustls 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab72e4883a4fc9fd5cd462a51c55d79f6a7b5c9483e8d73a2b7bca0b18430bcd"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||
"checksum sct 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4540aed8d71a5de961a8902cf356e28122bd62695eb5be1c214f84d8704097c"
|
||||
"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
|
||||
"checksum smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03dab98ab5ded3a8b43b2c80751194608d0b2aa0f1d46cf95d1c35e192844aa7"
|
||||
"checksum socket2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ff606e0486e88f5fc6cfeb3966e434fb409abbc7a3ab495238f70a1ca97f789d"
|
||||
|
@ -1457,6 +1538,7 @@ dependencies = [
|
|||
"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708"
|
||||
"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a"
|
||||
"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b"
|
||||
"checksum tokio-rustls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7abfd34d641c0cade850bd94bd0bbfa548b88adb2f08ae237a129144034ff9ae"
|
||||
"checksum tokio-signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6a5bf935a0151cc8899aa806ce6a425bdaec79ed4034de1a1e6bfa247e2def"
|
||||
"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f"
|
||||
"checksum tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5783254b10c7c84a56f62c74766ef7e5b83d1f13053218c7cab8d3f2c826fa0e"
|
||||
|
@ -1481,10 +1563,12 @@ dependencies = [
|
|||
"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum untrusted 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70afa43c8c5d23a53a3c39ec9b56232c5badc19f6bb5ad529c1d6448a7241365"
|
||||
"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
|
||||
"checksum webpki 0.18.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)" = "30cf7434bea34e094993720093b0f0ef4117d3edd977e5bd234de72e6d4c354e"
|
||||
"checksum widestring 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||
|
|
|
@ -50,6 +50,12 @@ tower-reconnect = { git = "https://github.com/tower-rs/tower" }
|
|||
tower-in-flight-limit = { git = "https://github.com/tower-rs/tower" }
|
||||
tower-util = { git = "https://github.com/tower-rs/tower" }
|
||||
|
||||
ring = "0.13.0-alpha4"
|
||||
webpki = "0.18.0-alpha3"
|
||||
rustls = "0.12.0"
|
||||
tokio-rustls = "0.6.0"
|
||||
untrusted = "0.6.1"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ use std::time::Duration;
|
|||
|
||||
use http;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use transport::{Host, HostAndPort, HostAndPortError};
|
||||
use transport::{Host, HostAndPort, HostAndPortError, tls};
|
||||
use convert::TryFrom;
|
||||
|
||||
// TODO:
|
||||
|
@ -17,7 +16,7 @@ use convert::TryFrom;
|
|||
// * Make struct fields private.
|
||||
|
||||
/// Tracks all configuration settings for the process.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
/// Where to listen for connections that are initiated on the host.
|
||||
pub private_listener: Listener,
|
||||
|
@ -52,6 +51,8 @@ pub struct Config {
|
|||
|
||||
pub outbound_router_max_idle_age: Duration,
|
||||
|
||||
pub tls_settings: Option<tls::CommonSettings>,
|
||||
|
||||
/// The path to "/etc/resolv.conf"
|
||||
pub resolv_conf_path: PathBuf,
|
||||
|
||||
|
@ -159,6 +160,10 @@ pub const ENV_OUTBOUND_ROUTER_MAX_IDLE_AGE: &str = "CONDUIT_PROXY_OUTBOUND_ROUTE
|
|||
pub const ENV_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION: &str = "CONDUIT_PROXY_INBOUND_PORTS_DISABLE_PROTOCOL_DETECTION";
|
||||
pub const ENV_OUTBOUND_PORTS_DISABLE_PROTOCOL_DETECTION: &str = "CONDUIT_PROXY_OUTBOUND_PORTS_DISABLE_PROTOCOL_DETECTION";
|
||||
|
||||
pub const ENV_TLS_TRUST_ANCHORS: &str = "CONDUIT_PROXY_TLS_TRUST_ANCHORS";
|
||||
pub const ENV_TLS_CERT: &str = "CONDUIT_PROXY_TLS_CERT";
|
||||
pub const ENV_TLS_PRIVATE_KEY: &str = "CONDUIT_PROXY_TLS_PRIVATE_KEY";
|
||||
|
||||
pub const ENV_POD_NAMESPACE: &str = "CONDUIT_PROXY_POD_NAMESPACE";
|
||||
|
||||
pub const ENV_CONTROL_URL: &str = "CONDUIT_PROXY_CONTROL_URL";
|
||||
|
@ -214,6 +219,9 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
|||
let outbound_router_capacity = parse(strings, ENV_OUTBOUND_ROUTER_CAPACITY, parse_number);
|
||||
let inbound_router_max_idle_age = parse(strings, ENV_INBOUND_ROUTER_MAX_IDLE_AGE, parse_duration);
|
||||
let outbound_router_max_idle_age = parse(strings, ENV_OUTBOUND_ROUTER_MAX_IDLE_AGE, parse_duration);
|
||||
let tls_trust_anchors = parse(strings, ENV_TLS_TRUST_ANCHORS, parse_path);
|
||||
let tls_end_entity_cert = parse(strings, ENV_TLS_CERT, parse_path);
|
||||
let tls_private_key = parse(strings, ENV_TLS_PRIVATE_KEY, parse_path);
|
||||
let bind_timeout = parse(strings, ENV_BIND_TIMEOUT, parse_duration);
|
||||
let resolv_conf_path = strings.get(ENV_RESOLV_CONF);
|
||||
let event_buffer_capacity = parse(strings, ENV_EVENT_BUFFER_CAPACITY, parse_number);
|
||||
|
@ -237,6 +245,31 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
|||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
let tls_settings = match (tls_trust_anchors?, tls_end_entity_cert?, tls_private_key?) {
|
||||
(Some(trust_anchors), Some(end_entity_cert), Some(private_key)) =>
|
||||
Ok(Some(tls::CommonSettings {
|
||||
trust_anchors,
|
||||
end_entity_cert,
|
||||
private_key,
|
||||
})),
|
||||
(_, None, None) => Ok(None), // No TLS in server role.
|
||||
(trust_anchors, end_entity_cert, private_key) => {
|
||||
if trust_anchors.is_none() {
|
||||
error!("{} is not set; it is required when {} and {} are set.",
|
||||
ENV_TLS_TRUST_ANCHORS, ENV_TLS_CERT, ENV_TLS_PRIVATE_KEY);
|
||||
}
|
||||
if end_entity_cert.is_none() {
|
||||
error!("{} is not set; it is required when {} are set.",
|
||||
ENV_TLS_CERT, ENV_TLS_PRIVATE_KEY);
|
||||
}
|
||||
if private_key.is_none() {
|
||||
error!("{} is not set; it is required when {} are set.",
|
||||
ENV_TLS_PRIVATE_KEY, ENV_TLS_CERT);
|
||||
}
|
||||
Err(Error::InvalidEnvVar)
|
||||
},
|
||||
}?;
|
||||
|
||||
Ok(Config {
|
||||
private_listener: Listener {
|
||||
addr: private_listener_addr?
|
||||
|
@ -276,6 +309,8 @@ impl<'a> TryFrom<&'a Strings> for Config {
|
|||
outbound_router_max_idle_age: outbound_router_max_idle_age?
|
||||
.unwrap_or(DEFAULT_OUTBOUND_ROUTER_MAX_IDLE_AGE),
|
||||
|
||||
tls_settings,
|
||||
|
||||
resolv_conf_path: resolv_conf_path?
|
||||
.unwrap_or(DEFAULT_RESOLV_CONF.into())
|
||||
.into(),
|
||||
|
@ -377,6 +412,10 @@ fn parse_duration(s: &str) -> Result<Duration, ParseError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_path(s: &str) -> Result<PathBuf, ParseError> {
|
||||
Ok(PathBuf::from(s))
|
||||
}
|
||||
|
||||
fn parse_url(s: &str) -> Result<HostAndPort, ParseError> {
|
||||
let url = s.parse::<http::Uri>().map_err(|_| ParseError::UrlError(UrlError::SyntaxError))?;
|
||||
if url.scheme_part().map(|s| s.as_str()) != Some("tcp") {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bytes::{Buf, BytesMut};
|
||||
use futures::*;
|
||||
use futures::{*, future::Either};
|
||||
use std;
|
||||
use std::cmp;
|
||||
use std::io;
|
||||
|
@ -11,8 +11,7 @@ use tokio::{
|
|||
};
|
||||
|
||||
use config::Addr;
|
||||
use transport::GetOriginalDst;
|
||||
use transport::Io;
|
||||
use transport::{GetOriginalDst, Io, tls};
|
||||
|
||||
pub type PlaintextSocket = TcpStream;
|
||||
|
||||
|
@ -96,7 +95,11 @@ impl BoundPort {
|
|||
// This ensures that every incoming connection has the correct options set.
|
||||
// In the future it will also ensure that the connection is upgraded with
|
||||
// TLS when needed.
|
||||
pub fn listen_and_fold<T, F, Fut>(self, initial: T, f: F)
|
||||
pub fn listen_and_fold<T, F, Fut>(
|
||||
self,
|
||||
tls_config: Option<tls::ServerConfig>,
|
||||
initial: T,
|
||||
f: F)
|
||||
-> impl Future<Item = (), Error = io::Error> + Send + 'static
|
||||
where
|
||||
F: Fn(T, (Connection, SocketAddr)) -> Fut + Send + 'static,
|
||||
|
@ -122,8 +125,18 @@ impl BoundPort {
|
|||
// libraries don't have the necessary API for that, so just
|
||||
// do it here.
|
||||
set_nodelay_or_warn(&socket);
|
||||
let connection = Connection::plain(socket);
|
||||
future::ok((connection, remote_addr))
|
||||
match tls_config.clone() {
|
||||
Some(tls_config) => {
|
||||
Either::A(
|
||||
tls::Connection::accept(socket, tls_config)
|
||||
.map(move |tls| (Connection::new(Box::new(tls)), remote_addr)))
|
||||
},
|
||||
None => Either::B(future::ok((Connection::new(Box::new(socket)), remote_addr))),
|
||||
}
|
||||
})
|
||||
.or_else(|err| {
|
||||
debug!("error handshaking: {}", err);
|
||||
future::err(err)
|
||||
})
|
||||
.fold(initial, f)
|
||||
)
|
||||
|
@ -140,7 +153,7 @@ impl Future for Connecting {
|
|||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let socket = try_ready!(self.0.poll());
|
||||
set_nodelay_or_warn(&socket);
|
||||
Ok(Async::Ready(Connection::plain(socket)))
|
||||
Ok(Async::Ready(Connection::new(Box::new(socket))))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,9 +161,9 @@ impl Future for Connecting {
|
|||
|
||||
impl Connection {
|
||||
/// A constructor of `Connection` with a plain text TCP socket.
|
||||
pub fn plain(socket: PlaintextSocket) -> Self {
|
||||
fn new(io: Box<Io>) -> Self {
|
||||
Connection {
|
||||
io: Box::new(socket),
|
||||
io,
|
||||
peek_buf: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ use inbound::Inbound;
|
|||
use map_err::MapErr;
|
||||
use task::MainRuntime;
|
||||
use transparency::{HttpBody, Server};
|
||||
pub use transport::{AddrInfo, GetOriginalDst, SoOriginalDst};
|
||||
pub use transport::{AddrInfo, GetOriginalDst, SoOriginalDst, tls};
|
||||
use outbound::Outbound;
|
||||
|
||||
/// Runs a sidecar proxy.
|
||||
|
@ -229,6 +229,21 @@ where
|
|||
|
||||
let bind = Bind::new().with_sensors(sensors.clone());
|
||||
|
||||
// TODO: Load the TLS configuration asynchronously and watch for
|
||||
// changes to the files.
|
||||
let tls_config = config.tls_settings.and_then(|settings| {
|
||||
match tls::CommonConfig::load_from_disk(&settings) {
|
||||
Ok(config) => Some(config),
|
||||
Err(e) => {
|
||||
// Keep going without TLS if loading settings failed.
|
||||
error!("Error loading TLS configuration: {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let tls_server_config = tls_config.as_ref().map(tls::ServerConfig::from);
|
||||
|
||||
// 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).
|
||||
|
@ -246,6 +261,7 @@ where
|
|||
);
|
||||
serve(
|
||||
inbound_listener,
|
||||
tls_server_config,
|
||||
router,
|
||||
config.private_connect_timeout,
|
||||
config.inbound_ports_disable_protocol_detection,
|
||||
|
@ -269,6 +285,7 @@ where
|
|||
);
|
||||
serve(
|
||||
outbound_listener,
|
||||
None, // No TLS
|
||||
router,
|
||||
config.public_connect_timeout,
|
||||
config.outbound_ports_disable_protocol_detection,
|
||||
|
@ -331,6 +348,7 @@ where
|
|||
|
||||
fn serve<R, B, E, F, G>(
|
||||
bound_port: BoundPort,
|
||||
tls_config: Option<tls::ServerConfig>,
|
||||
router: Router<R>,
|
||||
tcp_connect_timeout: Duration,
|
||||
disable_protocol_detection_ports: IndexSet<u16>,
|
||||
|
@ -406,6 +424,7 @@ where
|
|||
|
||||
let accept = {
|
||||
let fut = bound_port.listen_and_fold(
|
||||
tls_config,
|
||||
(),
|
||||
move |(), (connection, remote_addr)| {
|
||||
let s = server.serve(connection, remote_addr);
|
||||
|
@ -488,6 +507,7 @@ where
|
|||
let fut = {
|
||||
let log = log.clone();
|
||||
bound_port.listen_and_fold(
|
||||
None, // No TLS
|
||||
server,
|
||||
move |server, (session, remote)| {
|
||||
let log = log.clone().with_remote(remote);
|
||||
|
|
|
@ -103,6 +103,7 @@ impl Control {
|
|||
let fut = {
|
||||
let log = log.clone();
|
||||
bound_port.listen_and_fold(
|
||||
None, // TODO: No TLS
|
||||
hyper::server::conn::Http::new(),
|
||||
move |hyper, (conn, remote)| {
|
||||
let service = service.clone();
|
||||
|
|
|
@ -5,6 +5,7 @@ use tokio::net::TcpStream;
|
|||
|
||||
mod connect;
|
||||
mod addr_info;
|
||||
pub mod tls;
|
||||
|
||||
pub use self::connect::{
|
||||
Connect,
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
use std::{
|
||||
sync::Arc,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use super::{
|
||||
config,
|
||||
|
||||
ring::{self, rand, signature},
|
||||
rustls,
|
||||
untrusted,
|
||||
webpki,
|
||||
};
|
||||
|
||||
pub struct CertResolver {
|
||||
certified_key: rustls::sign::CertifiedKey,
|
||||
}
|
||||
|
||||
struct SigningKey {
|
||||
signer: Signer,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Signer {
|
||||
private_key: Arc<signature::KeyPair>,
|
||||
}
|
||||
|
||||
impl CertResolver {
|
||||
/// Returns a new `CertResolver` that has a certificate (chain) verified to
|
||||
/// have been issued by one of the given trust anchors.
|
||||
///
|
||||
/// TODO: Verify that the public key of the certificate matches the private
|
||||
/// key.
|
||||
pub fn new(
|
||||
trust_anchors: &webpki::TLSServerTrustAnchors,
|
||||
cert_chain: Vec<rustls::Certificate>,
|
||||
private_key: untrusted::Input)
|
||||
-> Result<Self, config::Error>
|
||||
{
|
||||
let now = webpki::Time::try_from(SystemTime::now())
|
||||
.map_err(|ring::error::Unspecified| config::Error::TimeConversionFailed)?;
|
||||
|
||||
// Verify that we were given a valid TLS certificate that was issued by
|
||||
// our CA.
|
||||
parse_end_entity_cert(&cert_chain)
|
||||
.and_then(|cert| {
|
||||
cert.verify_is_valid_tls_server_cert(
|
||||
&[SIGNATURE_ALG_WEBPKI],
|
||||
&trust_anchors,
|
||||
&[], // No intermediate certificates
|
||||
now)
|
||||
}).map_err(config::Error::EndEntityCertIsNotValid)?;
|
||||
|
||||
let private_key = signature::key_pair_from_pkcs8(SIGNATURE_ALG_RING_SIGNING, private_key)
|
||||
.map_err(|ring::error::Unspecified| config::Error::InvalidPrivateKey)?;
|
||||
|
||||
let signer = Signer { private_key: Arc::new(private_key) };
|
||||
let signing_key = SigningKey { signer };
|
||||
let certified_key = rustls::sign::CertifiedKey::new(
|
||||
cert_chain, Arc::new(Box::new(signing_key)));
|
||||
Ok(Self { certified_key })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_end_entity_cert<'a>(cert_chain: &'a[rustls::Certificate])
|
||||
-> Result<webpki::EndEntityCert<'a>, webpki::Error>
|
||||
{
|
||||
let cert = cert_chain.first()
|
||||
.map(rustls::Certificate::as_ref)
|
||||
.unwrap_or(&[]); // An empty input fill fail to parse.
|
||||
webpki::EndEntityCert::from(untrusted::Input::from(cert))
|
||||
}
|
||||
|
||||
impl rustls::ResolvesServerCert for CertResolver {
|
||||
fn resolve(&self, server_name: Option<webpki::DNSNameRef>,
|
||||
sigschemes: &[rustls::SignatureScheme]) -> Option<rustls::sign::CertifiedKey> {
|
||||
let server_name = if let Some(server_name) = server_name {
|
||||
server_name
|
||||
} else {
|
||||
debug!("no SNI -> no certificate");
|
||||
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)) {
|
||||
debug!("our certificate is not valid for the SNI name -> no certificate: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.certified_key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Some(Box::new(self.signer.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn algorithm(&self) -> rustls::internal::msgs::enums::SignatureAlgorithm {
|
||||
SIGNATURE_ALG_RUSTLS_ALGORITHM
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::sign::Signer for Signer {
|
||||
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, rustls::TLSError> {
|
||||
let rng = rand::SystemRandom::new();
|
||||
signature::sign(&self.private_key, &rng, untrusted::Input::from(message))
|
||||
.map(|signature| signature.as_ref().to_owned())
|
||||
.map_err(|ring::error::Unspecified|
|
||||
rustls::TLSError::General("Signing Failed".to_owned()))
|
||||
}
|
||||
|
||||
fn get_scheme(&self) -> rustls::SignatureScheme {
|
||||
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;
|
||||
static SIGNATURE_ALG_WEBPKI: &webpki::SignatureAlgorithm = &webpki::ECDSA_P256_SHA256;
|
|
@ -0,0 +1,142 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{self, Cursor, Read},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use super::{
|
||||
cert_resolver::CertResolver,
|
||||
|
||||
rustls,
|
||||
untrusted,
|
||||
webpki,
|
||||
};
|
||||
|
||||
/// Not-yet-validated settings that are used for both TLS clients and TLS
|
||||
/// servers.
|
||||
///
|
||||
/// The trust anchors are stored in PEM format because, in Kubernetes, they are
|
||||
/// stored in a ConfigMap, and until very recently Kubernetes cannot store
|
||||
/// binary data in ConfigMaps. Also, PEM is the most interoperable way to
|
||||
/// distribute trust anchors, especially if it is desired to support multiple
|
||||
/// trust anchors at once.
|
||||
///
|
||||
/// The end-entity certificate and private key are in DER format because they
|
||||
/// are stored in the secret store where space utilization is a concern, and
|
||||
/// because PEM doesn't offer any advantages.
|
||||
#[derive(Debug)]
|
||||
pub struct CommonSettings {
|
||||
/// The trust anchors as concatenated PEM-encoded X.509 certificates.
|
||||
pub trust_anchors: PathBuf,
|
||||
|
||||
/// The end-entity certificate as a DER-encoded X.509 certificate.
|
||||
pub end_entity_cert: PathBuf,
|
||||
|
||||
/// The private key in DER-encoded PKCS#8 form.
|
||||
pub private_key: PathBuf,
|
||||
}
|
||||
|
||||
/// Validated configuration common between TLS clients and TLS servers.
|
||||
pub struct CommonConfig {
|
||||
cert_resolver: Arc<CertResolver>,
|
||||
}
|
||||
|
||||
/// Validated configuration for TLS servers.
|
||||
#[derive(Clone)]
|
||||
pub struct ServerConfig(pub(super) Arc<rustls::ServerConfig>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(PathBuf, io::Error),
|
||||
FailedToParsePrivateKey,
|
||||
FailedToParseTrustAnchors(Option<webpki::Error>),
|
||||
EmptyEndEntityCert,
|
||||
EndEntityCertIsNotValid(webpki::Error),
|
||||
InvalidPrivateKey,
|
||||
TimeConversionFailed,
|
||||
}
|
||||
|
||||
impl CommonConfig {
|
||||
/// Loads a configuration from the given files and validates it. If an
|
||||
/// error is returned then the caller should try again after the files are
|
||||
/// updated.
|
||||
///
|
||||
/// In a valid configuration, all the files need to be in sync with each
|
||||
/// other. For example, the private key file must contain the private
|
||||
/// key for the end-entity certificate, and the end-entity certificate
|
||||
/// must be issued by the CA represented by a certificate in the
|
||||
/// trust anchors file. Since filesystem operations are not atomic, we
|
||||
/// need to check for this consistency.
|
||||
pub fn load_from_disk(settings: &CommonSettings) -> Result<Self, Error> {
|
||||
let trust_anchor_certs = load_file_contents(&settings.trust_anchors)
|
||||
.and_then(|file_contents|
|
||||
rustls::internal::pemfile::certs(&mut Cursor::new(file_contents))
|
||||
.map_err(|()| Error::FailedToParseTrustAnchors(None)))?;
|
||||
let mut trust_anchors = Vec::with_capacity(trust_anchor_certs.len());
|
||||
for ta in &trust_anchor_certs {
|
||||
let ta = webpki::trust_anchor_util::cert_der_as_trust_anchor(
|
||||
untrusted::Input::from(ta.as_ref()))
|
||||
.map_err(|e| Error::FailedToParseTrustAnchors(Some(e)))?;
|
||||
trust_anchors.push(ta);
|
||||
}
|
||||
let trust_anchors = webpki::TLSServerTrustAnchors(&trust_anchors);
|
||||
|
||||
let end_entity_cert = load_file_contents(&settings.end_entity_cert)?;
|
||||
|
||||
// XXX: Assume there are no intermediates since there is no way to load
|
||||
// them yet.
|
||||
let cert_chain = vec![rustls::Certificate(end_entity_cert)];
|
||||
|
||||
// Load the private key after we've validated the certificate.
|
||||
let private_key = load_file_contents(&settings.private_key)?;
|
||||
let private_key = untrusted::Input::from(&private_key);
|
||||
|
||||
// `CertResolver::new` is responsible for the consistency check.
|
||||
let cert_resolver = CertResolver::new(&trust_anchors, cert_chain, private_key)?;
|
||||
|
||||
Ok(Self {
|
||||
cert_resolver: Arc::new(cert_resolver),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
pub fn from(common: &CommonConfig) -> Self {
|
||||
let mut config = rustls::ServerConfig::new(Arc::new(rustls::NoClientAuth));
|
||||
set_common_settings(&mut config.versions);
|
||||
config.cert_resolver = common.cert_resolver.clone();
|
||||
ServerConfig(Arc::new(config))
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
let mut file = File::open(path)?;
|
||||
loop {
|
||||
match file.read_to_end(&mut result) {
|
||||
Ok(_) => {
|
||||
return Ok(result);
|
||||
},
|
||||
Err(e) => {
|
||||
if e.kind() != io::ErrorKind::Interrupted {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load_file(path)
|
||||
.map(|contents| {
|
||||
trace!("loaded file {:?}", path);
|
||||
contents
|
||||
})
|
||||
.map_err(|e| Error::Io(path.clone(), e))
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use bytes::Buf;
|
||||
use futures::Future;
|
||||
use tokio::prelude::*;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use transport::{AddrInfo, Io};
|
||||
|
||||
use super::{
|
||||
rustls::ServerSession,
|
||||
tokio_rustls::{ServerConfigExt, TlsStream},
|
||||
|
||||
ServerConfig,
|
||||
};
|
||||
|
||||
// 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>);
|
||||
|
||||
impl Connection {
|
||||
pub fn accept(socket: TcpStream, ServerConfig(config): ServerConfig)
|
||||
-> impl Future<Item = Connection, Error = io::Error>
|
||||
{
|
||||
config.accept_async(socket).map(Connection)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for Connection {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Connection {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
||||
self.0.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Connection {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Connection {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
self.0.shutdown()
|
||||
}
|
||||
|
||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
||||
self.0.write_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddrInfo for Connection {
|
||||
fn local_addr(&self) -> Result<SocketAddr, io::Error> {
|
||||
self.0.get_ref().0.local_addr()
|
||||
}
|
||||
|
||||
fn get_original_dst(&self) -> Option<SocketAddr> {
|
||||
self.0.get_ref().0.get_original_dst()
|
||||
}
|
||||
}
|
||||
|
||||
impl Io for Connection {
|
||||
fn shutdown_write(&mut self) -> Result<(), io::Error> {
|
||||
self.0.get_mut().0.shutdown_write()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// These crates are only used within the `tls` module.
|
||||
extern crate ring;
|
||||
extern crate rustls;
|
||||
extern crate tokio_rustls;
|
||||
extern crate untrusted;
|
||||
extern crate webpki;
|
||||
|
||||
mod config;
|
||||
mod cert_resolver;
|
||||
mod connection;
|
||||
|
||||
pub use self::{
|
||||
config::{CommonSettings, CommonConfig, Error, ServerConfig},
|
||||
connection::Connection,
|
||||
};
|
Loading…
Reference in New Issue