From 0db79a9052160c60e9d8cf7e1ddd025e3a76bce1 Mon Sep 17 00:00:00 2001 From: Zahari Dichev Date: Mon, 6 Nov 2023 17:21:37 +0000 Subject: [PATCH] split tls id and sni name Signed-off-by: Zahari Dichev --- linkerd/app/admin/src/stack.rs | 2 +- linkerd/app/gateway/src/http.rs | 2 +- linkerd/app/inbound/src/detect.rs | 4 +- linkerd/app/outbound/src/http/concrete.rs | 3 +- .../outbound/src/http/require_id_header.rs | 2 +- linkerd/app/outbound/src/opaq/concrete.rs | 3 +- .../app/outbound/src/tcp/tagged_transport.rs | 37 +++++----- linkerd/app/src/env.rs | 67 +++++++++++++++---- linkerd/app/src/identity.rs | 15 +++-- linkerd/app/src/lib.rs | 6 +- linkerd/http-access-log/src/lib.rs | 2 +- linkerd/identity/src/credentials.rs | 6 +- linkerd/identity/src/lib.rs | 3 + linkerd/identity/src/local.rs | 35 ++++++++-- linkerd/identity/src/name.rs | 2 +- linkerd/identity/src/tls_name.rs | 46 +++++++++++++ linkerd/meshtls/boring/src/client.rs | 17 +++-- linkerd/meshtls/boring/src/creds.rs | 7 +- linkerd/meshtls/boring/src/creds/receiver.rs | 14 ++-- linkerd/meshtls/boring/src/creds/store.rs | 17 +++-- linkerd/meshtls/boring/src/server.rs | 8 +-- linkerd/meshtls/boring/src/tests.rs | 1 + linkerd/meshtls/rustls/src/client.rs | 13 ++-- linkerd/meshtls/rustls/src/creds.rs | 8 ++- linkerd/meshtls/rustls/src/creds/receiver.rs | 12 +++- linkerd/meshtls/rustls/src/creds/store.rs | 13 ++-- linkerd/meshtls/rustls/src/server.rs | 8 +-- linkerd/meshtls/rustls/src/tests.rs | 1 + linkerd/meshtls/src/creds.rs | 14 ++-- linkerd/meshtls/src/lib.rs | 13 ++-- linkerd/meshtls/src/server.rs | 6 +- linkerd/meshtls/tests/util.rs | 37 ++++++++-- linkerd/proxy/api-resolve/src/metadata.rs | 8 +-- linkerd/proxy/api-resolve/src/pb.rs | 16 +++-- linkerd/proxy/identity-client/src/certify.rs | 2 +- linkerd/tls/src/client.rs | 63 ++++++++++------- linkerd/tls/src/lib.rs | 6 +- linkerd/tls/src/server.rs | 30 ++++----- linkerd/tls/src/server/client_hello.rs | 11 +-- linkerd/tls/test-util/src/lib.rs | 5 ++ 40 files changed, 385 insertions(+), 180 deletions(-) create mode 100644 linkerd/identity/src/tls_name.rs diff --git a/linkerd/app/admin/src/stack.rs b/linkerd/app/admin/src/stack.rs index 211184cb0d..7973840abb 100644 --- a/linkerd/app/admin/src/stack.rs +++ b/linkerd/app/admin/src/stack.rs @@ -34,7 +34,7 @@ struct NonHttpClient(Remote); #[derive(Debug, Error)] #[error("Unexpected TLS connection to {} from {}", self.0, self.1)] -struct UnexpectedSni(tls::ServerId, Remote); +struct UnexpectedSni(tls::ServerName, Remote); #[derive(Clone, Debug)] struct Tcp { diff --git a/linkerd/app/gateway/src/http.rs b/linkerd/app/gateway/src/http.rs index 275d92bda7..9c3c2585ce 100644 --- a/linkerd/app/gateway/src/http.rs +++ b/linkerd/app/gateway/src/http.rs @@ -105,7 +105,7 @@ impl Gateway { .push_map_target(Target::discard_parent) // Add headers to prevent loops. .push(NewHttpGateway::layer(identity::LocalId( - self.inbound.identity().name().clone(), + self.inbound.identity().tls_name().clone(), ))) .push_on_service(svc::LoadShed::layer()) .lift_new() diff --git a/linkerd/app/inbound/src/detect.rs b/linkerd/app/inbound/src/detect.rs index 0341d81b08..dd4d233c53 100644 --- a/linkerd/app/inbound/src/detect.rs +++ b/linkerd/app/inbound/src/detect.rs @@ -396,8 +396,8 @@ impl svc::Param for Http { } } -impl svc::Param> for Http { - fn param(&self) -> Option { +impl svc::Param> for Http { + fn param(&self) -> Option { self.tls .status .value() diff --git a/linkerd/app/outbound/src/http/concrete.rs b/linkerd/app/outbound/src/http/concrete.rs index e690530f6e..f0dac1e9cc 100644 --- a/linkerd/app/outbound/src/http/concrete.rs +++ b/linkerd/app/outbound/src/http/concrete.rs @@ -374,9 +374,10 @@ impl svc::Param for Endpoint { self.metadata .identity() .cloned() - .map(move |server_id| { + .map(move |(server_id, server_name)| { tls::ConditionalClientTls::Some(tls::ClientTls { server_id, + server_name, alpn: if use_transport_header { use linkerd_app_core::transport_header::PROTOCOL; Some(tls::client::AlpnProtocols(vec![PROTOCOL.into()])) diff --git a/linkerd/app/outbound/src/http/require_id_header.rs b/linkerd/app/outbound/src/http/require_id_header.rs index bc9c431039..5affef8f4a 100644 --- a/linkerd/app/outbound/src/http/require_id_header.rs +++ b/linkerd/app/outbound/src/http/require_id_header.rs @@ -57,7 +57,7 @@ type ResponseFuture = impl RequireIdentity { #[inline] - fn extract_id(req: &mut http::Request) -> Option { + fn extract_id(req: &mut http::Request) -> Option { let v = req.headers_mut().remove(HEADER_NAME)?; v.to_str().ok()?.parse().ok() } diff --git a/linkerd/app/outbound/src/opaq/concrete.rs b/linkerd/app/outbound/src/opaq/concrete.rs index c71bc67b85..acc2aade95 100644 --- a/linkerd/app/outbound/src/opaq/concrete.rs +++ b/linkerd/app/outbound/src/opaq/concrete.rs @@ -283,9 +283,10 @@ impl svc::Param for Endpoint { self.metadata .identity() .cloned() - .map(move |server_id| { + .map(move |(server_id, server_name)| { tls::ConditionalClientTls::Some(tls::ClientTls { server_id, + server_name, alpn: if use_transport_header { use linkerd_app_core::transport_header::PROTOCOL; Some(tls::client::AlpnProtocols(vec![PROTOCOL.into()])) diff --git a/linkerd/app/outbound/src/tcp/tagged_transport.rs b/linkerd/app/outbound/src/tcp/tagged_transport.rs index fc053592af..f78585269e 100644 --- a/linkerd/app/outbound/src/tcp/tagged_transport.rs +++ b/linkerd/app/outbound/src/tcp/tagged_transport.rs @@ -154,17 +154,18 @@ mod test { struct Endpoint { port_override: Option, authority: Option, - server_id: Option, + identity: Option<(tls::ServerId, tls::ServerName)>, proto: Option, } impl svc::Param for Endpoint { fn param(&self) -> tls::ConditionalClientTls { - self.server_id + self.identity .clone() - .map(|server_id| { + .map(|(server_id, server_name)| { tls::ConditionalClientTls::Some(tls::ClientTls { server_id, + server_name, alpn: Some(tls::client::AlpnProtocols(vec![ transport_header::PROTOCOL.into() ])), @@ -261,8 +262,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: None, proto: None, @@ -285,8 +287,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: Some(http::uri::Authority::from_str("foo.bar.example.com:5555").unwrap()), proto: None, @@ -309,8 +312,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: None, proto: None, @@ -333,8 +337,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: None, proto: Some(SessionProtocol::Http1), @@ -357,8 +362,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: Some(http::uri::Authority::from_str("foo.bar.example.com:5555").unwrap()), proto: Some(SessionProtocol::Http1), @@ -381,8 +387,9 @@ mod test { let e = Endpoint { port_override: Some(4143), - server_id: Some(tls::ServerId( - identity::Name::from_str("server.id").unwrap(), + identity: Some(( + tls::ServerId(identity::TlsName::from_str("server.id").unwrap()), + tls::ServerName(identity::Name::from_str("server.id").unwrap()), )), authority: None, proto: Some(SessionProtocol::Http1), diff --git a/linkerd/app/src/env.rs b/linkerd/app/src/env.rs index 479d6243db..3c8f5fa6e4 100644 --- a/linkerd/app/src/env.rs +++ b/linkerd/app/src/env.rs @@ -187,6 +187,7 @@ pub const ENV_IDENTITY_DISABLED: &str = "LINKERD2_PROXY_IDENTITY_DISABLED"; pub const ENV_IDENTITY_DIR: &str = "LINKERD2_PROXY_IDENTITY_DIR"; pub const ENV_IDENTITY_TRUST_ANCHORS: &str = "LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS"; pub const ENV_IDENTITY_IDENTITY_LOCAL_NAME: &str = "LINKERD2_PROXY_IDENTITY_LOCAL_NAME"; +pub const ENV_IDENTITY_IDENTITY_LOCAL_SERVER_NAME: &str = "LINKERD2_PROXY_IDENTITY_SERVER_NAME"; pub const ENV_IDENTITY_TOKEN_FILE: &str = "LINKERD2_PROXY_IDENTITY_TOKEN_FILE"; pub const ENV_IDENTITY_MIN_REFRESH: &str = "LINKERD2_PROXY_IDENTITY_MIN_REFRESH"; pub const ENV_IDENTITY_MAX_REFRESH: &str = "LINKERD2_PROXY_IDENTITY_MAX_REFRESH"; @@ -982,13 +983,20 @@ fn parse_port_range_set(s: &str) -> Result, ParseError> { Ok(set) } -pub(super) fn parse_identity(s: &str) -> Result { - identity::Name::from_str(s).map_err(|identity::InvalidName| { +pub(super) fn parse_identity(s: &str) -> Result { + identity::TlsName::from_str(s).map_err(|identity::InvalidName| { error!("Not a valid identity name: {}", s); ParseError::NameError }) } +pub(super) fn parse_name(s: &str) -> Result { + identity::Name::from_str(s).map_err(|identity::InvalidName| { + error!("Not a valid DNS name: {}", s); + ParseError::NameError + }) +} + pub(super) fn parse( strings: &dyn Strings, name: &str, @@ -1131,15 +1139,25 @@ pub fn parse_control_addr( ) -> Result, EnvError> { let a = parse(strings, &format!("{}_ADDR", base), parse_addr)?; let n = parse(strings, &format!("{}_NAME", base), parse_identity)?; - match (a, n) { - (None, None) => Ok(None), - (Some(ref addr), _) if addr.is_loopback() => Ok(Some(ControlAddr { + let sn = parse(strings, &format!("{}_SERVER_NAME", base), parse_name).or(parse( + strings, + &format!("{}_NAME", base), + parse_name, + ))?; + + match (a, n, sn) { + (None, None, None) => Ok(None), + (Some(ref addr), _, _) if addr.is_loopback() => Ok(Some(ControlAddr { addr: addr.clone(), identity: Conditional::None(tls::NoClientTls::Loopback), })), - (Some(addr), Some(name)) => Ok(Some(ControlAddr { + (Some(addr), Some(tls_name), Some(server_name)) => Ok(Some(ControlAddr { addr, - identity: Conditional::Some(tls::ServerId(name).into()), + identity: Conditional::Some(tls::ClientTls { + server_id: tls::ServerId(tls_name), + server_name: tls::ServerName(server_name), + alpn: None, + }), })), _ => { error!("{}_ADDR and {}_NAME must be specified together", base, base); @@ -1165,7 +1183,15 @@ pub fn parse_identity_config( ParseError::InvalidTokenSource }) }); - let li = parse(strings, ENV_IDENTITY_IDENTITY_LOCAL_NAME, parse_identity); + let tls_name = parse(strings, ENV_IDENTITY_IDENTITY_LOCAL_NAME, parse_identity); + + let server_name = match strings.get(ENV_IDENTITY_IDENTITY_LOCAL_SERVER_NAME)? { + Some(ref sn) => parse_name(sn) + .map(Some) + .map_err(|_| EnvError::InvalidEnvVar), + None => parse(strings, ENV_IDENTITY_IDENTITY_LOCAL_NAME, parse_name), + }; + let min_refresh = parse(strings, ENV_IDENTITY_MIN_REFRESH, parse_duration); let max_refresh = parse(strings, ENV_IDENTITY_MAX_REFRESH, parse_duration); @@ -1181,12 +1207,22 @@ pub fn parse_identity_config( return Err(EnvError::InvalidEnvVar); } - match (control?, ta?, dir?, li?, tok?, min_refresh?, max_refresh?) { + match ( + control?, + ta?, + dir?, + tls_name?, + server_name?, + tok?, + min_refresh?, + max_refresh?, + ) { ( Some(control), Some(trust_anchors_pem), Some(dir), - Some(local_name), + Some(tls_name), + Some(server_name), Some(token), min_refresh, max_refresh, @@ -1235,21 +1271,26 @@ pub fn parse_identity_config( max_refresh: max_refresh.unwrap_or(DEFAULT_IDENTITY_MAX_REFRESH), }; let docs = identity::Documents { - id: identity::LocalId(local_name), + name: identity::LocalName(server_name), + tls_name: identity::LocalId(tls_name), trust_anchors_pem, key_pkcs8: key?, csr_der: csr?, }; Ok((control, certify, docs)) } - (addr, trust_anchors, end_entity_dir, local_id, token, _minr, _maxr) => { + (addr, trust_anchors, end_entity_dir, tls_name, server_name, token, _minr, _maxr) => { let s = format!("{0}_ADDR and {0}_NAME", ENV_IDENTITY_SVC_BASE); let svc_env: &str = s.as_str(); for (unset, name) in &[ (addr.is_none(), svc_env), (trust_anchors.is_none(), ENV_IDENTITY_TRUST_ANCHORS), (end_entity_dir.is_none(), ENV_IDENTITY_DIR), - (local_id.is_none(), ENV_IDENTITY_IDENTITY_LOCAL_NAME), + (tls_name.is_none(), ENV_IDENTITY_IDENTITY_LOCAL_NAME), + ( + server_name.is_none(), + ENV_IDENTITY_IDENTITY_LOCAL_SERVER_NAME, + ), (token.is_none(), ENV_IDENTITY_TOKEN_FILE), ] { if *unset { diff --git a/linkerd/app/src/identity.rs b/linkerd/app/src/identity.rs index 040bd06516..5cb0a41838 100644 --- a/linkerd/app/src/identity.rs +++ b/linkerd/app/src/identity.rs @@ -1,6 +1,6 @@ pub use linkerd_app_core::identity::{ client::{certify, TokenSource}, - InvalidName, LocalId, Name, + InvalidName, LocalId, LocalName, Name, TlsName, }; use linkerd_app_core::{ control, dns, @@ -25,7 +25,8 @@ pub struct Config { #[derive(Clone)] pub struct Documents { - pub id: LocalId, + pub name: LocalName, + pub tls_name: LocalId, pub trust_anchors_pem: String, pub key_pkcs8: Vec, pub csr_der: Vec, @@ -56,7 +57,8 @@ struct NotifyReady { impl Config { pub fn build(self, dns: dns::Resolver, client_metrics: ClientMetrics) -> Result { let (store, receiver) = Mode::default().watch( - (*self.documents.id).clone(), + (*self.documents.name).clone(), + (*self.documents.tls_name).clone(), &self.documents.trust_anchors_pem, &self.documents.key_pkcs8, &self.documents.csr_der, @@ -93,8 +95,8 @@ impl Config { impl Credentials for NotifyReady { #[inline] - fn dns_name(&self) -> &Name { - self.store.dns_name() + fn tls_name(&self) -> &TlsName { + self.store.tls_name() } #[inline] @@ -119,7 +121,8 @@ impl Credentials for NotifyReady { impl std::fmt::Debug for Documents { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Documents") - .field("id", &self.id) + .field("name", &self.name) + .field("tls_name", &self.tls_name) .field("trust_anchors_pem", &self.trust_anchors_pem) .finish() } diff --git a/linkerd/app/src/lib.rs b/linkerd/app/src/lib.rs index 22bff9a62e..4384944675 100644 --- a/linkerd/app/src/lib.rs +++ b/linkerd/app/src/lib.rs @@ -311,8 +311,8 @@ impl App { &self.dst } - pub fn local_identity(&self) -> identity::Name { - self.identity.receiver().name().clone() + pub fn local_identity(&self) -> identity::TlsName { + self.identity.receiver().tls_name().clone() } pub fn identity_addr(&self) -> ControlAddr { @@ -364,7 +364,7 @@ impl App { // Kick off the identity so that the process can become ready. let local = identity.receiver(); - let local_id = local.name().clone(); + let local_id = local.tls_name().clone(); let ready = identity.ready(); tokio::spawn( identity diff --git a/linkerd/http-access-log/src/lib.rs b/linkerd/http-access-log/src/lib.rs index 8a785d64fd..c14c7ab113 100644 --- a/linkerd/http-access-log/src/lib.rs +++ b/linkerd/http-access-log/src/lib.rs @@ -28,7 +28,7 @@ pub struct NewAccessLog { pub struct AccessLogContext { inner: S, client_addr: SocketAddr, - client_id: Option, + client_id: Option, } struct ResponseFutureInner { diff --git a/linkerd/identity/src/credentials.rs b/linkerd/identity/src/credentials.rs index ebb2887b25..e378f6bd94 100644 --- a/linkerd/identity/src/credentials.rs +++ b/linkerd/identity/src/credentials.rs @@ -1,11 +1,11 @@ -use crate::Name; +use crate::TlsName; use linkerd_error::Result; use std::{ops::Deref, time::SystemTime}; /// Publishes certificates to be used by TLS implementations. pub trait Credentials { - /// Get the authoritative DNS-like name used in the certificate. - fn dns_name(&self) -> &Name; + /// Get the tls name used in the certificate's SAN. + fn tls_name(&self) -> &TlsName; /// Generate a CSR to to be sent to the identity service. fn gen_certificate_signing_request(&mut self) -> DerX509; diff --git a/linkerd/identity/src/lib.rs b/linkerd/identity/src/lib.rs index 4f65c8f9c8..096b8d88c2 100644 --- a/linkerd/identity/src/lib.rs +++ b/linkerd/identity/src/lib.rs @@ -4,10 +4,13 @@ mod credentials; mod local; mod name; +mod tls_name; pub use self::{ credentials::{Credentials, DerX509}, local::LocalId, + local::LocalName, name::Name, + tls_name::TlsName, }; pub use linkerd_dns_name::InvalidName; diff --git a/linkerd/identity/src/local.rs b/linkerd/identity/src/local.rs index 520e4f0e43..7122bf5fc1 100644 --- a/linkerd/identity/src/local.rs +++ b/linkerd/identity/src/local.rs @@ -1,22 +1,27 @@ use crate::Name; +use crate::TlsName; use std::{fmt, ops::Deref}; /// A newtype for local server identities. #[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct LocalId(pub Name); +pub struct LocalId(pub TlsName); + +/// A newtype for local server names. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct LocalName(pub Name); // === impl LocalId === -impl From for LocalId { - fn from(n: Name) -> Self { +impl From for LocalId { + fn from(n: TlsName) -> Self { Self(n) } } impl Deref for LocalId { - type Target = Name; + type Target = TlsName; - fn deref(&self) -> &Name { + fn deref(&self) -> &TlsName { &self.0 } } @@ -27,8 +32,24 @@ impl fmt::Display for LocalId { } } -impl From for Name { - fn from(LocalId(name): LocalId) -> Name { +impl From for TlsName { + fn from(LocalId(name): LocalId) -> TlsName { name } } + +// === impl LocalName === + +impl Deref for LocalName { + type Target = Name; + + fn deref(&self) -> &Name { + &self.0 + } +} + +impl fmt::Display for LocalName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/linkerd/identity/src/name.rs b/linkerd/identity/src/name.rs index 591d0f6af3..03f7b24267 100644 --- a/linkerd/identity/src/name.rs +++ b/linkerd/identity/src/name.rs @@ -1,7 +1,7 @@ use linkerd_dns_name::InvalidName; use std::{fmt, ops::Deref, str::FromStr, sync::Arc}; -/// An endpoint's identity. +/// An endpoint's DNS like name. #[derive(Clone, Eq, PartialEq, Hash)] pub struct Name(Arc); diff --git a/linkerd/identity/src/tls_name.rs b/linkerd/identity/src/tls_name.rs new file mode 100644 index 0000000000..f0d4d85052 --- /dev/null +++ b/linkerd/identity/src/tls_name.rs @@ -0,0 +1,46 @@ +use linkerd_dns_name::InvalidName; +use std::{fmt, ops::Deref, str::FromStr, sync::Arc}; + +/// An endpoint's identity. +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct TlsName(pub Arc); + +// === impl Name === + +impl From for TlsName { + fn from(n: linkerd_dns_name::Name) -> Self { + TlsName(Arc::new(n)) + } +} + +impl FromStr for TlsName { + type Err = InvalidName; + + fn from_str(s: &str) -> Result { + if s.ends_with('.') { + return Err(InvalidName); // SNI hostnames are implicitly absolute. + } + + linkerd_dns_name::Name::from_str(s).map(|n| TlsName(Arc::new(n))) + } +} + +impl Deref for TlsName { + type Target = linkerd_dns_name::Name; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Debug for TlsName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for TlsName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fmt::Display::fmt(&self.0, f) + } +} diff --git a/linkerd/meshtls/boring/src/client.rs b/linkerd/meshtls/boring/src/client.rs index 2806e34ce4..adad18c4f4 100644 --- a/linkerd/meshtls/boring/src/client.rs +++ b/linkerd/meshtls/boring/src/client.rs @@ -1,8 +1,8 @@ use crate::creds::CredsRx; -use linkerd_identity::Name; +use linkerd_identity::{Name, TlsName}; use linkerd_io as io; use linkerd_stack::{NewService, Service}; -use linkerd_tls::{client::AlpnProtocols, ClientTls, NegotiatedProtocolRef, ServerId}; +use linkerd_tls::{client::AlpnProtocols, ClientTls, NegotiatedProtocolRef, ServerId, ServerName}; use std::{future::Future, pin::Pin, sync::Arc, task::Context}; use tracing::debug; @@ -13,7 +13,9 @@ pub struct NewClient(CredsRx); pub struct Connect { rx: CredsRx, alpn: Option]>>, - server_id: Name, + server_name: Name, + // TODO: Use by the client to verify the SANs of the peer's end cert + _server_id: TlsName, } pub type ConnectFuture = Pin>> + Send>>; @@ -48,11 +50,14 @@ impl std::fmt::Debug for NewClient { impl Connect { pub(crate) fn new(client_tls: ClientTls, rx: CredsRx) -> Self { let ServerId(server_id) = client_tls.server_id; + let ServerName(server_name) = client_tls.server_name; + let alpn = client_tls.alpn.map(|AlpnProtocols(ps)| ps.into()); Self { rx, alpn, - server_id, + server_name, + _server_id: server_id, } } } @@ -70,7 +75,7 @@ where } fn call(&mut self, io: I) -> Self::Future { - let id = self.server_id.clone(); + let server_name = self.server_name.clone(); let connector = self .rx .borrow() @@ -80,7 +85,7 @@ where let config = conn .configure() .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - let io = tokio_boring::connect(config, id.as_str(), io) + let io = tokio_boring::connect(config, server_name.as_str(), io) .await .map_err(|e| match e.as_io_error() { // TODO(ver) boring should let us take ownership of the error directly. diff --git a/linkerd/meshtls/boring/src/creds.rs b/linkerd/meshtls/boring/src/creds.rs index 5178dba126..659853a02e 100644 --- a/linkerd/meshtls/boring/src/creds.rs +++ b/linkerd/meshtls/boring/src/creds.rs @@ -13,7 +13,8 @@ use std::sync::Arc; use tokio::sync::watch; pub fn watch( - identity: id::Name, + name: id::Name, + tls_name: id::TlsName, roots_pem: &str, key_pkcs8: &[u8], csr: &[u8], @@ -25,8 +26,8 @@ pub fn watch( }; let (tx, rx) = watch::channel(Creds::from(creds.clone())); - let rx = Receiver::new(identity.clone(), rx); - let store = Store::new(creds, csr, identity, tx); + let rx = Receiver::new(name, tls_name.clone(), rx); + let store = Store::new(creds, csr, tls_name, tx); Ok((store, rx)) } diff --git a/linkerd/meshtls/boring/src/creds/receiver.rs b/linkerd/meshtls/boring/src/creds/receiver.rs index 9e78dea160..3f5e1f5653 100644 --- a/linkerd/meshtls/boring/src/creds/receiver.rs +++ b/linkerd/meshtls/boring/src/creds/receiver.rs @@ -1,21 +1,22 @@ use super::CredsRx; use crate::{NewClient, Server}; -use linkerd_identity::Name; +use linkerd_identity::{Name, TlsName}; #[derive(Clone)] pub struct Receiver { name: Name, + tls_name: TlsName, rx: CredsRx, } impl Receiver { - pub(crate) fn new(name: Name, rx: CredsRx) -> Self { - Self { name, rx } + pub(crate) fn new(name: Name, tls_name: TlsName, rx: CredsRx) -> Self { + Self { tls_name, name, rx } } - /// Returns the local identity. - pub fn name(&self) -> &Name { - &self.name + /// Returns the local identity name. + pub fn tls_name(&self) -> &TlsName { + &self.tls_name } /// Returns a `NewClient` that can be used to establish TLS on client connections. @@ -33,6 +34,7 @@ impl std::fmt::Debug for Receiver { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Receiver") .field("name", &self.name) + .field("tls_name", &self.tls_name) .finish() } } diff --git a/linkerd/meshtls/boring/src/creds/store.rs b/linkerd/meshtls/boring/src/creds/store.rs index bdabc7dbad..f1f6c4644f 100644 --- a/linkerd/meshtls/boring/src/creds/store.rs +++ b/linkerd/meshtls/boring/src/creds/store.rs @@ -7,18 +7,23 @@ use std::sync::Arc; pub struct Store { creds: Arc, csr: Vec, - name: id::Name, + tls_name: id::TlsName, tx: CredsTx, } // === impl Store === impl Store { - pub(super) fn new(creds: Arc, csr: &[u8], name: id::Name, tx: CredsTx) -> Self { + pub(super) fn new( + creds: Arc, + csr: &[u8], + tls_name: id::TlsName, + tx: CredsTx, + ) -> Self { Self { creds, csr: csr.into(), - name, + tls_name, tx, } } @@ -27,7 +32,7 @@ impl Store { for san in cert.subject_alt_names().into_iter().flatten() { if let Some(n) = san.dnsname() { if let Ok(name) = n.parse::() { - if name == *self.name { + if name == *self.tls_name { return true; } } @@ -40,8 +45,8 @@ impl Store { impl id::Credentials for Store { /// Returns the proxy's identity. - fn dns_name(&self) -> &id::Name { - &self.name + fn tls_name(&self) -> &id::TlsName { + &self.tls_name } /// Returns the CSR that was configured at proxy startup. diff --git a/linkerd/meshtls/boring/src/server.rs b/linkerd/meshtls/boring/src/server.rs index 623daf849b..9f82ae2308 100644 --- a/linkerd/meshtls/boring/src/server.rs +++ b/linkerd/meshtls/boring/src/server.rs @@ -2,7 +2,7 @@ use crate::creds::CredsRx; use linkerd_identity::Name; use linkerd_io as io; use linkerd_stack::{Param, Service}; -use linkerd_tls::{ClientId, LocalId, NegotiatedProtocol, ServerTls}; +use linkerd_tls::{ClientId, LocalName, NegotiatedProtocol, ServerTls}; use std::{future::Future, pin::Pin, sync::Arc, task::Context}; use tracing::debug; @@ -41,9 +41,9 @@ impl Server { } } -impl Param for Server { - fn param(&self) -> LocalId { - LocalId(self.name.clone()) +impl Param for Server { + fn param(&self) -> LocalName { + LocalName(self.name.clone()) } } diff --git a/linkerd/meshtls/boring/src/tests.rs b/linkerd/meshtls/boring/src/tests.rs index 1fd56856b3..4d96ae4cae 100644 --- a/linkerd/meshtls/boring/src/tests.rs +++ b/linkerd/meshtls/boring/src/tests.rs @@ -6,6 +6,7 @@ fn load(ent: &Entity) -> crate::creds::Store { let roots_pem = std::str::from_utf8(ent.trust_anchors).expect("valid PEM"); let (store, _) = crate::creds::watch( ent.name.parse().unwrap(), + ent.tls_name.parse().unwrap(), roots_pem, ent.key, b"fake CSR data", diff --git a/linkerd/meshtls/rustls/src/client.rs b/linkerd/meshtls/rustls/src/client.rs index a5eef085fc..e31c2aa64b 100644 --- a/linkerd/meshtls/rustls/src/client.rs +++ b/linkerd/meshtls/rustls/src/client.rs @@ -15,7 +15,7 @@ pub struct NewClient { /// A `Service` that initiates client-side TLS connections. #[derive(Clone)] pub struct Connect { - server_id: rustls::ServerName, + server_name: rustls::ServerName, config: Arc, } @@ -68,10 +68,13 @@ impl Connect { } }; - let server_id = rustls::ServerName::try_from(client_tls.server_id.as_str()) - .expect("identity must be a valid DNS name"); + let server_name = rustls::ServerName::try_from(client_tls.server_name.as_str()) + .expect("server name must be a valid DNS name"); - Self { server_id, config } + Self { + server_name, + config, + } } } @@ -90,7 +93,7 @@ where fn call(&mut self, io: I) -> Self::Future { tokio_rustls::TlsConnector::from(self.config.clone()) // XXX(eliza): it's a bummer that the server name has to be cloned here... - .connect(self.server_id.clone(), io) + .connect(self.server_name.clone(), io) .map_ok(ClientIo) } } diff --git a/linkerd/meshtls/rustls/src/creds.rs b/linkerd/meshtls/rustls/src/creds.rs index 084efecae3..e6e5a66647 100644 --- a/linkerd/meshtls/rustls/src/creds.rs +++ b/linkerd/meshtls/rustls/src/creds.rs @@ -20,7 +20,8 @@ pub struct InvalidKey(KeyRejected); pub struct InvalidTrustRoots(()); pub fn watch( - identity: id::Name, + name: id::Name, + tls_name: id::TlsName, roots_pem: &str, key_pkcs8: &[u8], csr: &[u8], @@ -80,13 +81,13 @@ pub fn watch( watch::channel(store::server_config(roots.clone(), empty_resolver)) }; - let rx = Receiver::new(identity.clone(), client_rx, server_rx); + let rx = Receiver::new(name, tls_name.clone(), client_rx, server_rx); let store = Store::new( roots, server_cert_verifier, key, csr, - identity, + tls_name, client_tx, server_tx, ); @@ -97,6 +98,7 @@ pub fn watch( #[cfg(feature = "test-util")] pub fn for_test(ent: &linkerd_tls_test_util::Entity) -> (Store, Receiver) { watch( + ent.name.parse().expect("name must be valid"), ent.name.parse().expect("name must be valid"), std::str::from_utf8(ent.trust_anchors).expect("roots must be PEM"), ent.key, diff --git a/linkerd/meshtls/rustls/src/creds/receiver.rs b/linkerd/meshtls/rustls/src/creds/receiver.rs index 51c1a1f558..91e61c6324 100644 --- a/linkerd/meshtls/rustls/src/creds/receiver.rs +++ b/linkerd/meshtls/rustls/src/creds/receiver.rs @@ -1,5 +1,5 @@ use crate::{NewClient, Server}; -use linkerd_identity::Name; +use linkerd_identity::{Name, TlsName}; use std::sync::Arc; use tokio::sync::watch; use tokio_rustls::rustls; @@ -8,6 +8,7 @@ use tokio_rustls::rustls; #[derive(Clone)] pub struct Receiver { name: Name, + tls_name: TlsName, client_rx: watch::Receiver>, server_rx: watch::Receiver>, } @@ -17,19 +18,21 @@ pub struct Receiver { impl Receiver { pub(super) fn new( name: Name, + tls_name: TlsName, client_rx: watch::Receiver>, server_rx: watch::Receiver>, ) -> Self { Self { name, + tls_name, client_rx, server_rx, } } /// Returns the local identity. - pub fn name(&self) -> &Name { - &self.name + pub fn tls_name(&self) -> &TlsName { + &self.tls_name } /// Returns a `NewClient` that can be used to establish TLS on client connections. @@ -47,6 +50,7 @@ impl std::fmt::Debug for Receiver { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Receiver") .field("name", &self.name) + .field("tls_name", &self.name) .finish() } } @@ -86,6 +90,7 @@ mod tests { let (_, client_rx) = watch::channel(Arc::new(empty_client_config())); let receiver = Receiver { name: "example".parse().unwrap(), + tls_name: "example".parse().unwrap(), server_rx, client_rx, }; @@ -109,6 +114,7 @@ mod tests { let (_, client_rx) = watch::channel(Arc::new(empty_client_config())); let receiver = Receiver { name: "example".parse().unwrap(), + tls_name: "example".parse().unwrap(), server_rx, client_rx, }; diff --git a/linkerd/meshtls/rustls/src/creds/store.rs b/linkerd/meshtls/rustls/src/creds/store.rs index 1c744106bd..d6ace07a52 100644 --- a/linkerd/meshtls/rustls/src/creds/store.rs +++ b/linkerd/meshtls/rustls/src/creds/store.rs @@ -12,7 +12,7 @@ pub struct Store { server_cert_verifier: Arc, key: Arc, csr: Arc<[u8]>, - name: id::Name, + tls_name: id::TlsName, client_tx: watch::Sender>, server_tx: watch::Sender>, } @@ -75,7 +75,7 @@ impl Store { server_cert_verifier: Arc, key: EcdsaKeyPair, csr: &[u8], - name: id::Name, + tls_name: id::TlsName, client_tx: watch::Sender>, server_tx: watch::Sender>, ) -> Self { @@ -84,7 +84,7 @@ impl Store { key: Arc::new(key), server_cert_verifier, csr: csr.into(), - name, + tls_name, client_tx, server_tx, } @@ -105,7 +105,7 @@ impl Store { /// Ensures the certificate is valid for the services we terminate for TLS. This assumes that /// server cert validation does the same or more validation than client cert validation. fn validate(&self, certs: &[rustls::Certificate]) -> Result<()> { - let name = rustls::ServerName::try_from(self.name.as_str()) + let name = rustls::ServerName::try_from(self.tls_name.as_str()) .expect("server name must be a valid DNS name"); static NO_OCSP: &[u8] = &[]; let end_entity = &certs[0]; @@ -120,6 +120,7 @@ impl Store { NO_OCSP, now, )?; + //// ... /// debug!("Certified"); Ok(()) } @@ -127,8 +128,8 @@ impl Store { impl id::Credentials for Store { /// Returns the proxy's identity. - fn dns_name(&self) -> &id::Name { - &self.name + fn tls_name(&self) -> &id::TlsName { + &self.tls_name } /// Returns the CSR that was configured at proxy startup. diff --git a/linkerd/meshtls/rustls/src/server.rs b/linkerd/meshtls/rustls/src/server.rs index 1b98216634..000baa1efb 100644 --- a/linkerd/meshtls/rustls/src/server.rs +++ b/linkerd/meshtls/rustls/src/server.rs @@ -1,5 +1,5 @@ use futures::prelude::*; -use linkerd_identity::{LocalId, Name}; +use linkerd_identity::{LocalName, Name}; use linkerd_io as io; use linkerd_stack::{Param, Service}; use linkerd_tls::{ClientId, NegotiatedProtocol, NegotiatedProtocolRef, ServerTls}; @@ -82,9 +82,9 @@ impl Server { } } -impl Param for Server { - fn param(&self) -> LocalId { - LocalId(self.name.clone()) +impl Param for Server { + fn param(&self) -> LocalName { + LocalName(self.name.clone()) } } diff --git a/linkerd/meshtls/rustls/src/tests.rs b/linkerd/meshtls/rustls/src/tests.rs index 1fd56856b3..4d96ae4cae 100644 --- a/linkerd/meshtls/rustls/src/tests.rs +++ b/linkerd/meshtls/rustls/src/tests.rs @@ -6,6 +6,7 @@ fn load(ent: &Entity) -> crate::creds::Store { let roots_pem = std::str::from_utf8(ent.trust_anchors).expect("valid PEM"); let (store, _) = crate::creds::watch( ent.name.parse().unwrap(), + ent.tls_name.parse().unwrap(), roots_pem, ent.key, b"fake CSR data", diff --git a/linkerd/meshtls/src/creds.rs b/linkerd/meshtls/src/creds.rs index 27305f52f7..5170b87e42 100644 --- a/linkerd/meshtls/src/creds.rs +++ b/linkerd/meshtls/src/creds.rs @@ -1,6 +1,6 @@ use crate::{NewClient, Server}; use linkerd_error::Result; -use linkerd_identity::{Credentials, DerX509, Name}; +use linkerd_identity::{Credentials, DerX509, TlsName}; #[cfg(feature = "boring")] pub use crate::boring; @@ -32,13 +32,13 @@ pub enum Receiver { // === impl Store === impl Credentials for Store { - fn dns_name(&self) -> &Name { + fn tls_name(&self) -> &TlsName { match self { #[cfg(feature = "boring")] - Self::Boring(store) => store.dns_name(), + Self::Boring(store) => store.tls_name(), #[cfg(feature = "rustls")] - Self::Rustls(store) => store.dns_name(), + Self::Rustls(store) => store.tls_name(), #[cfg(not(feature = "__has_any_tls_impls"))] _ => crate::no_tls!(), } @@ -91,13 +91,13 @@ impl From for Receiver { } impl Receiver { - pub fn name(&self) -> &Name { + pub fn tls_name(&self) -> &TlsName { match self { #[cfg(feature = "boring")] - Self::Boring(receiver) => receiver.name(), + Self::Boring(receiver) => receiver.tls_name(), #[cfg(feature = "rustls")] - Self::Rustls(receiver) => receiver.name(), + Self::Rustls(receiver) => receiver.tls_name(), #[cfg(not(feature = "__has_any_tls_impls"))] _ => crate::no_tls!(), } diff --git a/linkerd/meshtls/src/lib.rs b/linkerd/meshtls/src/lib.rs index 1ee2c3d754..55d32859a8 100644 --- a/linkerd/meshtls/src/lib.rs +++ b/linkerd/meshtls/src/lib.rs @@ -21,7 +21,7 @@ pub use self::{ server::{Server, ServerIo, TerminateFuture}, }; use linkerd_error::{Error, Result}; -use linkerd_identity::Name; +use linkerd_identity::{Name, TlsName}; use std::str::FromStr; #[cfg(feature = "boring")] @@ -82,7 +82,8 @@ impl Default for Mode { impl Mode { pub fn watch( self, - identity: Name, + name: Name, + tls_name: TlsName, roots_pem: &str, key_pkcs8: &[u8], csr: &[u8], @@ -90,7 +91,8 @@ impl Mode { match self { #[cfg(feature = "boring")] Self::Boring => { - let (store, receiver) = boring::creds::watch(identity, roots_pem, key_pkcs8, csr)?; + let (store, receiver) = + boring::creds::watch(name, tls_name, roots_pem, key_pkcs8, csr)?; Ok(( creds::Store::Boring(store), creds::Receiver::Boring(receiver), @@ -99,7 +101,8 @@ impl Mode { #[cfg(feature = "rustls")] Self::Rustls => { - let (store, receiver) = rustls::creds::watch(identity, roots_pem, key_pkcs8, csr)?; + let (store, receiver) = + rustls::creds::watch(name, tls_name, roots_pem, key_pkcs8, csr)?; Ok(( creds::Store::Rustls(store), creds::Receiver::Rustls(receiver), @@ -107,7 +110,7 @@ impl Mode { } #[cfg(not(feature = "__has_any_tls_impls"))] - _ => no_tls!(identity, roots_pem, key_pkcs8, csr), + _ => no_tls!(name, tls_name, roots_pem, key_pkcs8, csr), } } } diff --git a/linkerd/meshtls/src/server.rs b/linkerd/meshtls/src/server.rs index cceb9c570a..bd40652948 100644 --- a/linkerd/meshtls/src/server.rs +++ b/linkerd/meshtls/src/server.rs @@ -1,5 +1,5 @@ use linkerd_error::Result; -use linkerd_identity::LocalId; +use linkerd_identity::LocalName; use linkerd_io as io; use linkerd_stack::{Param, Service}; use linkerd_tls::ServerTls; @@ -57,9 +57,9 @@ pub enum ServerIo { // === impl Server === -impl Param for Server { +impl Param for Server { #[inline] - fn param(&self) -> LocalId { + fn param(&self) -> LocalName { match self { #[cfg(feature = "boring")] Self::Boring(srv) => srv.param(), diff --git a/linkerd/meshtls/tests/util.rs b/linkerd/meshtls/tests/util.rs index 0152da2aa4..e64840e78a 100644 --- a/linkerd/meshtls/tests/util.rs +++ b/linkerd/meshtls/tests/util.rs @@ -4,7 +4,7 @@ use futures::prelude::*; use linkerd_conditional::Conditional; use linkerd_error::Infallible; -use linkerd_identity::{Credentials, DerX509, Name}; +use linkerd_identity::{Credentials, DerX509, Name, TlsName}; use linkerd_io::{self as io, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use linkerd_meshtls as meshtls; use linkerd_proxy_transport::{ @@ -50,9 +50,11 @@ pub async fn proxy_to_proxy_tls_works(mode: meshtls::Mode) { let (_foo, _, server_tls) = load(mode, &test_util::FOO_NS1); let (_bar, client_tls, _) = load(mode, &test_util::BAR_NS1); let server_id = tls::ServerId(test_util::FOO_NS1.name.parse().unwrap()); + let server_name = tls::ServerName(test_util::FOO_NS1.tls_name.parse().unwrap()); + let (client_result, server_result) = run_test( client_tls.clone(), - Conditional::Some(server_id.clone()), + Conditional::Some((server_id.clone(), server_name.clone())), |conn| write_then_read(conn, PING), server_tls, |(_, conn)| read_then_write(conn, PING.len(), PONG), @@ -62,6 +64,7 @@ pub async fn proxy_to_proxy_tls_works(mode: meshtls::Mode) { client_result.tls, Some(Conditional::Some(tls::ClientTls { server_id, + server_name, alpn: None, })) ); @@ -83,10 +86,14 @@ pub async fn proxy_to_proxy_tls_pass_through_when_identity_does_not_match(mode: // identity other than `server_tls.server_identity` would work. let (_bar, client_tls, _) = load(mode, &test_util::BAR_NS1); let sni = test_util::BAR_NS1.name.parse::().unwrap(); + let tls_name = test_util::BAR_NS1.tls_name.parse::().unwrap(); let (client_result, server_result) = run_test( client_tls, - Conditional::Some(tls::ServerId(sni.clone())), + Conditional::Some(( + tls::ServerId(tls_name.clone()), + tls::ServerName(sni.clone()), + )), |conn| write_then_read(conn, PING), server_tls, |(_, conn)| read_then_write(conn, START_OF_TLS.len(), PONG), @@ -100,7 +107,7 @@ pub async fn proxy_to_proxy_tls_pass_through_when_identity_does_not_match(mode: assert_eq!( server_result.tls, Some(Conditional::Some(tls::ServerTls::Passthru { - sni: tls::ServerId(sni) + sni: tls::ServerName(sni) })) ); assert_eq!(&server_result.result.unwrap()[..], START_OF_TLS); @@ -119,6 +126,7 @@ fn load( let (mut store, rx) = mode .watch( ent.name.parse().unwrap(), + ent.tls_name.parse().unwrap(), roots_pem, ent.key, b"fake CSR data", @@ -153,7 +161,7 @@ type ClientIo = /// side. async fn run_test( client_tls: meshtls::NewClient, - client_server_id: Conditional, + client_server_id: Conditional<(tls::ServerId, tls::ServerName), tls::NoClientTls>, client: C, server_tls: meshtls::Server, server: S, @@ -230,11 +238,26 @@ where // parallels the server side. let (sender, receiver) = mpsc::channel::>(); - let tls = Some(client_server_id.clone().map(Into::into)); + let tls = Some( + client_server_id + .clone() + .map(|(server_id, server_name)| tls::ClientTls { + server_id, + server_name, + alpn: None, + }), + ); let client = async move { let conn = tls::Client::layer(client_tls) .layer(ConnectTcp::new(Keepalive(None))) - .oneshot(Target(server_addr.into(), client_server_id.map(Into::into))) + .oneshot(Target( + server_addr.into(), + client_server_id.map(|(server_id, server_name)| tls::ClientTls { + server_id, + server_name, + alpn: None, + }), + )) .await; match conn { Err(e) => { diff --git a/linkerd/proxy/api-resolve/src/metadata.rs b/linkerd/proxy/api-resolve/src/metadata.rs index dd1623a959..eb5fb23ba0 100644 --- a/linkerd/proxy/api-resolve/src/metadata.rs +++ b/linkerd/proxy/api-resolve/src/metadata.rs @@ -1,5 +1,5 @@ use http::uri::Authority; -use linkerd_tls::client::ServerId; +use linkerd_tls::client::{ServerId, ServerName}; use std::collections::BTreeMap; /// Endpoint labels are lexigraphically ordered by key. @@ -18,7 +18,7 @@ pub struct Metadata { tagged_transport_port: Option, /// How to verify TLS for the endpoint. - identity: Option, + identity: Option<(ServerId, ServerName)>, /// Used to override the the authority if needed authority_override: Option, @@ -55,7 +55,7 @@ impl Metadata { labels: impl IntoIterator, protocol_hint: ProtocolHint, tagged_transport_port: Option, - identity: Option, + identity: Option<(ServerId, ServerName)>, authority_override: Option, ) -> Self { Self { @@ -76,7 +76,7 @@ impl Metadata { self.protocol_hint } - pub fn identity(&self) -> Option<&ServerId> { + pub fn identity(&self) -> Option<&(ServerId, ServerName)> { self.identity.as_ref() } diff --git a/linkerd/proxy/api-resolve/src/pb.rs b/linkerd/proxy/api-resolve/src/pb.rs index 889dc6860c..9f2362b2ac 100644 --- a/linkerd/proxy/api-resolve/src/pb.rs +++ b/linkerd/proxy/api-resolve/src/pb.rs @@ -7,7 +7,7 @@ use crate::{ metadata::{Metadata, ProtocolHint}, }; use http::uri::Authority; -use linkerd_tls::client::ServerId; +use linkerd_tls::client::{ServerId, ServerName}; use std::{collections::HashMap, net::SocketAddr, str::FromStr}; /// Construct a new labeled `SocketAddr `from a protobuf `WeightedAddr`. @@ -39,24 +39,26 @@ pub fn to_addr_meta( } } - let tls_id = pb.tls_identity.and_then(to_id); + let identity = pb.tls_identity.and_then(to_identity); + let meta = Metadata::new( labels, proto_hint, tagged_transport_port, - tls_id, + identity, authority_override, ); Some((addr, meta)) } -fn to_id(pb: TlsIdentity) -> Option { +// TODO: differenciate between ServerId and ServerName +fn to_identity(pb: TlsIdentity) -> Option<(ServerId, ServerName)> { use crate::api::destination::tls_identity::Strategy; let Strategy::DnsLikeIdentity(i) = pb.strategy?; - match ServerId::from_str(&i.name) { - Ok(i) => Some(i), - Err(_) => { + match (ServerId::from_str(&i.name), ServerName::from_str(&i.name)) { + (Ok(i), Ok(n)) => Some((i, n)), + _ => { tracing::warn!("Ignoring invalid identity: {}", i.name); None } diff --git a/linkerd/proxy/identity-client/src/certify.rs b/linkerd/proxy/identity-client/src/certify.rs index ec4e6c6636..dd9450e503 100644 --- a/linkerd/proxy/identity-client/src/certify.rs +++ b/linkerd/proxy/identity-client/src/certify.rs @@ -95,7 +95,7 @@ where { let req = tonic::Request::new(api::CertifyRequest { token: token.load()?, - identity: credentials.dns_name().to_string(), + identity: credentials.tls_name().to_string(), certificate_signing_request: credentials.gen_certificate_signing_request().to_vec(), }); diff --git a/linkerd/tls/src/client.rs b/linkerd/tls/src/client.rs index 3243393d1e..53f97c200d 100644 --- a/linkerd/tls/src/client.rs +++ b/linkerd/tls/src/client.rs @@ -16,12 +16,17 @@ use tracing::debug; /// A newtype for target server identities. #[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct ServerId(pub id::Name); +pub struct ServerId(pub id::TlsName); + +/// A newtype for target server names. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ServerName(pub id::Name); /// A stack parameter that configures a `Client` to establish a TLS connection. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ClientTls { pub server_id: ServerId, + pub server_name: ServerName, pub alpn: Option, } @@ -73,17 +78,6 @@ pub struct ConnectMeta { pub tls: Conditional, NoClientTls>, } -// === impl ClientTls === - -impl From for ClientTls { - fn from(server_id: ServerId) -> Self { - Self { - server_id, - alpn: None, - } - } -} - // === impl Client === impl Client { @@ -180,22 +174,16 @@ where // === impl ServerId === -impl From for ServerId { - fn from(n: id::Name) -> Self { +impl From for ServerId { + fn from(n: id::TlsName) -> Self { Self(n) } } -impl From for id::Name { - fn from(ServerId(name): ServerId) -> id::Name { - name - } -} - impl Deref for ServerId { - type Target = id::Name; + type Target = id::TlsName; - fn deref(&self) -> &id::Name { + fn deref(&self) -> &id::TlsName { &self.0 } } @@ -203,7 +191,7 @@ impl Deref for ServerId { impl FromStr for ServerId { type Err = id::InvalidName; fn from_str(s: &str) -> Result { - id::Name::from_str(s).map(ServerId) + id::TlsName::from_str(s).map(ServerId) } } @@ -213,6 +201,35 @@ impl fmt::Display for ServerId { } } +// === impl ServerName === + +impl FromStr for ServerName { + type Err = id::InvalidName; + fn from_str(s: &str) -> Result { + id::Name::from_str(s).map(ServerName) + } +} + +impl From for id::Name { + fn from(ServerName(name): ServerName) -> id::Name { + name + } +} + +impl Deref for ServerName { + type Target = id::Name; + + fn deref(&self) -> &id::Name { + &self.0 + } +} + +impl fmt::Display for ServerName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + // === impl NoClientTls === impl fmt::Display for NoClientTls { diff --git a/linkerd/tls/src/lib.rs b/linkerd/tls/src/lib.rs index 9aad11f13d..65fb5a429f 100755 --- a/linkerd/tls/src/lib.rs +++ b/linkerd/tls/src/lib.rs @@ -4,10 +4,12 @@ pub mod client; pub mod server; -pub use linkerd_identity::LocalId; +pub use linkerd_identity::{LocalId, LocalName}; pub use self::{ - client::{Client, ClientTls, ConditionalClientTls, ConnectMeta, NoClientTls, ServerId}, + client::{ + Client, ClientTls, ConditionalClientTls, ConnectMeta, NoClientTls, ServerId, ServerName, + }, server::{ClientId, ConditionalServerTls, NewDetectTls, NoServerTls, ServerTls}, }; diff --git a/linkerd/tls/src/server.rs b/linkerd/tls/src/server.rs index e0e0ea6a5e..5a5bc3dde9 100644 --- a/linkerd/tls/src/server.rs +++ b/linkerd/tls/src/server.rs @@ -1,6 +1,6 @@ mod client_hello; -use crate::{NegotiatedProtocol, ServerId}; +use crate::{NegotiatedProtocol, ServerName}; use bytes::BytesMut; use futures::prelude::*; use linkerd_conditional::Conditional; @@ -21,7 +21,7 @@ use tracing::{debug, trace, warn}; /// A newtype for remote client idenities. #[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct ClientId(pub id::Name); +pub struct ClientId(pub id::TlsName); /// Indicates a server-side connection's TLS status. #[derive(Clone, Debug, Eq, PartialEq, Hash)] @@ -31,7 +31,7 @@ pub enum ServerTls { negotiated_protocol: Option, }, Passthru { - sni: ServerId, + sni: ServerName, }, } @@ -134,7 +134,7 @@ where T: Clone + Send + 'static, P: InsertParam + Clone + Send + Sync + 'static, P::Target: Send + 'static, - L: Param + Clone + Send + 'static, + L: Param + Clone + Send + 'static, L: Service, Response = (ServerTls, LIo), Error = io::Error>, L::Future: Send, LIo: io::AsyncRead + io::AsyncWrite + Send + Sync + Unpin + 'static, @@ -164,10 +164,10 @@ where Box::pin(async move { let (sni, io) = detect.await.map_err(|_| ServerTlsTimeoutError(()))??; - let id::LocalId(id) = tls.param(); + let id::LocalName(id) = tls.param(); let (peer, io) = match sni { // If we detected an SNI matching this proxy, terminate TLS. - Some(ServerId(sni)) if sni == id => { + Some(ServerName(sni)) if sni == id => { trace!("Identified local SNI"); let (peer, io) = tls.oneshot(io).await?; (Conditional::Some(peer), EitherIo::Left(io)) @@ -193,7 +193,7 @@ where } /// Peek or buffer the provided stream to determine an SNI value. -async fn detect_sni(mut io: I) -> io::Result<(Option, DetectIo)> +async fn detect_sni(mut io: I) -> io::Result<(Option, DetectIo)> where I: io::Peek + io::AsyncRead + io::AsyncWrite + Send + Sync + Unpin, { @@ -249,22 +249,22 @@ where // === impl ClientId === -impl From for ClientId { - fn from(n: id::Name) -> Self { +impl From for ClientId { + fn from(n: id::TlsName) -> Self { Self(n) } } -impl From for id::Name { - fn from(ClientId(name): ClientId) -> id::Name { +impl From for id::TlsName { + fn from(ClientId(name): ClientId) -> id::TlsName { name } } impl Deref for ClientId { - type Target = id::Name; + type Target = id::TlsName; - fn deref(&self) -> &id::Name { + fn deref(&self) -> &id::TlsName { &self.0 } } @@ -278,7 +278,7 @@ impl fmt::Display for ClientId { impl FromStr for ClientId { type Err = id::InvalidName; fn from_str(s: &str) -> Result { - id::Name::from_str(s).map(Self) + id::TlsName::from_str(s).map(Self) } } @@ -331,7 +331,7 @@ mod tests { .expect("SNI detection must not fail"); let identity = id::Name::from_str("example.com").unwrap(); - assert_eq!(sni, Some(ServerId(identity))); + assert_eq!(sni, Some(ServerName(identity))); match io { EitherIo::Left(_) => panic!("Detected IO should be buffered"), diff --git a/linkerd/tls/src/server/client_hello.rs b/linkerd/tls/src/server/client_hello.rs index bc77aa2988..658322d988 100644 --- a/linkerd/tls/src/server/client_hello.rs +++ b/linkerd/tls/src/server/client_hello.rs @@ -1,4 +1,4 @@ -use crate::ServerId; +use crate::client::ServerName; use linkerd_identity as id; use tracing::trace; @@ -18,7 +18,7 @@ pub struct Incomplete; /// This assumes that the ClientHello is small and is sent in a single TLS record, which is what all /// reasonable implementations do. (If they were not to, they wouldn't interoperate with picky /// servers.) -pub fn parse_sni(input: &[u8]) -> Result, Incomplete> { +pub fn parse_sni(input: &[u8]) -> Result, Incomplete> { let r = untrusted::Input::from(input).read_all(untrusted::EndOfInput, |input| { let r = extract_sni(input); input.skip_to_end(); // Ignore anything after what we parsed. @@ -34,7 +34,7 @@ pub fn parse_sni(input: &[u8]) -> Result, Incomplete> { None => return Ok(None), }; trace!(?sni, "parse_sni: parsed correctly up to SNI"); - Ok(Some(ServerId(sni))) + Ok(Some(ServerName(sni))) } Ok(None) => { trace!("parse_sni: failed to parse up to SNI"); @@ -211,7 +211,10 @@ mod tests { // The same result will be returned for all longer prefixes. for i in i..input.len() { - assert_eq!(Ok(Some(ServerId(identity.clone()))), parse_sni(&input[..i])) + assert_eq!( + Ok(Some(ServerName(identity.clone()))), + parse_sni(&input[..i]) + ) } } } diff --git a/linkerd/tls/test-util/src/lib.rs b/linkerd/tls/test-util/src/lib.rs index 7ed17257e5..61db47267d 100644 --- a/linkerd/tls/test-util/src/lib.rs +++ b/linkerd/tls/test-util/src/lib.rs @@ -3,6 +3,7 @@ pub struct Entity { pub name: &'static str, + pub tls_name: &'static str, pub trust_anchors: &'static [u8], pub crt: &'static [u8], pub key: &'static [u8], @@ -10,6 +11,7 @@ pub struct Entity { pub static DEFAULT_DEFAULT: Entity = Entity { name: "default.default.serviceaccount.identity.linkerd.cluster.local", + tls_name: "default.default.serviceaccount.identity.linkerd.cluster.local", trust_anchors: include_bytes!("testdata/ca1.pem"), crt: include_bytes!("testdata/default-default-ca1/crt.der"), key: include_bytes!("testdata/default-default-ca1/key.p8"), @@ -17,6 +19,7 @@ pub static DEFAULT_DEFAULT: Entity = Entity { pub static FOO_NS1: Entity = Entity { name: "foo.ns1.serviceaccount.identity.linkerd.cluster.local", + tls_name: "foo.ns1.serviceaccount.identity.linkerd.cluster.local", trust_anchors: include_bytes!("testdata/ca1.pem"), crt: include_bytes!("testdata/foo-ns1-ca1/crt.der"), key: include_bytes!("testdata/foo-ns1-ca1/key.p8"), @@ -24,6 +27,7 @@ pub static FOO_NS1: Entity = Entity { pub static FOO_NS1_CA2: Entity = Entity { name: "foo.ns1.serviceaccount.identity.linkerd.cluster.local", + tls_name: "foo.ns1.serviceaccount.identity.linkerd.cluster.local", trust_anchors: include_bytes!("testdata/ca2.pem"), crt: include_bytes!("testdata/foo-ns1-ca2/crt.der"), key: include_bytes!("testdata/foo-ns1-ca2/key.p8"), @@ -31,6 +35,7 @@ pub static FOO_NS1_CA2: Entity = Entity { pub static BAR_NS1: Entity = Entity { name: "bar.ns1.serviceaccount.identity.linkerd.cluster.local", + tls_name: "bar.ns1.serviceaccount.identity.linkerd.cluster.local", trust_anchors: include_bytes!("testdata/ca1.pem"), crt: include_bytes!("testdata/bar-ns1-ca1/crt.der"), key: include_bytes!("testdata/bar-ns1-ca1/key.p8"),