Skip to content

Commit a8e58d6

Browse files
authored
chore: migrate from rustls-pemfile to rustls-pki-types (#4487)
see rustls/pemfile#61, and [RUSTSEC-2025-0134](https://rustsec.org/advisories/RUSTSEC-2025-0134). see also, olix0r/kubert#432. this branch updates the place where we invoke `rustls_pemfile::certs` inside of `linkerd-meshtls` and `linkerd-app-integration`. see <https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/lib.rs.html#85-93> for a link to the original `certs()` function being replaced here: ```rust // https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/lib.rs.html#85-93 pub fn certs( rd: &mut dyn io::BufRead, ) -> impl Iterator<Item = Result<CertificateDer<'static>, io::Error>> + '_ { iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { Ok(Item::X509Certificate(cert)) => Some(Ok(cert)), Err(err) => Some(Err(err)), _ => None, }) } // https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/pemfile.rs.html /// Extract and decode the next PEM section from `rd`. /// /// - Ok(None) is returned if there is no PEM section read from `rd`. /// - Underlying IO errors produce a `Err(...)` /// - Otherwise each decoded section is returned with a `Ok(Some(Item::...))` /// /// You can use this function to build an iterator, for example: /// `for item in iter::from_fn(|| read_one(rd).transpose()) { ... }` #[cfg(feature = "std")] pub fn read_one(rd: &mut dyn io::BufRead) -> Result<Option<Item>, io::Error> { Item::from_buf(rd).map_err(|err| match err { pem::Error::Io(io) => io, other => Error::from(other).into(), }) } ``` in `rustls_pki_types`, we do this using the `PemObject` trait. this provides facilities to read PEM files through various interfaces, but `pem_slice_iter()` feels like the most natural fit here. this takes an in-memory `&'a [u8]` byte slice representing the contents of a PEM file, and returns an iterator yielding `CertificateDer` deserialized certificates. that closely follows the same interface that `certs()` (above) presented us. <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/pem/trait.PemObject.html#method.pem_slice_iter> --- * refactor: `rustls-pemfile` is a workspace dependency Signed-off-by: katelyn martin <kate@buoyant.io> * refactor: replace `rustls-pemfile` with `rustls-pki-types` NB: code is not update here, only package manifests. Signed-off-by: katelyn martin <kate@buoyant.io> * chore(meshtls): update `rustls_pemfile::certs` call this commit updates the place where we invoke `rustls_pemfile::certs` inside of `linkerd-meshtls`. see <https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/lib.rs.html#85-93> for a link to the original `certs()` function being replaced here: ```rust // https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/lib.rs.html#85-93 pub fn certs( rd: &mut dyn io::BufRead, ) -> impl Iterator<Item = Result<CertificateDer<'static>, io::Error>> + '_ { iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { Ok(Item::X509Certificate(cert)) => Some(Ok(cert)), Err(err) => Some(Err(err)), _ => None, }) } // https://docs.rs/rustls-pemfile/latest/src/rustls_pemfile/pemfile.rs.html /// Extract and decode the next PEM section from `rd`. /// /// - Ok(None) is returned if there is no PEM section read from `rd`. /// - Underlying IO errors produce a `Err(...)` /// - Otherwise each decoded section is returned with a `Ok(Some(Item::...))` /// /// You can use this function to build an iterator, for example: /// `for item in iter::from_fn(|| read_one(rd).transpose()) { ... }` #[cfg(feature = "std")] pub fn read_one(rd: &mut dyn io::BufRead) -> Result<Option<Item>, io::Error> { Item::from_buf(rd).map_err(|err| match err { pem::Error::Io(io) => io, other => Error::from(other).into(), }) } ``` in `rustls_pki_types`, we do this using the `PemObject` trait. this provides facilities to read PEM files through various interfaces, but `pem_slice_iter()` feels like the most natural fit here. this takes an in-memory `&'a [u8]` byte slice representing the contents of a PEM file, and returns an iterator yielding `CertificateDer` deserialized certificates. that closely follows the same interface that `certs()` (above) presented us. <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/pem/trait.PemObject.html#method.pem_slice_iter> Signed-off-by: katelyn martin <kate@buoyant.io> * chore(app/integration): update `rustls_pemfile::certs` calls as with the previous commit, we update calls to `rustls_pemfile::certs`. Signed-off-by: katelyn martin <kate@buoyant.io> * refactor: use `AsRef<[u8]>` for byte slices this polishes some type signatures. `CertificateDer::pem_slice_iter` accepts a slice of bytes. we must, for various reasons such as dealing with our `TestEnv` system, sometimes hold our PEM data in the form of a UTF-8 string. this commit uses `AsRef` to coerce types to a slice of bytes rather than presenting these parameters as `&str` string slices, which is needlessly specific. Signed-off-by: katelyn martin <kate@buoyant.io> * refactor: `rustls-webpki` is a workspace dependency this crate is related to rustls-pki-types, and the feature flags we set on rustls-webpki affects which flags are enabled on rustls-pki-types. defining both as workspace dependencies should help make this relationship slightly more discoverable. Signed-off-by: katelyn martin <kate@buoyant.io> * nit(app/integration): add `rustls-pki-typess/std` flag strictly speaking, we need this feature flag. it's enabled for us already as part of `linkerd-meshtls`'s dependency on the `rustls-webpki/alloc` flag, which `linkerd-app-integration` in turns depends on. that said, it's nice to be extra clear that we depend upon the `std` flag should that change in the future. Signed-off-by: katelyn martin <kate@buoyant.io> --------- Signed-off-by: katelyn martin <kate@buoyant.io>
1 parent 2ed52f2 commit a8e58d6

6 files changed

Lines changed: 32 additions & 37 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ dependencies = [
15121512
"maplit",
15131513
"parking_lot",
15141514
"regex",
1515-
"rustls-pemfile",
1515+
"rustls-pki-types",
15161516
"serde_json",
15171517
"socket2 0.6.2",
15181518
"tokio",
@@ -1980,7 +1980,7 @@ dependencies = [
19801980
"linkerd-tls-test-util",
19811981
"linkerd-tracing",
19821982
"rcgen",
1983-
"rustls-pemfile",
1983+
"rustls-pki-types",
19841984
"rustls-webpki",
19851985
"thiserror",
19861986
"tokio",
@@ -3498,15 +3498,6 @@ dependencies = [
34983498
"zeroize",
34993499
]
35003500

3501-
[[package]]
3502-
name = "rustls-pemfile"
3503-
version = "2.2.0"
3504-
source = "registry+https://github.com/rust-lang/crates.io-index"
3505-
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
3506-
dependencies = [
3507-
"rustls-pki-types",
3508-
]
3509-
35103501
[[package]]
35113502
name = "rustls-pki-types"
35123503
version = "1.14.0"

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ prometheus-client = { version = "0.23" }
110110
prost = { version = "0.14" }
111111
prost-build = { version = "0.14", default-features = false }
112112
prost-types = { version = "0.14" }
113+
rustls-pki-types = { version = "1.14" }
114+
rustls-webpki = { version = "0.103.11", default-features = false }
113115
tokio-rustls = { version = "0.26", default-features = false, features = [
114116
"logging",
115117
] }

linkerd/app/integration/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ linkerd-tracing = { path = "../../tracing" }
3535
maplit = "1"
3636
parking_lot = "0.12"
3737
regex = "1"
38-
rustls-pemfile = "2.2"
38+
rustls-pki-types = { workspace = true, features = ["std"] }
3939
socket2 = "0.6"
4040
tokio = { version = "1", features = ["io-util", "net", "rt", "macros"] }
4141
tokio-rustls = { workspace = true }

linkerd/app/integration/src/identity.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ struct Certificates {
4141
}
4242

4343
impl Certificates {
44-
pub fn load<P>(p: P) -> Result<Certificates, io::Error>
44+
pub fn load<P>(p: P) -> Result<Certificates, crate::Error>
4545
where
4646
P: AsRef<Path>,
4747
{
48-
let f = fs::File::open(p)?;
49-
let mut r = io::BufReader::new(f);
50-
let mut certs = rustls_pemfile::certs(&mut r);
48+
use rustls_pki_types::{pem::PemObject as _, CertificateDer};
49+
50+
let mut certs = CertificateDer::pem_file_iter(p)?;
5151
let leaf = certs
5252
.next()
5353
.expect("no leaf cert in pemfile")
@@ -94,13 +94,14 @@ impl Identity {
9494
}
9595

9696
fn configs(
97-
trust_anchors: &str,
97+
trust_anchors: impl AsRef<[u8]>,
9898
certs: &Certificates,
9999
key: rustls::pki_types::PrivateKeyDer<'static>,
100100
) -> (Arc<rustls::ClientConfig>, Arc<rustls::ServerConfig>) {
101-
use std::io::Cursor;
101+
use rustls_pki_types::{pem::PemObject as _, CertificateDer};
102+
102103
let mut roots = rustls::RootCertStore::empty();
103-
let trust_anchors = rustls_pemfile::certs(&mut Cursor::new(trust_anchors))
104+
let trust_anchors = CertificateDer::pem_slice_iter(trust_anchors.as_ref())
104105
.collect::<Result<Vec<_>, _>>()
105106
.expect("error parsing pemfile");
106107
let (added, skipped) = roots.add_parsable_certificates(trust_anchors);

linkerd/meshtls/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ test-util = ["linkerd-tls-test-util"]
1212

1313
[dependencies]
1414
futures = { version = "0.3", default-features = false }
15-
rustls-pemfile = "2.2"
16-
rustls-webpki = { version = "0.103.11", default-features = false, features = ["std", "aws-lc-rs"] }
15+
rustls-pki-types = { workspace = true, features = ["alloc"] }
16+
rustls-webpki = { workspace = true, features = ["std", "aws-lc-rs"] }
1717
thiserror = "2"
1818
tokio = { version = "1", features = ["macros", "rt", "sync"] }
1919
tokio-rustls = { workspace = true, features = ["aws-lc-rs"] }
@@ -37,4 +37,4 @@ rcgen = { version = "0.14.7", default-features = false, features = ["crypto", "p
3737
linkerd-conditional = { path = "../conditional" }
3838
linkerd-proxy-transport = { path = "../proxy/transport" }
3939
linkerd-tls-test-util = { path = "../tls/test-util" }
40-
linkerd-tracing = { path = "../tracing", features = ["ansi"] }
40+
linkerd-tracing = { path = "../tracing", features = ["ansi"] }

linkerd/meshtls/src/creds.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ pub use self::{receiver::Receiver, store::Store};
66
use linkerd_dns_name as dns;
77
use linkerd_error::Result;
88
use linkerd_identity as id;
9+
use rustls_pki_types::{pem::PemObject as _, CertificateDer};
910
use std::sync::Arc;
1011
use thiserror::Error;
1112
use tokio::sync::watch;
12-
use tokio_rustls::rustls::{self};
13+
use tokio_rustls::rustls;
1314
use tracing::warn;
1415

1516
#[derive(Debug, Error)]
@@ -19,22 +20,22 @@ pub struct InvalidTrustRoots(());
1920
pub fn watch(
2021
local_id: id::Id,
2122
server_name: dns::Name,
22-
roots_pem: &str,
23+
roots_pem: impl AsRef<[u8]>,
2324
) -> Result<(Store, Receiver)> {
2425
let mut roots = rustls::RootCertStore::empty();
25-
let certs = match rustls_pemfile::certs(&mut std::io::Cursor::new(roots_pem))
26-
.collect::<Result<Vec<_>, _>>()
27-
{
28-
Err(error) => {
29-
warn!(%error, "invalid trust anchors file");
30-
return Err(error.into());
31-
}
32-
Ok(certs) if certs.is_empty() => {
33-
warn!("no valid certs in trust anchors file");
34-
return Err("no trust roots in PEM file".into());
35-
}
36-
Ok(certs) => certs,
37-
};
26+
27+
let certs =
28+
match CertificateDer::pem_slice_iter(roots_pem.as_ref()).collect::<Result<Vec<_>, _>>() {
29+
Err(error) => {
30+
warn!(%error, "invalid trust anchors file");
31+
return Err(error.into());
32+
}
33+
Ok(certs) if certs.is_empty() => {
34+
warn!("no valid certs in trust anchors file");
35+
return Err("no trust roots in PEM file".into());
36+
}
37+
Ok(certs) => certs,
38+
};
3839

3940
let (added, skipped) = roots.add_parsable_certificates(certs);
4041
if skipped != 0 {

0 commit comments

Comments
 (0)