diff --git a/Cargo.lock b/Cargo.lock index a6ffbc3..968407c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -870,6 +870,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1645,6 +1655,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.11" @@ -2134,6 +2160,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2215,7 +2258,7 @@ dependencies = [ [[package]] name = "omnect-cli" -version = "0.26.2" +version = "0.26.3" dependencies = [ "actix-web", "anyhow", @@ -2318,6 +2361,12 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "openssl-sys" version = "0.9.107" @@ -2757,19 +2806,23 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.6.0", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tower", "tower-service", "url", @@ -2834,6 +2887,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + [[package]] name = "rustversion" version = "1.0.20" @@ -2855,12 +2923,44 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.26" @@ -3134,6 +3234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand 2.3.0", + "getrandom 0.3.2", "once_cell", "rustix 1.0.5", "windows-sys 0.59.0", @@ -3276,6 +3377,16 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.14" diff --git a/Cargo.toml b/Cargo.toml index 2f50883..844d882 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" name = "omnect-cli" readme = "README.md" repository = "https://github.com/omnect/omnect-cli" -version = "0.26.2" +version = "0.26.3" [dependencies] actix-web = "4.11" @@ -47,7 +47,7 @@ num_cpus = { version = "1.17", default-features = false } oauth2 = { version = "5.0", default-features = false, features = ["reqwest"] } open = { version = "5.3", default-features = false } regex = { version = "1.11", default-features = false } -reqwest = { version = "0.12", default-features = false, features = ["json"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false } serde_path_to_error = { version = "0.1", default-features = false } diff --git a/src/auth.rs b/src/auth.rs index f35f20f..b8bb436 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -4,13 +4,12 @@ use tokio::sync::{mpsc, oneshot}; use anyhow::Result; -use actix_web::{error, get, web, App, HttpServer}; +use actix_web::{App, HttpServer, error, get, web}; use serde::Deserialize; -use oauth2::basic::BasicClient; use oauth2::{ AuthUrl, AuthorizationCode, ClientId, CsrfToken, PkceCodeChallenge, RedirectUrl, TokenResponse, - TokenUrl, + TokenUrl, basic::BasicClient, }; #[derive(Deserialize)] diff --git a/src/device_update.rs b/src/device_update.rs index 861f3ac..c59559a 100644 --- a/src/device_update.rs +++ b/src/device_update.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use azure_identity::{ClientSecretCredential, TokenCredentialOptions}; use azure_iot_deviceupdate::DeviceUpdateClient; -use azure_storage::{shared_access_signature::service_sas::BlobSasPermissions, StorageCredentials}; +use azure_storage::{StorageCredentials, shared_access_signature::service_sas::BlobSasPermissions}; use azure_storage_blobs::prelude::{BlobServiceClient, ContainerClient}; use base64::prelude::*; use log::{debug, info}; diff --git a/src/file/mod.rs b/src/file/mod.rs index 8013b66..a493e51 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -2,15 +2,17 @@ pub mod compression; pub mod functions; use super::validators::{ device_update, - identity::{validate_identity, IdentityConfig, IdentityType}, + identity::{IdentityConfig, IdentityType, validate_identity}, ssh::validate_ssh_pub_key, }; use crate::file::functions::{FileCopyFromParams, FileCopyToParams, Partition}; use anyhow::{Context, Result}; use log::warn; use regex::Regex; -use std::fs; -use std::path::{Path, PathBuf}; +use std::{ + fs, + path::{Path, PathBuf}, +}; pub fn set_iotedge_gateway_config( config_file: &Path, diff --git a/src/image.rs b/src/image.rs index 6983322..b07222e 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,7 +1,7 @@ use std::path::Path; -use crate::file::functions::read_file_from_image; use crate::file::functions::Partition; +use crate::file::functions::read_file_from_image; use anyhow::{Context, Result}; use regex::Regex; use std::sync::LazyLock; diff --git a/src/lib.rs b/src/lib.rs index 11eeaa6..8713377 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -251,17 +251,14 @@ pub fn run() -> Result<()> { generate_bmap, compress_image, }) => { - let cert_info = create_image_cert( - &image, - CertificateOptions { - intermediate_full_chain_cert: &intermediate_full_chain_cert, - intermediate_key: &intermediate_key, - target_cert: "device_cert_path.pem", - target_key: "device_key_path.key.pem", - subject: &device_id, - validity_days: days, - }, - ) + let cert_info = create_image_cert(&image, CertificateOptions { + intermediate_full_chain_cert: &intermediate_full_chain_cert, + intermediate_key: &intermediate_key, + target_cert: "device_cert_path.pem", + target_key: "device_key_path.key.pem", + subject: &device_id, + validity_days: days, + }) .context("set_device_certificate: could not create certificate")?; run_image_command(image, generate_bmap, compress_image, |img| { @@ -282,17 +279,14 @@ pub fn run() -> Result<()> { generate_bmap, compress_image, }) => { - let cert_info = create_image_cert( - &image, - CertificateOptions { - intermediate_full_chain_cert: &intermediate_full_chain_cert, - intermediate_key: &intermediate_key, - target_cert: "edge_ca_cert_path.pem", - target_key: "edge_ca_key_path.key.pem", - subject: &device_id, - validity_days: days, - }, - ) + let cert_info = create_image_cert(&image, CertificateOptions { + intermediate_full_chain_cert: &intermediate_full_chain_cert, + intermediate_key: &intermediate_key, + target_cert: "edge_ca_cert_path.pem", + target_key: "edge_ca_key_path.key.pem", + subject: &device_id, + validity_days: days, + }) .context("set_edge_ca_certificate: could not create certificate")?; run_image_command(image, generate_bmap, compress_image, |img| { diff --git a/src/ssh.rs b/src/ssh.rs index 5cb7a88..f6f12f9 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -1,17 +1,14 @@ use std::convert::AsRef; use std::fs; -use std::io::prelude::*; -use std::io::BufWriter; +use std::io::{BufWriter, prelude::*}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str; -use anyhow::anyhow; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, anyhow}; use directories::ProjectDirs; use oauth2::AccessToken; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use url::Url; static BACKEND_API_ENDPOINT: &str = "/api/devices/prepareSSHConnection"; @@ -192,9 +189,11 @@ async fn unpack_response(response: reqwest::Response) -> Re internal_message: String, } - anyhow::bail!(serde_json::from_str::(&body) - .map(|err| err.internal_message) - .unwrap_or_else(|_| "unknown error type".to_string())) + anyhow::bail!( + serde_json::from_str::(&body) + .map(|err| err.internal_message) + .unwrap_or_else(|_| "unknown error type".to_string()) + ) } else { serde_json::from_str(&body).map_err(|_| anyhow!("unsupported reply.")) } diff --git a/src/validators/identity.rs b/src/validators/identity.rs index 606eb6f..afc2c51 100644 --- a/src/validators/identity.rs +++ b/src/validators/identity.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use log::debug; use regex::Regex; use serde::Deserialize; @@ -189,19 +189,15 @@ const PAYLOAD_FILEPATH: &str = "file:///etc/omnect/dps-payload.json"; const WARN_MISSING_PROVISIONING: &str = "A provisioning section should be specified."; const WARN_MISSING_DPS_PARAMS: &str = "For provisioning source dps, global_endpoint and id_scope should be specified."; -const WARN_MISSING_MANUAL_PARAMS: &str = - "For provisioning source manual, either connection_string or iothub_hostname and device_id are required."; -const WARN_MISSING_AUTHENTICATION: &str = - "For provisioning source manual, an authentication section should be present in the provisioning section."; -const WARN_MISSING_ATTESTATION: &str = - "For provisioning source dps an attestation section should be present in the provisioning section."; +const WARN_MISSING_MANUAL_PARAMS: &str = "For provisioning source manual, either connection_string or iothub_hostname and device_id are required."; +const WARN_MISSING_AUTHENTICATION: &str = "For provisioning source manual, an authentication section should be present in the provisioning section."; +const WARN_MISSING_ATTESTATION: &str = "For provisioning source dps an attestation section should be present in the provisioning section."; const WARN_ATTESTATION_VALID_METHOD_EXPECTED: &str = "The attestation method should be tpm, x509 or symmetric_key."; const WARN_INVALID_SOURCE: &str = "The provisioning source should be dps or manual."; const WARN_AUTHENTICATION_VALID_METHOD_EXPECTED: &str = "The authentication method should be sas."; const WARN_UNEXPECTED_PATH: &str = "Unexpected path found."; -const WARN_UNEQUAL_COMMON_NAME_AND_REGISTRATION_ID: &str = - "provisioning.attestation.registration_id is not equal to provisioning.attestation.identity_cert.common_name"; +const WARN_UNEQUAL_COMMON_NAME_AND_REGISTRATION_ID: &str = "provisioning.attestation.registration_id is not equal to provisioning.attestation.identity_cert.common_name"; const WARN_PAYLOAD_FILEPATH_MISSING: &str = "Payload file is configred but file is missing."; const WARN_PAYLOAD_CONFIG_MISSING: &str = "Payload file is passed but not configred."; @@ -375,12 +371,14 @@ mod tests { #[test] fn identity_config_hostname_valid() { LazyLock::force(&LOG); - assert!(validate_identity( - IdentityType::Standalone, - Path::new("testfiles/identity_config_hostname_valid.toml"), - &None, - ) - .is_ok()); + assert!( + validate_identity( + IdentityType::Standalone, + Path::new("testfiles/identity_config_hostname_valid.toml"), + &None, + ) + .is_ok() + ); } #[test] diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 348f5a9..13e96a7 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,9 +1,7 @@ use data_encoding::HEXUPPER; use env_logger::{Builder, Env}; use ring::digest::{Context, SHA256}; -use std::fs::copy; -use std::fs::File; -use std::fs::{create_dir_all, remove_dir_all}; +use std::fs::{File, copy, create_dir_all, remove_dir_all}; use std::io::{BufReader, Read}; use std::path::PathBuf; use std::sync::LazyLock; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3409d1f..4b9c492 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -108,17 +108,23 @@ fn check_set_identity_gateway_config() { edge_device_identity_key_file_path.to_str().unwrap(), edge_device_identity_key_file_out_path )); - assert!(std::path::Path::new(hosts_file_out_path) - .try_exists() - .is_ok_and(|exists| exists)); + assert!( + std::path::Path::new(hosts_file_out_path) + .try_exists() + .is_ok_and(|exists| exists) + ); - assert!(std::fs::read_to_string(hosts_file_out_path) - .unwrap() - .contains("127.0.1.1 my-omnect-iotedge-gateway-device")); + assert!( + std::fs::read_to_string(hosts_file_out_path) + .unwrap() + .contains("127.0.1.1 my-omnect-iotedge-gateway-device") + ); - assert!(std::fs::read_to_string(hostname_file_out_path) - .unwrap() - .contains("my-omnect-iotedge-gateway-device")); + assert!( + std::fs::read_to_string(hostname_file_out_path) + .unwrap() + .contains("my-omnect-iotedge-gateway-device") + ); } #[test] @@ -185,13 +191,17 @@ fn check_set_identity_leaf_config() { root_ca_file_out_path )); - assert!(std::fs::read_to_string(hosts_file_out_path) - .unwrap() - .contains("127.0.1.1 my-omnect-iot-leaf-device")); + assert!( + std::fs::read_to_string(hosts_file_out_path) + .unwrap() + .contains("127.0.1.1 my-omnect-iot-leaf-device") + ); - assert!(std::fs::read_to_string(hostname_file_out_path) - .unwrap() - .contains("my-omnect-iot-leaf-device")); + assert!( + std::fs::read_to_string(hostname_file_out_path) + .unwrap() + .contains("my-omnect-iot-leaf-device") + ); } #[test] @@ -247,13 +257,17 @@ fn check_set_identity_config_est_template() { config_file_out_path )); - assert!(std::fs::read_to_string(hosts_file_out_path) - .unwrap() - .contains("127.0.1.1 test-omnect-est")); + assert!( + std::fs::read_to_string(hosts_file_out_path) + .unwrap() + .contains("127.0.1.1 test-omnect-est") + ); - assert!(std::fs::read_to_string(hostname_file_out_path) - .unwrap() - .contains("test-omnect-est")); + assert!( + std::fs::read_to_string(hostname_file_out_path) + .unwrap() + .contains("test-omnect-est") + ); } #[test] @@ -323,13 +337,17 @@ fn check_set_identity_config_payload_template() { payload_out_path )); - assert!(std::fs::read_to_string(hosts_file_out_path) - .unwrap() - .contains("127.0.1.1 test-omnect-est-with-payload")); + assert!( + std::fs::read_to_string(hosts_file_out_path) + .unwrap() + .contains("127.0.1.1 test-omnect-est-with-payload") + ); - assert!(std::fs::read_to_string(hostname_file_out_path) - .unwrap() - .contains("test-omnect-est-with-payload")); + assert!( + std::fs::read_to_string(hostname_file_out_path) + .unwrap() + .contains("test-omnect-est-with-payload") + ); } #[test] @@ -385,13 +403,17 @@ fn check_set_identity_config_tpm_template() { config_file_out_path )); - assert!(std::fs::read_to_string(hosts_file_out_path) - .unwrap() - .contains("127.0.1.1 test-omnect-tpm")); + assert!( + std::fs::read_to_string(hosts_file_out_path) + .unwrap() + .contains("127.0.1.1 test-omnect-tpm") + ); - assert!(std::fs::read_to_string(hostname_file_out_path) - .unwrap() - .contains("test-omnect-tpm")); + assert!( + std::fs::read_to_string(hostname_file_out_path) + .unwrap() + .contains("test-omnect-tpm") + ); } #[test] @@ -1009,31 +1031,36 @@ async fn check_ssh_tunnel_setup() { .await .unwrap(); - assert!(tr - .pathbuf() - .join("config") - .try_exists() - .is_ok_and(|exists| exists)); - assert!(tr - .pathbuf() - .join("id_ed25519") - .try_exists() - .is_ok_and(|exists| exists)); - assert!(tr - .pathbuf() - .join("id_ed25519.pub") - .try_exists() - .is_ok_and(|exists| exists)); - assert!(tr - .pathbuf() - .join("bastion-cert.pub") - .try_exists() - .is_ok_and(|exists| exists)); - assert!(tr - .pathbuf() - .join("device-cert.pub") - .try_exists() - .is_ok_and(|exists| exists)); + assert!( + tr.pathbuf() + .join("config") + .try_exists() + .is_ok_and(|exists| exists) + ); + assert!( + tr.pathbuf() + .join("id_ed25519") + .try_exists() + .is_ok_and(|exists| exists) + ); + assert!( + tr.pathbuf() + .join("id_ed25519.pub") + .try_exists() + .is_ok_and(|exists| exists) + ); + assert!( + tr.pathbuf() + .join("bastion-cert.pub") + .try_exists() + .is_ok_and(|exists| exists) + ); + assert!( + tr.pathbuf() + .join("device-cert.pub") + .try_exists() + .is_ok_and(|exists| exists) + ); let ssh_config = std::fs::read_to_string(tr.pathbuf().join("config")).unwrap(); let expected_config = format!(