This enable reusage of this code in other crates. Needed esp. by the upcoming post-installation notification hook functionality.
No functional changes overall. Signed-off-by: Christoph Heiss <c.he...@proxmox.com> --- Changes v2 -> v3: * no changes Changes v1 -> v2: * no changes Cargo.toml | 6 ++ proxmox-fetch-answer/Cargo.toml | 17 +-- .../src/fetch_plugins/http.rs | 100 +----------------- proxmox-installer-common/Cargo.toml | 20 ++++ proxmox-installer-common/src/http.rs | 94 ++++++++++++++++ proxmox-installer-common/src/lib.rs | 3 + 6 files changed, 130 insertions(+), 110 deletions(-) create mode 100644 proxmox-installer-common/src/http.rs diff --git a/Cargo.toml b/Cargo.toml index 1e730ce..1dcf669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,9 @@ members = [ "proxmox-tui-installer", ] +[workspace.dependencies] +anyhow = "1.0" +log = "0.4.20" +toml = "0.7" +proxmox-auto-installer.path = "./proxmox-auto-installer" +proxmox-installer-common.path = "./proxmox-installer-common" diff --git a/proxmox-fetch-answer/Cargo.toml b/proxmox-fetch-answer/Cargo.toml index 964682a..50f3da3 100644 --- a/proxmox-fetch-answer/Cargo.toml +++ b/proxmox-fetch-answer/Cargo.toml @@ -10,16 +10,9 @@ license = "AGPL-3" exclude = [ "build", "debian" ] homepage = "https://www.proxmox.com" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -anyhow = "1.0" -hex = "0.4" -log = "0.4.20" -native-tls = "0.2" -proxmox-auto-installer = { path = "../proxmox-auto-installer" } -rustls = { version = "0.20", features = [ "dangerous_configuration" ] } -rustls-native-certs = "0.6" -sha2 = "0.10" -toml = "0.7" -ureq = { version = "2.6", features = [ "native-certs", "native-tls" ] } +anyhow.workspace = true +log.workspace = true +proxmox-auto-installer.workspace = true +proxmox-installer-common = { workspace = true, features = ["http"] } +toml.workspace = true diff --git a/proxmox-fetch-answer/src/fetch_plugins/http.rs b/proxmox-fetch-answer/src/fetch_plugins/http.rs index 5e10f6a..4317430 100644 --- a/proxmox-fetch-answer/src/fetch_plugins/http.rs +++ b/proxmox-fetch-answer/src/fetch_plugins/http.rs @@ -67,7 +67,8 @@ impl FetchFromHTTP { info!("Gathering system information."); let payload = SysInfo::as_json()?; info!("Sending POST request to '{answer_url}'."); - let answer = http_post::call(&answer_url, fingerprint.as_deref(), payload)?; + let answer = + proxmox_installer_common::http::post(&answer_url, fingerprint.as_deref(), payload)?; Ok(answer) } @@ -179,100 +180,3 @@ impl FetchFromHTTP { value.map(|value| String::from(&value[1..value.len() - 2])) } } - -mod http_post { - use anyhow::Result; - use rustls::ClientConfig; - use sha2::{Digest, Sha256}; - use std::sync::Arc; - use ureq::{Agent, AgentBuilder}; - - /// Issues a POST request with the payload (JSON). Optionally a SHA256 fingerprint can be used to - /// check the cert against it, instead of the regular cert validation. - /// To gather the sha256 fingerprint you can use the following command: - /// ```no_compile - /// openssl s_client -connect <host>:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin - /// ``` - /// - /// # Arguments - /// * `url` - URL to call - /// * `fingerprint` - SHA256 cert fingerprint if certificate pinning should be used. Optional. - /// * `payload` - The payload to send to the server. Expected to be a JSON formatted string. - pub fn call(url: &str, fingerprint: Option<&str>, payload: String) -> Result<String> { - let answer; - - if let Some(fingerprint) = fingerprint { - let tls_config = ClientConfig::builder() - .with_safe_defaults() - .with_custom_certificate_verifier(VerifyCertFingerprint::new(fingerprint)?) - .with_no_client_auth(); - - let agent: Agent = AgentBuilder::new().tls_config(Arc::new(tls_config)).build(); - - answer = agent - .post(url) - .set("Content-type", "application/json; charset=utf-8") - .send_string(&payload)? - .into_string()?; - } else { - let mut roots = rustls::RootCertStore::empty(); - for cert in rustls_native_certs::load_native_certs()? { - roots.add(&rustls::Certificate(cert.0)).unwrap(); - } - - let tls_config = rustls::ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(roots) - .with_no_client_auth(); - - let agent = AgentBuilder::new() - .tls_connector(Arc::new(native_tls::TlsConnector::new()?)) - .tls_config(Arc::new(tls_config)) - .build(); - answer = agent - .post(url) - .set("Content-type", "application/json; charset=utf-8") - .timeout(std::time::Duration::from_secs(60)) - .send_string(&payload)? - .into_string()?; - } - Ok(answer) - } - - struct VerifyCertFingerprint { - cert_fingerprint: Vec<u8>, - } - - impl VerifyCertFingerprint { - fn new<S: AsRef<str>>(cert_fingerprint: S) -> Result<std::sync::Arc<Self>> { - let cert_fingerprint = cert_fingerprint.as_ref(); - let sanitized = cert_fingerprint.replace(':', ""); - let decoded = hex::decode(sanitized)?; - Ok(std::sync::Arc::new(Self { - cert_fingerprint: decoded, - })) - } - } - - impl rustls::client::ServerCertVerifier for VerifyCertFingerprint { - fn verify_server_cert( - &self, - end_entity: &rustls::Certificate, - _intermediates: &[rustls::Certificate], - _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator<Item = &[u8]>, - _ocsp_response: &[u8], - _now: std::time::SystemTime, - ) -> Result<rustls::client::ServerCertVerified, rustls::Error> { - let mut hasher = Sha256::new(); - hasher.update(end_entity); - let result = hasher.finalize(); - - if result.as_slice() == self.cert_fingerprint { - Ok(rustls::client::ServerCertVerified::assertion()) - } else { - Err(rustls::Error::General("Fingerprint did not match!".into())) - } - } - } -} diff --git a/proxmox-installer-common/Cargo.toml b/proxmox-installer-common/Cargo.toml index 4b72041..d8e9e06 100644 --- a/proxmox-installer-common/Cargo.toml +++ b/proxmox-installer-common/Cargo.toml @@ -12,3 +12,23 @@ regex = "1.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_plain = "1.0" + +# `http` feature +anyhow = { workspace = true, optional = true } +hex = { version = "0.4", optional = true } +native-tls = { version = "0.2", optional = true } +rustls = { version = "0.20", features = [ "dangerous_configuration" ], optional = true } +rustls-native-certs = { version = "0.6", optional = true } +sha2 = { version = "0.10", optional = true } +ureq = { version = "2.6", features = [ "native-certs", "native-tls" ], optional = true } + +[features] +http = [ + "dep:anyhow", + "dep:hex", + "dep:native-tls", + "dep:rustls", + "dep:rustls-native-certs", + "dep:sha2", + "dep:ureq" +] diff --git a/proxmox-installer-common/src/http.rs b/proxmox-installer-common/src/http.rs new file mode 100644 index 0000000..b754ed8 --- /dev/null +++ b/proxmox-installer-common/src/http.rs @@ -0,0 +1,94 @@ +use anyhow::Result; +use rustls::ClientConfig; +use sha2::{Digest, Sha256}; +use std::sync::Arc; +use ureq::{Agent, AgentBuilder}; + +/// Issues a POST request with the payload (JSON). Optionally a SHA256 fingerprint can be used to +/// check the cert against it, instead of the regular cert validation. +/// To gather the sha256 fingerprint you can use the following command: +/// ```no_compile +/// openssl s_client -connect <host>:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin +/// ``` +/// +/// # Arguments +/// * `url` - URL to call +/// * `fingerprint` - SHA256 cert fingerprint if certificate pinning should be used. Optional. +/// * `payload` - The payload to send to the server. Expected to be a JSON formatted string. +pub fn post(url: &str, fingerprint: Option<&str>, payload: String) -> Result<String> { + let answer; + + if let Some(fingerprint) = fingerprint { + let tls_config = ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(VerifyCertFingerprint::new(fingerprint)?) + .with_no_client_auth(); + + let agent: Agent = AgentBuilder::new().tls_config(Arc::new(tls_config)).build(); + + answer = agent + .post(url) + .set("Content-Type", "application/json; charset=utf-8") + .send_string(&payload)? + .into_string()?; + } else { + let mut roots = rustls::RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs()? { + roots.add(&rustls::Certificate(cert.0)).unwrap(); + } + + let tls_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(); + + let agent = AgentBuilder::new() + .tls_connector(Arc::new(native_tls::TlsConnector::new()?)) + .tls_config(Arc::new(tls_config)) + .build(); + answer = agent + .post(url) + .set("Content-Type", "application/json; charset=utf-8") + .timeout(std::time::Duration::from_secs(60)) + .send_string(&payload)? + .into_string()?; + } + Ok(answer) +} + +struct VerifyCertFingerprint { + cert_fingerprint: Vec<u8>, +} + +impl VerifyCertFingerprint { + fn new<S: AsRef<str>>(cert_fingerprint: S) -> Result<std::sync::Arc<Self>> { + let cert_fingerprint = cert_fingerprint.as_ref(); + let sanitized = cert_fingerprint.replace(':', ""); + let decoded = hex::decode(sanitized)?; + Ok(std::sync::Arc::new(Self { + cert_fingerprint: decoded, + })) + } +} + +impl rustls::client::ServerCertVerifier for VerifyCertFingerprint { + fn verify_server_cert( + &self, + end_entity: &rustls::Certificate, + _intermediates: &[rustls::Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator<Item = &[u8]>, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result<rustls::client::ServerCertVerified, rustls::Error> { + let mut hasher = Sha256::new(); + hasher.update(end_entity); + let result = hasher.finalize(); + + if result.as_slice() == self.cert_fingerprint { + Ok(rustls::client::ServerCertVerified::assertion()) + } else { + Err(rustls::Error::General("Fingerprint did not match!".into())) + } + } +} diff --git a/proxmox-installer-common/src/lib.rs b/proxmox-installer-common/src/lib.rs index 850e825..ee9f2a8 100644 --- a/proxmox-installer-common/src/lib.rs +++ b/proxmox-installer-common/src/lib.rs @@ -3,4 +3,7 @@ pub mod options; pub mod setup; pub mod utils; +#[cfg(feature = "http")] +pub mod http; + pub const RUNTIME_DIR: &str = "/run/proxmox-installer"; -- 2.45.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel