Bambu CA trust is per-printer, not per-fleet — P1P needs its own cert

Checked BambuStudio's resources/cert/ directly: it ships exactly one
LAN-mode CA (the bundled bambu_ca2.pem) plus an unrelated cloud-API leaf
cert — no second file to bundle for P1P. So a P1P's certificate is a
genuine per-device case, same as older units' 'download it from the
printer' flow, and a single Config-wide override path can't express 'most
printers use the bundled CA, but this one doesn't'.

Config.bambu_ca_cert_path (Option<PathBuf>) -> bambu_ca_cert_overrides
(HashMap<printer_id, PathBuf>), parsed from a comma-separated
CONTINUUM_BAMBU_CA_CERT_OVERRIDES env var. BambuTls::from_config renamed
to ::resolve to make clear it's called once per printer with that
printer's own override, not once for the whole config.

Verified with cargo check --all-targets and a full run of
cargo run --example printer_polymorphism.
This commit is contained in:
2026-08-28 20:42:41 +00:00
parent 9eab5e5a04
commit 72ba1e1cdc
4 changed files with 45 additions and 16 deletions
+21 -8
View File
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
@@ -10,14 +11,17 @@ pub struct Config {
pub go2rtc_bin: String,
pub go2rtc_config: String,
/// Overrides the bundled Bambu root CA (see `certs/bambu_ca2.pem`) with
/// a specific certificate file — for a printer whose firmware doesn't
/// chain to that shared CA. Most setups leave this unset.
pub bambu_ca_cert_path: Option<PathBuf>,
/// Per-printer CA certificate overrides, keyed by printer ID. Most
/// Bambu printers chain to the bundled shared CA (`certs/bambu_ca2.pem`)
/// and don't need an entry here — but some models (P1P, at least) ship
/// their own certificate instead of one signed by that shared CA, with
/// no single file covering all of them. Look up a printer's own entry
/// (if any) when constructing it; there's no fleet-wide "the" override.
pub bambu_ca_cert_overrides: HashMap<String, PathBuf>,
/// When `false`, Bambu MQTTS connections skip certificate verification
/// entirely instead of checking against the CA above. `true` (the
/// default) is the secure choice; only flip this for a printer whose
/// certificate you can't get to verify any other way.
/// entirely instead of checking against a CA. `true` (the default) is
/// the secure choice; only flip this fleet-wide policy if you have
/// printers you can't get to verify any other way.
pub bambu_require_valid_cert: bool,
}
@@ -31,12 +35,21 @@ impl Config {
go2rtc_bin: env_or("CONTINUUM_GO2RTC_BIN", "go2rtc"),
go2rtc_config: env_or("CONTINUUM_GO2RTC_CONFIG", "./go2rtc.yaml"),
bambu_ca_cert_path: env::var("CONTINUUM_BAMBU_CA_CERT_PATH").ok().map(PathBuf::from),
bambu_ca_cert_overrides: parse_cert_overrides(&env_or("CONTINUUM_BAMBU_CA_CERT_OVERRIDES", "")),
bambu_require_valid_cert: env_or("CONTINUUM_BAMBU_REQUIRE_VALID_CERT", "true") == "true",
})
}
}
/// Parses `"printer_id=path,printer_id2=path2"` into a lookup map. Empty
/// input (the common case — no overrides needed) yields an empty map.
fn parse_cert_overrides(raw: &str) -> HashMap<String, PathBuf> {
raw.split(',')
.filter_map(|entry| entry.split_once('='))
.map(|(id, path)| (id.trim().to_string(), PathBuf::from(path.trim())))
.collect()
}
fn require(key: &str) -> anyhow::Result<String> {
env::var(key).map_err(|_| anyhow::anyhow!("missing required env var {key}"))
}
+7 -4
View File
@@ -24,10 +24,13 @@ pub enum BambuTls {
}
impl BambuTls {
/// Turns `Config`'s two raw settings into one of the three states
/// above. `require_valid_cert = false` always means `Insecure`,
/// regardless of whether a custom cert path was also given.
pub fn from_config(cert_path: Option<&Path>, require_valid_cert: bool) -> anyhow::Result<Self> {
/// Resolves the trust mode for *one* printer. Call this per printer,
/// passing `config.bambu_ca_cert_overrides.get(printer_id)` — there's
/// no single override for the whole fleet, because not every Bambu
/// model chains to the same CA (P1P doesn't). `require_valid_cert =
/// false` always means `Insecure`, regardless of whether that printer
/// also has an override entry.
pub fn resolve(cert_path: Option<&Path>, require_valid_cert: bool) -> anyhow::Result<Self> {
if !require_valid_cert {
return Ok(BambuTls::Insecure);
}