diff --git a/Cargo.lock b/Cargo.lock index 988f5dab2..332821eb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,15 +381,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.25" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbc37d37da9e5bce8173f3a41b71d9bf3c674deebbaceacd0ebdabde76efb03" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", - "winapi", + "wasm-bindgen", + "windows-targets 0.52.0", ] [[package]] @@ -898,7 +900,7 @@ dependencies = [ "openssl", "openssl-kdf", "paste", - "pem", + "pem 2.0.1", "serde", "serde_bytes", "serde_cbor", @@ -972,11 +974,13 @@ dependencies = [ "fdo-store", "fdo-util", "hex", + "hyper", "log", "openssl", "serde", "serde_yaml", "thiserror", + "tls-listener", "tokio", "warp", ] @@ -1552,6 +1556,7 @@ name = "integration-tests" version = "0.4.13" dependencies = [ "anyhow", + "chrono", "fdo-data-formats", "fdo-util", "hex", @@ -1559,7 +1564,7 @@ dependencies = [ "libc", "openssl", "paste", - "pem", + "pem 3.0.3", "pretty_assertions", "regex", "reqwest", @@ -2058,6 +2063,16 @@ dependencies = [ "serde", ] +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64 0.21.2", + "serde", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -2362,9 +2377,9 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64 0.21.2", "bytes", @@ -2387,6 +2402,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2856,6 +2872,27 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "target-lexicon" version = "0.12.7" @@ -2983,6 +3020,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls-listener" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81294c017957a1a69794f506723519255879e15a870507faf45dfed288b763dd" +dependencies = [ + "futures-util", + "hyper", + "openssl", + "pin-project-lite", + "thiserror", + "tokio", + "tokio-openssl", +] + [[package]] name = "tokio" version = "1.28.2" @@ -3023,6 +3075,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-openssl" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a" +dependencies = [ + "futures-util", + "openssl", + "openssl-sys", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -3557,6 +3621,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3569,6 +3648,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3581,6 +3666,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3593,6 +3684,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3605,6 +3702,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3617,6 +3720,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3629,6 +3738,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3641,13 +3756,20 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if 1.0.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/admin-tool/src/aio/configure.rs b/admin-tool/src/aio/configure.rs index e26612822..f2839d2f7 100644 --- a/admin-tool/src/aio/configure.rs +++ b/admin-tool/src/aio/configure.rs @@ -23,7 +23,9 @@ pub(super) struct Configuration { pub listen_ip_address: String, #[clap(long, default_value_t = 8080)] - pub listen_port_manufacturing_server: u16, + pub listen_port_http_manufacturing_server: u16, + #[clap(long, default_value_t = 8084)] + pub listen_port_https_manufacturing_server: u16, #[clap(long, default_value_t = 8081)] pub listen_port_owner_onboarding_server: u16, #[clap(long, default_value_t = 8082)] @@ -77,7 +79,8 @@ impl Default for Configuration { cert_country: String::from("US"), listen_ip_address: String::from("0.0.0.0"), - listen_port_manufacturing_server: 8080, + listen_port_http_manufacturing_server: 8080, + listen_port_https_manufacturing_server: 8084, listen_port_owner_onboarding_server: 8081, listen_port_rendezvous_server: 8082, listen_port_serviceinfo_api_server: 8083, @@ -253,7 +256,8 @@ fn generate_configs(aio_dir: &Path, config_args: &Configuration) -> Result<(), E path: aio_dir.join("stores").join("manufacturing_sessions"), }, - bind: get_bind(config_args.listen_port_manufacturing_server)?, + bind_http: get_bind(config_args.listen_port_http_manufacturing_server)?, + bind_https: get_bind(config_args.listen_port_https_manufacturing_server)?, ownership_voucher_store_driver: StoreConfig::Directory { path: aio_dir.join("stores").join(if config_args.separate_manufacturing_and_owner_voucher_store { @@ -293,7 +297,9 @@ fn generate_configs(aio_dir: &Path, config_args: &Configuration) -> Result<(), E device_cert_ca_private_key: AbsolutePathBuf::new(aio_dir.join("keys").join("device_ca_key.der")).unwrap(), device_cert_ca_chain: AbsolutePathBuf::new(aio_dir.join("keys").join("device_ca_cert.pem")).unwrap(), owner_cert_path: Some(AbsolutePathBuf::new(aio_dir.join("keys").join("owner_cert.pem")).unwrap()), - } + manufacturing_server_https_cert: AbsolutePathBuf::new(aio_dir.join("keys").join("manufacturing_server_https_cert.crt")).unwrap(), + manufacturing_server_https_key: AbsolutePathBuf::new(aio_dir.join("keys").join("manufacturing_server_https_key.key")).unwrap(), + }, }; write_config( aio_dir, diff --git a/admin-tool/src/aio/device.rs b/admin-tool/src/aio/device.rs index e5d751d93..48231b43e 100644 --- a/admin-tool/src/aio/device.rs +++ b/admin-tool/src/aio/device.rs @@ -95,7 +95,7 @@ async fn manufacture_device( "MANUFACTURING_SERVER_URL", format!( "http://localhost:{}", //DevSkim: ignore DS137138 - configuration.listen_port_manufacturing_server + configuration.listen_port_http_manufacturing_server ), ) .env("DI_MFG_STRING_TYPE", "serialnumber") diff --git a/admin-tool/src/aio/execute.rs b/admin-tool/src/aio/execute.rs index b8ca30eb2..cc02cc648 100644 --- a/admin-tool/src/aio/execute.rs +++ b/admin-tool/src/aio/execute.rs @@ -202,7 +202,7 @@ impl ChildBinary { fn port(&self, config: &Configuration) -> u16 { match self { - ChildBinary::ManufacturingServer => config.listen_port_manufacturing_server, + ChildBinary::ManufacturingServer => config.listen_port_http_manufacturing_server, ChildBinary::OwnerOnboardingServer => config.listen_port_owner_onboarding_server, ChildBinary::RendezvousServer => config.listen_port_rendezvous_server, ChildBinary::ServiceInfoApiServer => config.listen_port_serviceinfo_api_server, diff --git a/client-linuxapp/src/main.rs b/client-linuxapp/src/main.rs index 31baca81f..046be24e6 100644 --- a/client-linuxapp/src/main.rs +++ b/client-linuxapp/src/main.rs @@ -149,7 +149,7 @@ async fn get_client_list(rv_entry: &RendezvousInterpretedDirective) -> Result Result { log::info!("Performing TO2 protocol, URL: {:?}", url); - let mut client = fdo_http_wrapper::client::ServiceClient::new(ProtocolVersion::Version1_1, url); + let mut client = + fdo_http_wrapper::client::ServiceClient::new(ProtocolVersion::Version1_1, url)?; let nonce5 = match get_nonce(MessageType::TO1RVRedirect).await { Ok(nonce5) => nonce5, diff --git a/http-wrapper/Cargo.toml b/http-wrapper/Cargo.toml index 1b3355dac..207076e10 100644 --- a/http-wrapper/Cargo.toml +++ b/http-wrapper/Cargo.toml @@ -29,7 +29,7 @@ warp-sessions = { version = "1.0", optional = true } time = "0.3" # Client-side -reqwest = { version = "0.11", optional = true, features = ["native-tls", "json"] } +reqwest = { version = "0.11.21", optional = true, features = ["native-tls", "json", "__tls"] } url = { version = "2", optional = true } [features] diff --git a/http-wrapper/src/client.rs b/http-wrapper/src/client.rs index e807a8c2d..6b27d3cff 100644 --- a/http-wrapper/src/client.rs +++ b/http-wrapper/src/client.rs @@ -12,6 +12,8 @@ use fdo_data_formats::{ use crate::EncryptionKeys; +use std::env; + #[derive(Debug, Error)] #[non_exhaustive] pub enum Error { @@ -149,16 +151,26 @@ pub struct ServiceClient { } impl ServiceClient { - pub fn new(protocol_version: ProtocolVersion, base_url: &str) -> Self { - ServiceClient { + pub fn new(protocol_version: ProtocolVersion, base_url: &str) -> RequestResult { + let mut client_builder = reqwest::Client::builder(); + + if env::var("DEV_ENVIRONMENT").is_ok() { + log::debug!("DEV_ENVIRONMENT is set"); + client_builder = client_builder.danger_accept_invalid_certs(true); + } + + Ok(ServiceClient { protocol_version, base_url: base_url.trim_end_matches('/').to_string(), - client: reqwest::Client::new(), + client: client_builder + .tls_info(true) + // .danger_accept_invalid_certs(true) + .build()?, authorization_token: None, encryption_keys: EncryptionKeys::unencrypted(), last_message_type: None, non_interoperable_kdf_required: None, - } + }) } pub fn non_interoperable_kdf_required(&self) -> Option { @@ -219,6 +231,7 @@ impl ServiceClient { OM::message_type() as u8 ); + log::debug!("url: {}", url); let mut req = self .client .post(&url) diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index cb02138fc..2cdbb0420 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -32,7 +32,8 @@ serde_cbor = "0.11" serde_json = "1.0" pretty_assertions = "1.0.0" paste = "1.0" -pem = "2.0" +pem = "3.0.3" +chrono = "0.4.33" fdo-data-formats = { path = "../data-formats" } fdo-util = { path = "../util" } diff --git a/integration-tests/templates/manufacturing-server.yml.j2 b/integration-tests/templates/manufacturing-server.yml.j2 index 6eaa2e5fa..5cd640a53 100644 --- a/integration-tests/templates/manufacturing-server.yml.j2 +++ b/integration-tests/templates/manufacturing-server.yml.j2 @@ -8,7 +8,8 @@ ownership_voucher_store_driver: public_key_store_driver: Directory: path: {{ config_dir }}/keys/ -bind: {{ bind }} +bind_http: {{ bind }} +bind_https: {{ bind_https }} rendezvous_info: - dns: localhost device_port: 8082 @@ -33,3 +34,5 @@ manufacturing: owner_cert_path: {{ keys_path }}/owner_cert.pem device_cert_ca_private_key: {{ keys_path }}/device_ca_key.der device_cert_ca_chain: {{ keys_path }}/device_ca_cert.pem + manufacturing_server_https_cert: {{ keys_path }}/manufacturing_server_https_cert.crt + manufacturing_server_https_key: {{ keys_path }}/manufacturing_server_https_key.key diff --git a/integration-tests/tests/common/mod.rs b/integration-tests/tests/common/mod.rs index e406cc339..440993ce2 100644 --- a/integration-tests/tests/common/mod.rs +++ b/integration-tests/tests/common/mod.rs @@ -3,7 +3,7 @@ use std::{ env, fs::{self, create_dir, File}, - io::{BufRead, BufReader}, + io::{BufRead, BufReader, Write}, path::{Path, PathBuf}, process::{Child, Command, ExitStatus}, time::{Duration, Instant}, @@ -22,6 +22,11 @@ use openssl::{ use fdo_util::servers::format_conf_env; +use openssl::rsa::Rsa; +use openssl::x509::extension::SubjectAlternativeName; +use openssl::x509::X509Extension; +use openssl::x509::X509ReqBuilder; + const PORT_BASE: u16 = 5080; lazy_static::lazy_static! { @@ -241,6 +246,9 @@ impl TestContext { }; new_context.create_keys().context("Error creating keys")?; + new_context + .generate_https_keys_and_certs() + .context("Error creating https key & cert")?; Ok(new_context) } @@ -259,6 +267,98 @@ impl TestContext { pub fn runner_path(&self, number: &TestBinaryNumber) -> PathBuf { self.testpath.join(number.name()) } + pub fn generate_https_keys_and_certs(&self) -> Result<()> { + let https_keys_path = self.keys_path(); + // create_dir(&https_keys_path).context("Error creating HTTPS keys directory")?; + + /* // Generate RSA private key + let rsa = Rsa::generate(2048).context("Error generating RSA private key")?; + let private_key = PKey::from_rsa(rsa).context("Error converting RSA private key to PKey")?; + + // Generate certificate request + let mut req_builder = X509ReqBuilder::new().context("Error creating X509ReqBuilder")?; + req_builder.set_pubkey(&private_key).context("Error setting public key in request")?; + req_builder + .add_extension( + X509Extension::subject_alt_name( + &SubjectAlternativeName::new() + .dns("localhost") + .dns("example.com"), + ) + .context("Error adding Subject Alternative Name extension")?, + ) + .context("Error adding extension to request")?; + let req = req_builder.build(); + + // Sign the certificate request with the private key + let cert = req + .sign(&private_key, MessageDigest::sha256()) + .context("Error signing certificate request")?; + + // Now serialize the key and certificate + let private_key = private_key + .private_key_to_der() + .context("Error converting private key to DER")?; + let cert = cert.to_pem().context("Error converting certificate to PEM")?; + + // Write them to disk + fs::write(https_keys_path.join("server_key.der"), private_key) + .context("Error writing private key")?; + fs::write(https_keys_path.join("server_cert.pem"), cert) + .context("Error writing certificate")?; */ + + // Generate RSA private key + let rsa = Rsa::generate(2048)?; + //let private_key = rsa.private_key_to_pem()?; + let private_key = + PKey::from_rsa(rsa).context("Error converting RSA private key to PKey")?; + + // Write private key to server.key file + let mut key_file = File::create(format!( + "{}/manufacturing_server_https_key.key", + https_keys_path.display() + ))?; + //key_file.write_all(&private_key)?; + key_file.write_all(&private_key.private_key_to_pem_pkcs8()?)?; + // Generate X.509 certificate + let mut builder = X509Builder::new()?; + + // Set subject for the certificate + let mut name_builder = X509NameBuilder::new()?; + name_builder.append_entry_by_nid(openssl::nid::Nid::COMMONNAME, "localhost")?; + let subject_name = name_builder.build(); + builder.set_subject_name(&subject_name)?; + + // Set issuer same as subject (self-signed certificate) + builder.set_issuer_name(&subject_name)?; + + // Set public key in the certificate + builder.set_pubkey(&private_key)?; + + // Set validity period of the certificate (365 days) + // let not_after = chrono::Utc::now() + chrono::Duration::days(365); + //builder.set_not_after(¬_after)?; + //builder.set_not_before(&chrono::Utc::now())?; + + // let not_after = chrono::Utc::now() + chrono::Duration::days(365); + // builder.set_not_after(Asn1Time::from(¬_after)?)?; + builder.set_not_after(Asn1Time::days_from_now(365)?.as_ref())?; + builder.set_not_before(Asn1Time::days_from_now(0)?.as_ref())?; + // builder.set_not_before(Asn1Time::from(&chrono::Utc::now())?)?; + + // Sign the certificate with the private key + builder.sign(&private_key, openssl::hash::MessageDigest::sha256())?; + let certificate = builder.build(); + + // Write certificate to server.crt file + let mut cert_file = File::create(format!( + "{}/manufacturing_server_https_cert.crt", + https_keys_path.display() + ))?; + cert_file.write_all(&certificate.to_pem()?)?; + + Ok(()) + } fn create_keys(&self) -> Result<()> { let keys_path = self.keys_path(); @@ -336,7 +436,6 @@ impl TestContext { fs::write(keys_path.join(format!("{}_cert.pem", key_name)), cert) .context("Error writing certificate")?; } - Ok(()) } @@ -801,6 +900,7 @@ impl<'a> TestServerConfigurator<'a> { "bind", &format!("127.0.0.1:{}", self.server_number.server_port().unwrap()), ); + cfg.insert("bind_https", &format!("127.0.0.1:{}", 6000)); cfg.insert("test_dir", &self.test_context.testpath()); cfg.insert("owner_port", &self.server_number.server_port().unwrap()); cfg.insert( diff --git a/integration-tests/tests/di_diun.rs b/integration-tests/tests/di_diun.rs index 11ae30010..39235f2f7 100644 --- a/integration-tests/tests/di_diun.rs +++ b/integration-tests/tests/di_diun.rs @@ -25,6 +25,9 @@ async fn test_device_credentials_already_active() -> Result<()> { cfg.insert("rendezvous_port", "1337"); cfg.insert("diun_key_type", "FileSystem"); cfg.insert("device_identification_format", "SerialNumber"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8096")); Ok(()) })?) }, @@ -109,6 +112,9 @@ async fn test_device_credentials_generated_with_mac_address() -> Result<()> { cfg.insert("rendezvous_port", "1337"); cfg.insert("diun_key_type", "FileSystem"); cfg.insert("device_identification_format", "MACAddress"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); Ok(()) })?) }, @@ -207,6 +213,9 @@ async fn test_device_credentials_with_tpm() -> Result<()> { cfg.insert("rendezvous_port", "1337"); cfg.insert("diun_key_type", "Tpm"); cfg.insert("device_identification_format", "SerialNumber"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); Ok(()) })?) }, @@ -254,6 +263,10 @@ async fn test_device_credentials_generated_with_mac_address_no_user_given_iface( cfg.insert("rendezvous_port", "1337"); cfg.insert("diun_key_type", "FileSystem"); cfg.insert("device_identification_format", "MACAddress"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); + Ok(()) })?) }, diff --git a/integration-tests/tests/di_diun_https.rs b/integration-tests/tests/di_diun_https.rs new file mode 100644 index 000000000..62b7f8309 --- /dev/null +++ b/integration-tests/tests/di_diun_https.rs @@ -0,0 +1,57 @@ +mod common; +use anyhow::{Context, Result}; +use common::{Binary, LogSide, TestContext}; +use std::path::Path; +use std::time::Duration; +const L: LogSide = LogSide::Test; + +#[tokio::test] +async fn di_diun_https_test() -> Result<()> { + let mut ctx = TestContext::new().context("Error building test context")?; + + let mfg_server = ctx + .start_test_server( + Binary::ManufacturingServer, + |cfg| { + Ok(cfg.prepare_config_file(None, |cfg| { + cfg.insert("rendezvous_port", "1337"); + cfg.insert("diun_key_type", "FileSystem"); + cfg.insert("device_identification_format", "SerialNumber"); + // cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_http", "8085"); + // cfg.insert("bind_https", &("127.0.0.1:{}" )); + Ok(()) + })?) + }, + |_| Ok(()), + ) + .context("Error creating manufacturing server")?; + ctx.wait_until_servers_ready() + .await + .context("Error waiting for servers to start")?; + + let client_result = ctx + .run_client( + Binary::ManufacturingClient, + Some(&mfg_server), + |cfg| { + cfg.env("DEVICE_CREDENTIAL_FILENAME", "devicecredential.dc") + .env("MANUFACTURING_SERVER_URL", "https://localhost:8086") + .env("DEV_ENVIRONMENT", "1") + .env("DIUN_PUB_KEY_INSECURE", "true"); + Ok(()) + }, + Duration::from_secs(5), + ) + .context("Error running manufacturing client")?; + client_result + .expect_success() + .context("Manufacturing client failed")?; + + let dc_path = client_result.client_path().join("devicecredential.dc"); + L.l(format!("Device Credential should be in {:?}", dc_path)); + assert!(Path::new(&dc_path).exists()); + + Ok(()) +} diff --git a/integration-tests/tests/e2e.rs b/integration-tests/tests/e2e.rs index 4afdd92c0..b0835d51f 100644 --- a/integration-tests/tests/e2e.rs +++ b/integration-tests/tests/e2e.rs @@ -192,6 +192,10 @@ where cfg.insert("diun_key_type", diun_key_type); cfg.insert("rendezvous_port", &rendezvous_server.server_port().unwrap()); cfg.insert("device_identification_format", "SerialNumber"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); + Ok(()) })?) }, @@ -514,6 +518,10 @@ where cfg.insert("diun_key_type", diun_key_type); cfg.insert("rendezvous_port", &rendezvous_server.server_port().unwrap()); cfg.insert("device_identification_format", "SerialNumber"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); + Ok(()) })?) }, diff --git a/integration-tests/tests/service_info.rs b/integration-tests/tests/service_info.rs index 940b0fd56..f10d4296f 100644 --- a/integration-tests/tests/service_info.rs +++ b/integration-tests/tests/service_info.rs @@ -106,6 +106,9 @@ where cfg.insert("diun_key_type", diun_key_type); cfg.insert("rendezvous_port", &rendezvous_server.server_port().unwrap()); cfg.insert("device_identification_format", "SerialNumber"); + cfg.insert("manufacturing_server_https_cert_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + cfg.insert("manufacturing_server_https_key_path", "/workspaces/fido-device-onboard-rs/integration-tests/tests/test-data/https-test"); + // cfg.insert("bind_https", &format!("0.0.0.0:{}","8086")); Ok(()) })?) }, diff --git a/integration-tests/tests/test-data/https-test/manufacturing_server_https_cert.crt b/integration-tests/tests/test-data/https-test/manufacturing_server_https_cert.crt new file mode 100644 index 000000000..c289b9a5d --- /dev/null +++ b/integration-tests/tests/test-data/https-test/manufacturing_server_https_cert.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqzCCApMCFDBq5YwvijIjOB6U4yFgJpJwHTsEMA0GCSqGSIb3DQEBCwUAMIGR +MQswCQYDVQQGEwJJRTEPMA0GA1UECAwGR2Fsd2F5MQ8wDQYDVQQHDAZHYWx3YXkx +EDAOBgNVBAoMB1JlZCBIYXQxDDAKBgNVBAsMA1I0RTEcMBoGA1UEAwwTd3d3LmZk +by5leGFtcGxlLmNvbTEiMCAGCSqGSIb3DQEJARYTc2FybWFoYWpAcmVkaGF0LmNv +bTAeFw0yMzA5MTIxMDA5MzdaFw0yNDA5MTExMDA5MzdaMIGRMQswCQYDVQQGEwJJ +RTEPMA0GA1UECAwGR2Fsd2F5MQ8wDQYDVQQHDAZHYWx3YXkxEDAOBgNVBAoMB1Jl +ZCBIYXQxDDAKBgNVBAsMA1I0RTEcMBoGA1UEAwwTd3d3LmZkby5leGFtcGxlLmNv +bTEiMCAGCSqGSIb3DQEJARYTc2FybWFoYWpAcmVkaGF0LmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMiKFA4zj4DZ3S85HosHND7hAapN7MSS6h+4 +xdJC6xZBe4EkSNpvuj22I09bxdmdPB4KDI0mKIhzM5QTmeIj5ejGaeviuDbLuF1t +2CLbb4Dprj9uS81XattqSdRDeWa4EZRGf3iGoryb2KgdRaqT1sy5Rh2KfNa+267w +JElZ6EsBjjXojBO2yg+dW75U1oIhLtQPFUIQ78muOr8Hg6p67UHaLO6rry7R/Dhd +bphrJwLME5AaQAvpudWM7y0PrHsOzW3nmykktTSbOXBWtx2d7pZYju+DXSW9/1rV ++GV+NtoUIjUL9fEKm9mT2VuW433ZCvPrQTAcNo87VsMYk4mZyZcCAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEAx0l+3iEf6SydBwWP1qVFPRC9NExym5DN14bYQivBwvNO +454WrO/lQyXuKsMrS5Uu2bURNblxs7lOIfyzIn9CHZq8DRcAfPoVl9nn90WnD72j +YIqCvOcC5VtLR5SFMIfWYgpj7/uHhEO0ykQk5oLkxkooPROOcJPDdUuZZx5hY3f9 +r7zGBrPhQHT+3YJmg2aF4j7+GCGoydg+alkxLHhHfs7r+tH7bNtL28x86iqilWGs +7ciG5nZm+tM/DaI+yUtnJhN83J6914Zjm8QX/85IiaBC6rVcEfkFTkqlPXId2kHV +pmRu5tNQOqLctpmIr+M1/JQDuhkoh+MyJBfEwzG6Tw== +-----END CERTIFICATE----- diff --git a/integration-tests/tests/test-data/https-test/manufacturing_server_https_key.key b/integration-tests/tests/test-data/https-test/manufacturing_server_https_key.key new file mode 100644 index 000000000..0098d0464 --- /dev/null +++ b/integration-tests/tests/test-data/https-test/manufacturing_server_https_key.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIihQOM4+A2d0v +OR6LBzQ+4QGqTezEkuofuMXSQusWQXuBJEjab7o9tiNPW8XZnTweCgyNJiiIczOU +E5niI+Xoxmnr4rg2y7hdbdgi22+A6a4/bkvNV2rbaknUQ3lmuBGURn94hqK8m9io +HUWqk9bMuUYdinzWvtuu8CRJWehLAY416IwTtsoPnVu+VNaCIS7UDxVCEO/Jrjq/ +B4Oqeu1B2izuq68u0fw4XW6YaycCzBOQGkAL6bnVjO8tD6x7Ds1t55spJLU0mzlw +Vrcdne6WWI7vg10lvf9a1fhlfjbaFCI1C/XxCpvZk9lbluN92Qrz60EwHDaPO1bD +GJOJmcmXAgMBAAECgf9sNVs/8WPmXUt8Uqdio0ZTkESP8h8424G/Vl05aPm25bXh +p9V5Tlv2Hy7XI3PZBDkDcN0PHjZ7DSLTWzSiC7zI6Y0PgSPOPvBfeZSPaQcqZXkJ +NW8Or4WQmdVT7iv8e21d/ZUQlttQR4HyHXxXDp1wTrdMlnk6rMHGOs4T3anL5ZY9 +juXeva/7ilyETvuGCogx4F8/jMpaG9ypr8HYUCwEtt2pEL1eak5VQ/U1wzoN5mKd +Ke8eIfuE1JLdArFgXL9T3xmEu640JW27p94FTt6gUzutY6H88QvRy0S6A+Umat/A +0sM3T0U3wZso0VI3wpgLScy+aKYz+NE+HWb74hkCgYEA7D3yhQ5iORs1o/ELdfJP +j4pzaqyQ0fTjyXJ0BeCdbyvt6pKyEOVWr6N5tSb1LuoqHNO2qu/7pH6MjQMurT+U +wQZ6i0DCSVedu5FFVA6udvtZ3hVyatoufNf9bCEKWNZ5sa3H54FQ355FynM/Rn4R +t2GhoOJD44zbn97feXBqix8CgYEA2U+4nS6aNXqYcbGYJCMbkIeby4ryWyt9eTgv +T9H9eMD4A6pcHAIY7AFsqXvAIbZYMvDHUNwfQuJs9qdSciyiACypAYnXkJ+7tYYf +28gaYREoOmiKAwiJokpdkMcriKCMqf/dww31p1+7DUt2ph2IVAS7oxvXmI0kdQps +o9A16okCgYBcfHAKiTQI+U8JrJ95AHEHWAORWpejqsTWo7kqZ+IamM8ey2ls1ewW +/N/z/Jl2UXRFLtlPmH4iyuxJdFp8tuyMmPW2uJaXUfbNbLUAkHLa39Ix8KGHlX0n +oQN0poa/es/PsKIXTvgTY4odFPtLpKVY7p7xNkOvvQBqWT2R9GGxrwKBgQDOOSJY +P65mC9Z5JnDb8lkpOhe1/EDsFgR3scLsO7oQCwgR6myIw9DEvsFQwThehb2Dcg2k +BZDBF0ESfUz3PrXp9nfYFuhzvbITnJnFJ0spTG/hpe063bJHSc0rJGeCu+FhPohD +n4687FMFVdTd5W7HVMqACl75zQ+I5oCcoG1aCQKBgQCtG6wdu+hrYEziJv44+s3V +Iwcf2Ao5VLJvdSoBucSb5k5pXHVXX7o9Wc55CAz+1Asx6n1RcF2z0uThy7mfOWKF +nX6Q8jYQJZ0V/oLjp41+xq+vSznDhnh9dqxufhSq1mOmKG5b45klCGu9mGKvdNDO +FLaCYQ/hltBYdxWd3GEw9A== +-----END PRIVATE KEY----- diff --git a/manufacturing-client/src/main.rs b/manufacturing-client/src/main.rs index f9277754d..150588525 100644 --- a/manufacturing-client/src/main.rs +++ b/manufacturing-client/src/main.rs @@ -349,7 +349,7 @@ async fn main() -> Result<()> { keyref = KeyReference::str_key(args.key_ref) .await .context("Error determining key for DI")?; - client = ServiceClient::new(ProtocolVersion::Version1_1, &url); + client = ServiceClient::new(ProtocolVersion::Version1_1, &url)?; } Commands::NoPlainDI(args) => { url = args.manufacturing_server_url; @@ -369,7 +369,7 @@ async fn main() -> Result<()> { } log::debug!("Performing DIUN"); - client = ServiceClient::new(ProtocolVersion::Version1_1, &url); + client = ServiceClient::new(ProtocolVersion::Version1_1, &url)?; (keyref, mfg_string_type) = perform_diun(&mut client, diun_pub_key_verification) .await .context("Error performing DIUN")?; @@ -400,7 +400,7 @@ async fn main() -> Result<()> { url = env::var("MANUFACTURING_SERVER_URL") .context("Please provide MANUFACTURING_SERVER_URL")?; - client = ServiceClient::new(ProtocolVersion::Version1_1, &url); + client = ServiceClient::new(ProtocolVersion::Version1_1, &url)?; let use_plain_di = match env::var("USE_PLAIN_DI") { Ok(val) => val == "true", diff --git a/manufacturing-server/Cargo.toml b/manufacturing-server/Cargo.toml index 32faee453..6ba94955f 100644 --- a/manufacturing-server/Cargo.toml +++ b/manufacturing-server/Cargo.toml @@ -22,3 +22,6 @@ fdo-data-formats = { path = "../data-formats", version = "0.4.13" } fdo-http-wrapper = { path = "../http-wrapper", version = "0.4.13", features = ["server"] } fdo-store = { path = "../store", version = "0.4.13", features = ["directory"] } fdo-util = { path = "../util", version = "0.4.13" } + +hyper = { version = "0.14", features = ["tcp"] } +tls-listener = { version = "0.7.0", features = ["openssl", "hyper-h1"]} \ No newline at end of file diff --git a/manufacturing-server/src/main.rs b/manufacturing-server/src/main.rs index c99bd1eac..e5de4c27a 100644 --- a/manufacturing-server/src/main.rs +++ b/manufacturing-server/src/main.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::sync::Arc; use anyhow::{bail, Context, Error, Result}; +use fdo_util::servers::configuration::AbsolutePathBuf; use openssl::{ pkey::{PKey, Private}, x509::X509, @@ -26,6 +27,16 @@ use fdo_util::servers::{ settings_for, yaml_to_cbor, OwnershipVoucherStoreMetadataKey, }; +use std::convert::Infallible; +//use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslAcceptorBuilder}; +//use tokio::net::TcpListener; +//use std::net::SocketAddr; +use hyper::server::conn::AddrIncoming; +use tls_listener::TlsListener; + +pub mod tls_config; +use tls_config::tls_acceptor; + const PERFORMED_DIUN_SES_KEY: &str = "mfg_global_diun_performed"; const DEVICE_KEY_FROM_DIUN_SES_KEY: &str = "mfg_global_device_key_from_diun"; @@ -51,7 +62,7 @@ impl fdo_store::MetadataLocalKey for PublicKeyStoreMetadataKey { } } -struct ManufacturingServiceUD { +pub struct ManufacturingServiceUD { // Stores session_store: Arc, ownership_voucher_store: Box< @@ -71,8 +82,8 @@ struct ManufacturingServiceUD { device_cert_key: PKey, device_cert_chain: X5Chain, owner_cert: Option, - - // Rendezvous Info + manufacturing_server_https_cert: AbsolutePathBuf, + manufacturing_server_https_key: AbsolutePathBuf, rendezvous_info: RendezvousInfo, // Protocols @@ -175,7 +186,9 @@ async fn main() -> Result<()> { .context("Error parsing configuration")?; // Bind information - let bind_addr = settings.bind.clone(); + // seperate bind address for running http and https + let bind_https_addr = settings.bind_https.clone(); + let bind_http_addr = settings.bind_http.clone(); // Initialize stores let session_store = settings @@ -225,6 +238,9 @@ async fn main() -> Result<()> { .context("Error parsing manufacturer private key")?, ), }; + let manufacturing_server_https_cert = settings.manufacturing.manufacturing_server_https_cert; + let manufacturing_server_https_key = settings.manufacturing.manufacturing_server_https_key; + let owner_cert = match settings.manufacturing.owner_cert_path { None => None, Some(path) => Some( @@ -259,6 +275,8 @@ async fn main() -> Result<()> { manufacturer_cert, manufacturer_key, owner_cert, + manufacturing_server_https_cert, + manufacturing_server_https_key, rendezvous_info, @@ -304,42 +322,80 @@ async fn main() -> Result<()> { handlers::diun::provide_key, ); + // routes, no change in protocol handlers required since https request once handled at TLS layer should + // work the same way as http protocol handler + let routes = warp::post() .and( hello - .or(handler_ping) + .clone() + .or(handler_ping.clone()) // DI - .or(handler_di_app_start) - .or(handler_di_set_hmac) + .or(handler_di_app_start.clone()) + .or(handler_di_set_hmac.clone()) // DIUN - .or(handler_diun_connect) - .or(handler_diun_request_key_parameters) - .or(handler_diun_provide_key), + .or(handler_diun_connect.clone()) + .or(handler_diun_request_key_parameters.clone()) + .or(handler_diun_provide_key.clone()), ) - .recover(fdo_http_wrapper::server::handle_rejection) - .with(warp::log("manufacturing-server")); + .recover(fdo_http_wrapper::server::handle_rejection.clone()) + .with(warp::log("manufacturing-server to handle https and https")); - log::info!("Listening on {}", bind_addr); - let server = warp::serve(routes); + // Convert routes into a warp service, so that we can use hyper to serve this services with TLS config. + let service = warp::service(routes.clone()); - let maintenance_runner = - tokio::spawn(async move { perform_maintenance(user_data.clone()).await }); + let make_svc = hyper::service::make_service_fn(move |_| { + let svc = service.clone(); + async move { Ok::<_, Infallible>(svc) } + }); - let server = server - .bind_with_graceful_shutdown(bind_addr, async { + let incoming = TlsListener::new( + tls_acceptor(user_data.clone()), + AddrIncoming::bind(&bind_https_addr.into())?, + ); + let https_server = hyper::Server::builder(incoming).serve(make_svc); + let https_server = https_server.with_graceful_shutdown(async { + signal(SignalKind::terminate()).unwrap().recv().await; + log::info!("Terminating HTTPS server"); + }); + let https_server_handle = tokio::spawn(https_server); + + let http_server = warp::serve(routes.clone()); + let http_server = http_server + .bind_with_graceful_shutdown(bind_http_addr, async { signal(SignalKind::terminate()).unwrap().recv().await; - log::info!("Terminating"); + log::info!("Terminating HTTP server"); }) .1; - let server = tokio::spawn(server); - - tokio::select!( - _ = server => { - log::info!("Server terminated"); - }, - _ = maintenance_runner => { - log::info!("Maintenance runner terminated"); - }); + let http_server_handle = tokio::spawn(http_server); + + // maintainance runner + let maintenance_runner_handle = + tokio::spawn(async move { perform_maintenance(user_data.clone()).await }); + + log::info!("starting both servers with http & https support"); + // Join all the three handlers and wait + let (http_result, https_result, maintenance_result) = tokio::join!( + http_server_handle, + https_server_handle, + maintenance_runner_handle + ); + + // Check the results and handle accordingly since we have joined + match http_result { + Ok(_) => log::info!("HTTP server terminated successfully"), + Err(err) => log::error!("HTTP server terminated with an error: {:?}", err), + } + + match https_result { + Ok(_) => log::info!("HTTPS server terminated successfully"), + Err(err) => log::error!("HTTPS server terminated with an error: {:?}", err), + } + + match maintenance_result { + Ok(_) => log::info!("Maintenance runner terminated successfully"), + Err(err) => log::error!("Maintenance runner terminated with an error: {:?}", err), + } Ok(()) } diff --git a/manufacturing-server/src/tls_config/mod.rs b/manufacturing-server/src/tls_config/mod.rs new file mode 100644 index 000000000..cf75af3a6 --- /dev/null +++ b/manufacturing-server/src/tls_config/mod.rs @@ -0,0 +1,27 @@ +pub mod tls_config { + use crate::ManufacturingServiceUD; + use openssl::ssl::{SslContext, SslFiletype, SslMethod}; + use std::path::Path; + use std::sync::Arc; + pub type Acceptor = openssl::ssl::SslContext; + + fn tls_acceptor_impl>(cert_file: P, key_file: P) -> Acceptor { + let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap(); + builder + .set_certificate_file(cert_file, SslFiletype::PEM) + .unwrap(); + builder + .set_private_key_file(key_file, SslFiletype::PEM) + .unwrap(); + builder.build() + } + + pub fn tls_acceptor(user_data: Arc) -> Acceptor { + let cert_file = &user_data.manufacturing_server_https_cert; + let key_file = &user_data.manufacturing_server_https_key; + tls_acceptor_impl(cert_file, key_file) + } +} + +pub use tls_config::tls_acceptor; +pub use tls_config::Acceptor; diff --git a/owner-onboarding-server/src/main.rs b/owner-onboarding-server/src/main.rs index f1221aa78..ef9206776 100644 --- a/owner-onboarding-server/src/main.rs +++ b/owner-onboarding-server/src/main.rs @@ -177,7 +177,7 @@ async fn report_ov_to_rendezvous( ); let mut rv_client = - fdo_http_wrapper::client::ServiceClient::new(ProtocolVersion::Version1_1, &rv_url); + fdo_http_wrapper::client::ServiceClient::new(ProtocolVersion::Version1_1, &rv_url)?; // Send: Hello, Receive: HelloAck let hello_ack: RequestResult = rv_client diff --git a/util/src/servers/configuration/manufacturing_server.rs b/util/src/servers/configuration/manufacturing_server.rs index 315d7f442..06d437667 100644 --- a/util/src/servers/configuration/manufacturing_server.rs +++ b/util/src/servers/configuration/manufacturing_server.rs @@ -21,7 +21,9 @@ pub struct ManufacturingServerSettings { pub public_key_store_driver: Option, // Bind information - pub bind: Bind, + pub bind_http: Bind, + + pub bind_https: Bind, pub protocols: ProtocolSetting, @@ -38,6 +40,8 @@ pub struct ManufacturingSettings { pub owner_cert_path: Option, pub manufacturer_private_key: Option, + pub manufacturing_server_https_cert: AbsolutePathBuf, + pub manufacturing_server_https_key: AbsolutePathBuf, } #[derive(Debug, Serialize, Deserialize)]