Skip to content

Commit

Permalink
chore(deps): Upgrade tokio-rustls to 0.26
Browse files Browse the repository at this point in the history
This bumps rustls itself from 0.21 to 0.23, which comes with a few breaking API changes. Most of these are limited to types being moved or renamed, or how the certificate verifiers are constructed.

Signed-off-by: Scott Fleener <[email protected]>
  • Loading branch information
sfleen committed Dec 4, 2024
1 parent 4d9df4c commit cc5ff36
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 231 deletions.
269 changes: 142 additions & 127 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ lto = true

[workspace.dependencies]
linkerd2-proxy-api = "0.15.0"
tokio-rustls = { version = "0.26", default_features = false, features = ["ring", "logging"] }
# linkerd2-proxy-api = { git = "https://github.com/linkerd/linkerd2-proxy-api.git", branch = "main" }
4 changes: 2 additions & 2 deletions linkerd/app/integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ regex = "1"
socket2 = "0.5"
tokio = { version = "1", features = ["io-util", "net", "rt", "macros"] }
tokio-stream = { version = "0.1", features = ["sync"] }
tokio-rustls = "0.24"
rustls-pemfile = "1.0"
tokio-rustls.workspace = true
rustls-pemfile = "2.2"
tower = { version = "0.4", default-features = false }
tonic = { version = "0.10", features = ["transport"], default-features = false }
tracing = "0.1"
Expand Down
10 changes: 5 additions & 5 deletions linkerd/app/integration/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use super::*;
use linkerd_app_core::proxy::http::TracingExecutor;
use parking_lot::Mutex;
use std::io;
use tokio::net::TcpStream;
use tokio::task::JoinHandle;
use tokio::{net::TcpStream, task::JoinHandle};
use tokio_rustls::rustls::{self, ClientConfig};
use tracing::info_span;

