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:
@@ -10,4 +10,11 @@ CONTINUUM_UPLINK_URL=wss://api.continuum.local/ws/edge/v1
|
|||||||
CONTINUUM_GO2RTC_BIN=/usr/local/bin/go2rtc
|
CONTINUUM_GO2RTC_BIN=/usr/local/bin/go2rtc
|
||||||
CONTINUUM_GO2RTC_CONFIG=./go2rtc.yaml
|
CONTINUUM_GO2RTC_CONFIG=./go2rtc.yaml
|
||||||
|
|
||||||
|
# Bambu MQTTS certificate trust. Most printers verify fine against the
|
||||||
|
# bundled shared CA (certs/bambu_ca2.pem) and need nothing here. For a
|
||||||
|
# printer that doesn't chain to it (P1P, at least), add "printer_id=path"
|
||||||
|
# entries, comma-separated: CONTINUUM_BAMBU_CA_CERT_OVERRIDES=p1=./certs/p1p.pem
|
||||||
|
CONTINUUM_BAMBU_CA_CERT_OVERRIDES=
|
||||||
|
CONTINUUM_BAMBU_REQUIRE_VALID_CERT=true
|
||||||
|
|
||||||
RUST_LOG=info,continuum_proxy=debug
|
RUST_LOG=info,continuum_proxy=debug
|
||||||
|
|||||||
+10
-4
@@ -14,10 +14,16 @@ instead of fetching/pinning a certificate per printer.
|
|||||||
`Basic Constraints: CA:TRUE`, `Key Usage: Certificate Sign, CRL Sign` — a
|
`Basic Constraints: CA:TRUE`, `Key Usage: Certificate Sign, CRL Sign` — a
|
||||||
genuine root CA, not a per-device leaf certificate. Valid until 2050.
|
genuine root CA, not a per-device leaf certificate. Valid until 2050.
|
||||||
|
|
||||||
The "CA2" name implies there was a CA1 generation before it — some
|
The "CA2" name implies there was a CA1 generation before it, and at least
|
||||||
older printers/firmware may not chain to this root and need their own
|
one current model — P1P — doesn't chain to this root either: BambuStudio's
|
||||||
certificate instead. `CONTINUUM_BAMBU_CA_CERT_PATH` (see `.env.example`)
|
`resources/cert/` only has this one file (plus an unrelated
|
||||||
overrides this default with a specific file for exactly that case.
|
`*.bambulab.com` leaf cert for their cloud API), so there's no second
|
||||||
|
bundled file to grab for P1P. That means it's a genuine per-device
|
||||||
|
certificate, the same "download it from the printer" case as the older
|
||||||
|
units. `CONTINUUM_BAMBU_CA_CERT_OVERRIDES` (see `.env.example`) is a
|
||||||
|
per-printer-id map for exactly this — there's no single fleet-wide
|
||||||
|
override, because different printers can legitimately need different
|
||||||
|
certificates at the same time.
|
||||||
|
|
||||||
If a printer's connection ever fails certificate verification against this
|
If a printer's connection ever fails certificate verification against this
|
||||||
file, re-fetch from the source URL above (BambuStudio ships whatever the
|
file, re-fetch from the source URL above (BambuStudio ships whatever the
|
||||||
|
|||||||
+21
-8
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@@ -10,14 +11,17 @@ pub struct Config {
|
|||||||
pub go2rtc_bin: String,
|
pub go2rtc_bin: String,
|
||||||
pub go2rtc_config: String,
|
pub go2rtc_config: String,
|
||||||
|
|
||||||
/// Overrides the bundled Bambu root CA (see `certs/bambu_ca2.pem`) with
|
/// Per-printer CA certificate overrides, keyed by printer ID. Most
|
||||||
/// a specific certificate file — for a printer whose firmware doesn't
|
/// Bambu printers chain to the bundled shared CA (`certs/bambu_ca2.pem`)
|
||||||
/// chain to that shared CA. Most setups leave this unset.
|
/// and don't need an entry here — but some models (P1P, at least) ship
|
||||||
pub bambu_ca_cert_path: Option<PathBuf>,
|
/// 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
|
/// When `false`, Bambu MQTTS connections skip certificate verification
|
||||||
/// entirely instead of checking against the CA above. `true` (the
|
/// entirely instead of checking against a CA. `true` (the default) is
|
||||||
/// default) is the secure choice; only flip this for a printer whose
|
/// the secure choice; only flip this fleet-wide policy if you have
|
||||||
/// certificate you can't get to verify any other way.
|
/// printers you can't get to verify any other way.
|
||||||
pub bambu_require_valid_cert: bool,
|
pub bambu_require_valid_cert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,12 +35,21 @@ impl Config {
|
|||||||
go2rtc_bin: env_or("CONTINUUM_GO2RTC_BIN", "go2rtc"),
|
go2rtc_bin: env_or("CONTINUUM_GO2RTC_BIN", "go2rtc"),
|
||||||
go2rtc_config: env_or("CONTINUUM_GO2RTC_CONFIG", "./go2rtc.yaml"),
|
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",
|
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> {
|
fn require(key: &str) -> anyhow::Result<String> {
|
||||||
env::var(key).map_err(|_| anyhow::anyhow!("missing required env var {key}"))
|
env::var(key).map_err(|_| anyhow::anyhow!("missing required env var {key}"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,13 @@ pub enum BambuTls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BambuTls {
|
impl BambuTls {
|
||||||
/// Turns `Config`'s two raw settings into one of the three states
|
/// Resolves the trust mode for *one* printer. Call this per printer,
|
||||||
/// above. `require_valid_cert = false` always means `Insecure`,
|
/// passing `config.bambu_ca_cert_overrides.get(printer_id)` — there's
|
||||||
/// regardless of whether a custom cert path was also given.
|
/// no single override for the whole fleet, because not every Bambu
|
||||||
pub fn from_config(cert_path: Option<&Path>, require_valid_cert: bool) -> anyhow::Result<Self> {
|
/// 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 {
|
if !require_valid_cert {
|
||||||
return Ok(BambuTls::Insecure);
|
return Ok(BambuTls::Insecure);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user