public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH installer v2 08/17] fetch-answer: move http-related code to gated module in installer-common
Date: Thu, 18 Jul 2024 15:48:53 +0200	[thread overview]
Message-ID: <20240718134905.1177775-9-c.heiss@proxmox.com> (raw)
In-Reply-To: <20240718134905.1177775-1-c.heiss@proxmox.com>

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.heiss@proxmox.com>
---
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 21bc6a2..a6a8de0 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
-    /// ```
-    ///
-    /// # Arguemnts
-    /// * `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: String, 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..4a5d444
--- /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: String, 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.1



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  parent reply	other threads:[~2024-07-18 13:50 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-18 13:48 [pve-devel] [PATCH installer v2 00/17] fix #5536: implement post-(auto-)installation notification mechanism Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 01/17] tree-wide: fix some typos Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 02/17] fetch-answer: partition: fix clippy warning Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 03/17] common: simplify filesystem type serializing & Display trait impl Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 04/17] common: setup: serialize `target_hd` as string explicitly Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 05/17] common: split out installer setup files loading functionality Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 06/17] common: setup: deserialize `secure_boot` property from runtime env Christoph Heiss
2024-07-23 14:31   ` Aaron Lauterer
2024-08-05 13:12     ` Christoph Heiss
2024-08-19 10:32     ` Christoph Heiss
2024-08-20 14:56       ` Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 07/17] debian: strip unused library dependencies Christoph Heiss
2024-07-18 13:48 ` Christoph Heiss [this message]
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 09/17] tree-wide: convert some more crates to use workspace dependencies Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 10/17] auto-install-assistant: replace `PathBuf` parameters with `AsRef<Path>` Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 11/17] auto-installer: tests: replace manual panic!() with assert_eq!() Christoph Heiss
2024-07-23 10:39   ` Aaron Lauterer
2024-07-23 10:40     ` Aaron Lauterer
2024-07-23 10:46     ` Christoph Heiss
2024-07-23 11:04       ` Aaron Lauterer
2024-07-23 11:37         ` Christoph Heiss
2024-07-24  7:54           ` Thomas Lamprecht
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 12/17] auto-installer: tests: simplify empty disks check Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 13/17] auto-installer: tests: replace `PathBuf` parameters with `AsRef<Path>` Christoph Heiss
2024-07-18 13:48 ` [pve-devel] [PATCH installer v2 14/17] auto-installer: move `SystemDMI` struct to common crate Christoph Heiss
2024-07-18 13:49 ` [pve-devel] [PATCH installer v2 15/17] fix #5536: auto-installer: answer: add `posthook` section Christoph Heiss
2024-07-18 13:49 ` [pve-devel] [PATCH installer v2 16/17] fix #5536: post-hook: add utility for sending notifications after auto-install Christoph Heiss
2024-07-23 14:57   ` Aaron Lauterer
2024-07-24  8:24     ` Thomas Lamprecht
2024-07-24 11:21   ` Aaron Lauterer
2024-08-05 13:17     ` Christoph Heiss
2024-07-18 13:49 ` [pve-devel] [PATCH installer v2 17/17] unconfigured.sh: run proxmox-post-hook after successful auto-install Christoph Heiss
2024-07-24 11:34 ` [pve-devel] [PATCH installer v2 00/17] fix #5536: implement post-(auto-)installation notification mechanism Aaron Lauterer
2024-08-21  9:41 ` Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240718134905.1177775-9-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal