currencyrate: vendor root certificates with webpki-roots
What changed, and why it matters
This commit fixes a startup failure in the cln-currencyrate plugin on systems that do not have operating-system root certificates installed. Previously the plugin would crash with a generic 'builder error' because it had no trusted certificates to verify HTTPS connections to currency-rate sources. The fix bundles a known set of root certificates directly into the plugin and improves the error messages shown when configuration or client setup fails. It is a reliability/usability fix rather than a security vulnerability in the traditional sense, though missing certificate validation could in theory have allowed network attackers to impersonate rate sources on affected systems.
Treat as a routine reliability fix. Users running Core Lightning in minimal containers should ensure they upgrade to a version containing this commit so cln-currencyrate can establish HTTPS connections. No immediate incident-response action is required; however, operators relying on currency-rate data should verify the plugin starts cleanly after upgrade.
Security signals we found
Fixes TLS certificate validation failure on systems lacking OS root certificates
Vendors a static root certificate store (webpki-roots)
Improves error-message clarity for proxy, source, and oracle construction failures
Prevents potential silent disablement / opaque failure of a price-oracle plugin
Evidence from the diff
The patch vendors the webpki-roots certificate store and configures the reqwest/rustls HTTP client to use it explicitly instead of relying on the OS root store. It also adds the ‘http2’ feature to reqwest (likely required by the updated rustls/webpki-roots dependency chain) and wraps several error paths in more descriptive messages. The change is defensive: on minimal containers or OS installs without ca-certificates, TLS certificate validation would previously fail at client-build time, causing the plugin to disable itself with an opaque error. Now validation uses the vendored roots, so HTTPS connections to currency-rate APIs succeed.
Changed components
plugins/currencyrate-plugin/src/oracle.rsplugins/currencyrate-plugin/src/main.rsplugins/currencyrate-plugin/Cargo.tomlCargo.lockInspect captured patch +40 / −6
diff --git a/Cargo.lock b/Cargo.lock
index 1b2f97ed..8523209b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -438,6 +438,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
+ "webpki-roots 1.0.8",
]
[[package]]
@@ -2185,7 +2186,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots",
+ "webpki-roots 0.25.4",
"winreg",
]
@@ -2198,6 +2199,7 @@ dependencies = [
"base64 0.22.1",
"bytes",
"futures-core",
+ "h2 0.4.15",
"http 1.4.2",
"http-body 1.0.1",
"http-body-util",
@@ -3584,6 +3586,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+[[package]]
+name = "webpki-roots"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "winapi-util"
version = "0.1.11"
diff --git a/plugins/currencyrate-plugin/Cargo.toml b/plugins/currencyrate-plugin/Cargo.toml
index 974fac7f..29521635 100644
--- a/plugins/currencyrate-plugin/Cargo.toml
+++ b/plugins/currencyrate-plugin/Cargo.toml
@@ -22,6 +22,7 @@ reqwest = { version = "0.13", default-features = false, features = [
"rustls-no-provider",
"socks",
"json",
+ "http2",
] }
rustls = { version = "0.23", default-features = false, features = [
"logging",
@@ -29,6 +30,7 @@ rustls = { version = "0.23", default-features = false, features = [
"std",
"ring",
] }
+webpki-roots = "1"
futures = "0.3"
diff --git a/plugins/currencyrate-plugin/src/main.rs b/plugins/currencyrate-plugin/src/main.rs
index 8057dfd0..c35c59bb 100644
--- a/plugins/currencyrate-plugin/src/main.rs
+++ b/plugins/currencyrate-plugin/src/main.rs
@@ -94,17 +94,27 @@ median from currencyrates results",
let proxy = match check_proxy_config(&plugin).await {
Ok(o) => o,
- Err(e) => return plugin.disable(&e.to_string()).await,
+ Err(e) => {
+ return plugin.disable(&format!("Error in proxy config: {e}")).await;
+ }
};
let sources = match gather_sources(&plugin, proxy.is_some()) {
Ok(o) => o,
- Err(e) => return plugin.disable(&e.to_string()).await,
+ Err(e) => {
+ return plugin
+ .disable(&format!("Error in sources config: {e}"))
+ .await;
+ }
};
let price_oracle = match BtcPriceOracle::new(proxy, sources) {
Ok(o) => o,
- Err(e) => return plugin.disable(&e.to_string()).await,
+ Err(e) => {
+ return plugin
+ .disable(&format!("Error creating price oracle: {e}"))
+ .await;
+ }
};
let plugin_state = PluginState {
diff --git a/plugins/currencyrate-plugin/src/oracle.rs b/plugins/currencyrate-plugin/src/oracle.rs
index c2983dea..19b36110 100644
--- a/plugins/currencyrate-plugin/src/oracle.rs
+++ b/plugins/currencyrate-plugin/src/oracle.rs
@@ -2,6 +2,7 @@ use anyhow::anyhow;
use futures::future::join_all;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use reqwest::{Client, Proxy};
+use rustls::ClientConfig;
use serde_json::Value;
use std::cmp::Reverse;
use std::collections::HashMap;
@@ -254,9 +255,17 @@ impl BtcPriceOracle {
let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, HeaderValue::from_static("cln-currencyrate"));
+ let root_store = rustls::RootCertStore {
+ roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
+ };
+
+ let tls = ClientConfig::builder()
+ .with_root_certificates(root_store)
+ .with_no_client_auth();
+
let mut client = Client::builder()
.default_headers(headers)
- .tls_backend_rustls()
+ .tls_backend_preconfigured(tls)
.timeout(SOURCE_TIMEOUT_SECS)
.pool_max_idle_per_host(5);
@@ -267,7 +276,9 @@ impl BtcPriceOracle {
client = client.proxy(proxy);
}
- let client = client.build()?;
+ let client = client
+ .build()
+ .map_err(|e| anyhow!("HTTP client failed to build: {:?}", e.source()))?;
let mut map = HashMap::new();
for s in sources {
Why this scored 25/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.