Expand All @@ -15,12 +14,13 @@ type Sender = mpsc::UnboundedSender<(Request, oneshot::Sender<Result<Response, C
#[derive(Clone)]
pub struct TlsConfig {
client_config: Arc<ClientConfig>,
name: rustls::ServerName,
name: rustls::pki_types::ServerName<'static>,
}

impl TlsConfig {
pub fn new(client_config: Arc<ClientConfig>, name: &str) -> Self {
let name = rustls::ServerName::try_from(name).expect("name must be a valid DNS name");
pub fn new(client_config: Arc<ClientConfig>, name: &'static str) -> Self {
let name =
rustls::pki_types::ServerName::try_from(name).expect("name must be a valid DNS name");
TlsConfig {
client_config,
name,
Expand Down
47 changes: 28 additions & 19 deletions linkerd/app/integration/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{
};

use linkerd2_proxy_api::identity as pb;
use tokio_rustls::rustls;
use tokio_rustls::rustls::{self, pki_types::CertificateDer, server::WebPkiClientVerifier};
use tonic as grpc;

pub struct Identity {
Expand Down Expand Up @@ -36,7 +36,7 @@ type Certify = Box<

static TLS_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];
static TLS_SUPPORTED_CIPHERSUITES: &[rustls::SupportedCipherSuite] =
&[rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256];
&[rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256];

struct Certificates {
pub leaf: Vec<u8>,
Expand All @@ -62,11 +62,14 @@ impl Certificates {
})
}

pub fn chain(&self) -> Vec<rustls::Certificate> {
pub fn chain(&self) -> Vec<rustls::pki_types::CertificateDer<'static>> {
let mut chain = Vec::with_capacity(self.intermediates.len() + 1);
chain.push(self.leaf.clone());
chain.extend(self.intermediates.clone());
chain.into_iter().map(rustls::Certificate).collect()
chain
.into_iter()
.map(rustls::pki_types::CertificateDer::from)
.collect()
}

pub fn response(&self) -> pb::CertifyResponse {
Expand All @@ -79,43 +82,49 @@ impl Certificates {
}

impl Identity {
fn load_key<P>(p: P) -> rustls::PrivateKey
fn load_key<P>(p: P) -> rustls::pki_types::PrivateKeyDer<'static>
where
P: AsRef<Path>,
{
let p8 = fs::read(&p).expect("read key");
rustls::PrivateKey(p8)
rustls::pki_types::PrivateKeyDer::try_from(p8).expect("decode key")
}

fn configs(
trust_anchors: &str,
certs: &Certificates,
key: rustls::PrivateKey,
key: rustls::pki_types::PrivateKeyDer<'static>,
) -> (Arc<rustls::ClientConfig>, Arc<rustls::ServerConfig>) {
use std::io::Cursor;
let mut roots = rustls::RootCertStore::empty();
let trust_anchors =
rustls_pemfile::certs(&mut Cursor::new(trust_anchors)).expect("error parsing pemfile");
let (added, skipped) = roots.add_parsable_certificates(&trust_anchors[..]);
let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors))
.expect("error parsing pemfile")

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust

error[E0599]: no method named `expect` found for opaque type `impl std::iter::Iterator<Item = std::result::Result<tokio_rustls::rustls::pki_types::CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust

error[E0599]: no method named `expect` found for opaque type `impl std::iter::Iterator<Item = std::result::Result<tokio_rustls::rustls::pki_types::CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust

error[E0599]: no method named `expect` found for opaque type `impl std::iter::Iterator<Item = std::result::Result<tokio_rustls::rustls::pki_types::CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust

error[E0599]: no method named `expect` found for opaque type `impl std::iter::Iterator<Item = std::result::Result<tokio_rustls::rustls::pki_types::CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust-crates (linkerd-app-integration)

error[E0599]: no method named `expect` found for opaque type `impl Iterator<Item = Result<CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust-crates (linkerd-app-integration)

error[E0599]: no method named `expect` found for opaque type `impl Iterator<Item = Result<CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust-crates (linkerd-app-integration)

error[E0599]: no method named `expect` found for opaque type `impl Iterator<Item = Result<CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |

Check failure on line 101 in linkerd/app/integration/src/identity.rs

View workflow job for this annotation

GitHub Actions / rust-crates (linkerd-app-integration)

error[E0599]: no method named `expect` found for opaque type `impl Iterator<Item = Result<CertificateDer<'static>, std::io::Error>> + '_` in the current scope --> linkerd/app/integration/src/identity.rs:101:14 | 100 | let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors)) | _____________________________- 101 | | .expect("error parsing pemfile") | | -^^^^^^ method not found in `impl Iterator<Item = Result<CertificateDer<'static>, Error>>` | |_____________| |
.into_iter()
.map(CertificateDer::from);
let (added, skipped) = roots.add_parsable_certificates(trust_anchors);
assert_ne!(added, 0, "trust anchors must include at least one cert");
assert_eq!(skipped, 0, "no certs in pemfile should be invalid");

let client_config = rustls::ClientConfig::builder()
.with_cipher_suites(TLS_SUPPORTED_CIPHERSUITES)
.with_safe_default_kx_groups()
let mut provider = rustls::crypto::ring::default_provider();
provider.cipher_suites = TLS_SUPPORTED_CIPHERSUITES.to_vec();
let provider = Arc::new(provider);

let client_config = rustls::ClientConfig::builder_with_provider(provider.clone())
.with_protocol_versions(TLS_VERSIONS)
.expect("client config must be valid")
.with_root_certificates(roots.clone())
.with_no_client_auth();

let server_config = rustls::ServerConfig::builder()
.with_cipher_suites(TLS_SUPPORTED_CIPHERSUITES)
.with_safe_default_kx_groups()
let client_cert_verifier =
WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider.clone())
.allow_unauthenticated()
.build()
.expect("server verifier must be valid");

let server_config = rustls::ServerConfig::builder_with_provider(provider)
.with_protocol_versions(TLS_VERSIONS)
.expect("server config must be valid")
.with_client_cert_verifier(Arc::new(
rustls::server::AllowAnyAnonymousOrAuthenticatedClient::new(roots),
))
.with_client_cert_verifier(client_cert_verifier)
.with_single_cert(certs.chain(), key)
.unwrap();

Expand Down
2 changes: 1 addition & 1 deletion linkerd/app/outbound/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ futures-util = "0.3"
http-body = "0.4"
hyper = { version = "0.14", features = ["deprecated", "http1", "http2"] }
tokio = { version = "1", features = ["macros", "sync", "time"] }
tokio-rustls = "0.24"
tokio-rustls.workspace = true
tokio-test = "0.4"
tower-test = "0.4"

Expand Down
33 changes: 28 additions & 5 deletions linkerd/app/outbound/src/tls/logical/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::{
time::Duration,
};
use tokio::sync::watch;
use tokio_rustls::rustls::pki_types::DnsName;

mod basic;

Expand Down Expand Up @@ -171,28 +172,37 @@ fn generate_client_hello(sni: &str) -> Vec<u8> {
use tokio_rustls::rustls::{
internal::msgs::{
base::Payload,
codec::{Codec, Reader},
enums::Compression,
handshake::{
ClientExtension, ClientHelloPayload, HandshakeMessagePayload, HandshakePayload,
Random, SessionId,
Random, ServerName, SessionId,
},
message::{MessagePayload, PlainMessage},
},
server::DnsName,
CipherSuite, ContentType, HandshakeType, ProtocolVersion,
};

let sni = DnsName::try_from(sni.to_string()).unwrap();
let sni = trim_hostname_trailing_dot_for_sni(&sni);

let mut server_name_bytes = vec![];
0u8.encode(&mut server_name_bytes); // encode the type first
(sni.as_ref().len() as u16).encode(&mut server_name_bytes); // then the length as u16
server_name_bytes.extend_from_slice(sni.as_ref().as_bytes()); // then the server name itself

let server_name =
ServerName::read(&mut Reader::init(&server_name_bytes)).expect("Server name is valid");

let hs_payload = HandshakeMessagePayload {
typ: HandshakeType::ClientHello,
payload: HandshakePayload::ClientHello(ClientHelloPayload {
client_version: ProtocolVersion::TLSv1_2,
random: Random::from([0; 32]),
session_id: SessionId::empty(),
session_id: SessionId::read(&mut Reader::init(&[0])).unwrap(),
cipher_suites: vec![CipherSuite::TLS_NULL_WITH_NULL_NULL],
compression_methods: vec![Compression::Null],
extensions: vec![ClientExtension::make_sni(sni.borrow())],
extensions: vec![ClientExtension::ServerName(vec![server_name])],
}),
};

Expand All @@ -202,8 +212,21 @@ fn generate_client_hello(sni: &str) -> Vec<u8> {
let message = PlainMessage {
typ: ContentType::Handshake,
version: ProtocolVersion::TLSv1_2,
payload: Payload(hs_payload_bytes),
payload: Payload::Owned(hs_payload_bytes),
};

message.into_unencrypted_opaque().encode()
}

fn trim_hostname_trailing_dot_for_sni(dns_name: &DnsName<'_>) -> DnsName<'static> {
let dns_name_str = dns_name.as_ref();

// RFC6066: "The hostname is represented as a byte string using
// ASCII encoding without a trailing dot"
if dns_name_str.ends_with('.') {
let trimmed = &dns_name_str[0..dns_name_str.len() - 1];
DnsName::try_from(trimmed).unwrap().to_owned()
} else {
dns_name.to_owned()
}
}
6 changes: 3 additions & 3 deletions linkerd/meshtls/rustls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ test-util = ["linkerd-tls-test-util"]
[dependencies]
futures = { version = "0.3", default-features = false }
ring = { version = "0.17", features = ["std"] }
rustls-pemfile = "1.0"
rustls-webpki = { version = "0.101.5", features = ["std"] }
rustls-pemfile = "2.2"
rustls-webpki = { version = "0.102.8", features = ["std"] }
thiserror = "1"
tokio = { version = "1", features = ["macros", "rt", "sync"] }
tokio-rustls = { version = "0.24", features = ["dangerous_configuration"] }
tokio-rustls.workspace = true
tracing = "0.1"

linkerd-dns-name = { path = "../../dns/name" }
Expand Down
13 changes: 7 additions & 6 deletions linkerd/meshtls/rustls/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use linkerd_stack::{NewService, Service};
use linkerd_tls::{client::AlpnProtocols, ClientTls, NegotiatedProtocolRef};
use std::{convert::TryFrom, pin::Pin, sync::Arc, task::Context};
use tokio::sync::watch;
use tokio_rustls::rustls::{self, ClientConfig};
use tokio_rustls::rustls::{self, pki_types::CertificateDer, ClientConfig};

/// A `NewService` that produces `Connect` services from a dynamic TLS configuration.
#[derive(Clone)]
Expand All @@ -18,7 +18,7 @@ pub struct NewClient {
#[derive(Clone)]
pub struct Connect {
server_id: id::Id,
server_name: rustls::ServerName,
server_name: rustls::pki_types::ServerName<'static>,
config: Arc<ClientConfig>,
}

Expand Down Expand Up @@ -68,8 +68,9 @@ impl Connect {
}
};

let server_name = rustls::ServerName::try_from(client_tls.server_name.as_str())
.expect("identity must be a valid DNS name");
let server_name =
rustls::pki_types::ServerName::try_from(client_tls.server_name.to_string())
.expect("identity must be a valid DNS name");

Self {
server_id: client_tls.server_id.into(),
Expand All @@ -79,7 +80,7 @@ impl Connect {
}
}

fn extract_cert(c: &rustls::ClientConnection) -> io::Result<&rustls::Certificate> {
fn extract_cert(c: &rustls::ClientConnection) -> io::Result<&CertificateDer<'_>> {
match c.peer_certificates().and_then(|certs| certs.first()) {
Some(leaf_cert) => io::Result::Ok(leaf_cert),
None => Err(io::Error::new(io::ErrorKind::Other, "missing tls end cert")),
Expand Down Expand Up @@ -113,7 +114,7 @@ where
let s = s?;
let (_, conn) = s.get_ref();
let end_cert = extract_cert(conn)?;
verifier::verify_id(&end_cert.0, &server_id)?;
verifier::verify_id(end_cert, &server_id)?;
Ok(ClientIo(s))
}),
)
Expand Down
31 changes: 17 additions & 14 deletions linkerd/meshtls/rustls/src/creds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,22 @@ pub fn watch(
roots_pem: &str,
) -> Result<(Store, Receiver)> {
let mut roots = rustls::RootCertStore::empty();
let certs = match rustls_pemfile::certs(&mut std::io::Cursor::new(roots_pem)) {
Err(error) => {
warn!(%error, "invalid trust anchors file");
return Err(error.into());
}
Ok(certs) if certs.is_empty() => {
warn!("no valid certs in trust anchors file");
return Err("no trust roots in PEM file".into());
}
Ok(certs) => certs,
};
let mut certs = vec![];
for cert in rustls_pemfile::certs(&mut std::io::Cursor::new(roots_pem)) {
match cert {
Err(error) => {
warn!(%error, "invalid trust anchors file");
return Err(error.into());
}
Ok(cert) => certs.push(cert),
};
}
if certs.is_empty() {
warn!("no valid certs in trust anchors file");
return Err("no trust roots in PEM file".into());
}

let (added, skipped) = roots.add_parsable_certificates(&certs[..]);
let (added, skipped) = roots.add_parsable_certificates(certs);
if skipped != 0 {
warn!("Skipped {} invalid trust anchors", skipped);
}
Expand Down Expand Up @@ -104,7 +107,7 @@ pub fn default_for_test() -> (Store, Receiver) {
}

mod params {
use tokio_rustls::rustls;
use tokio_rustls::rustls::{self};

// These must be kept in sync:
pub static SIGNATURE_ALG_RING_SIGNING: &ring::signature::EcdsaSigningAlgorithm =
Expand All @@ -115,5 +118,5 @@ mod params {
rustls::SignatureAlgorithm::ECDSA;
pub static TLS_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];
pub static TLS_SUPPORTED_CIPHERSUITES: &[rustls::SupportedCipherSuite] =
&[rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256];
&[rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256];
}
22 changes: 14 additions & 8 deletions linkerd/meshtls/rustls/src/creds/receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ mod tests {
/// incoming handshakes, but that doesn't matter for these tests, where we
/// don't actually do any TLS.
fn empty_server_config() -> rustls::ServerConfig {
rustls::ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(Arc::new(rustls::server::NoClientAuth))
.with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new()))
rustls::ServerConfig::builder_with_provider(Arc::new(
rustls::crypto::ring::default_provider(),
))
.with_protocol_versions(rustls::ALL_VERSIONS)
.expect("client config must be valid")
.with_client_cert_verifier(Arc::new(rustls::server::NoClientAuth))
.with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new()))
}

/// Returns the simplest default rustls client config.
Expand All @@ -82,10 +85,13 @@ mod tests {
/// it doesn't trust any root certificates. However, that doesn't actually
/// matter for these tests, which don't actually do TLS.
fn empty_client_config() -> rustls::ClientConfig {
rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(rustls::RootCertStore::empty())
.with_no_client_auth()
rustls::ClientConfig::builder_with_provider(Arc::new(
rustls::crypto::ring::default_provider(),
))
.with_protocol_versions(rustls::ALL_VERSIONS)
.expect("client config must be valid")
.with_root_certificates(rustls::RootCertStore::empty())
.with_no_client_auth()
}

#[tokio::test]
Expand Down
Loading

0 comments on commit cc5ff36

Please sign in to comment.