···2727 "search.heap_bytes",
2828 "knot.allow_private",
2929 "knot.require_https",
3030+ "knot.extra_ca_cert",
3031 "log.format",
3132 "log.filter",
3233];
···4950 "BOBBIN_SEARCH_HEAP_BYTES",
5051 "BOBBIN_KNOT_ALLOW_PRIVATE",
5152 "BOBBIN_KNOT_REQUIRE_HTTPS",
5353+ "BOBBIN_KNOT_EXTRA_CA_CERT",
5254 "BOBBIN_LOG_FORMAT",
5355 "BOBBIN_LOG",
5456];
···223225 /// knot for development.
224226 #[config(env = "BOBBIN_KNOT_REQUIRE_HTTPS", default = true)]
225227 pub require_https: bool,
228228+229229+ /// Path to an extra PEM certificate to trust as a knot CA, in addition to
230230+ /// the bundled webpki roots. Set for local dev so a self-signed/dev CA is
231231+ /// trusted without disabling certificate verification. Empty disables this.
232232+ #[config(env = "BOBBIN_KNOT_EXTRA_CA_CERT", default = "")]
233233+ pub extra_ca_cert: String,
226234}
227235228236#[derive(Debug, Config)]
···6565 }
6666}
67676868+#[derive(Debug, Error)]
6969+pub enum ExtraCaError {
7070+ #[error("read {path}: {source}")]
7171+ Read {
7272+ path: String,
7373+ source: std::io::Error,
7474+ },
7575+ #[error("parse PEM certificate at {path}: {source}")]
7676+ Parse { path: String, source: reqwest::Error },
7777+}
7878+7979+/// Loads a PEM certificate from `path` (if given) so it can be added to a
8080+/// `reqwest::ClientBuilder` via `add_root_certificate`, extending the bundled
8181+/// webpki roots rather than replacing them. Used to trust a local dev CA -
8282+/// `rustls-tls-webpki-roots` ignores the OS trust store, so mounting a CA
8383+/// into `/etc/ssl/certs` has no effect on reqwest without this.
8484+pub fn load_extra_ca_cert(path: &std::path::Path) -> Result<reqwest::Certificate, ExtraCaError> {
8585+ let pem = std::fs::read(path).map_err(|source| ExtraCaError::Read {
8686+ path: path.display().to_string(),
8787+ source,
8888+ })?;
8989+ reqwest::Certificate::from_pem(&pem).map_err(|source| ExtraCaError::Parse {
9090+ path: path.display().to_string(),
9191+ source,
9292+ })
9393+}
9494+6895impl HttpTransport for ReqwestHttp {
6996 fn execute(&self, request: HttpRequest) -> HttpResponseFuture {
7097 let client = self.client.clone();