[pve-devel] [PATCH installer v6 34/36] fetch-answer: use ISO specified configurations

Aaron Lauterer a.lauterer at proxmox.com
Wed Apr 17 14:31:06 CEST 2024


This patch switches the behavior to use the settings that can be
specified in the ISO.

This means, that it is possible to control how the answer file should be
fetched:

* auto - as usually, go through the options until one works (partition,
  http)
* included - the answer file is included in the ISO
* partition - only check for an answer file in a partition called
  'proxmoxinst' in lower or uppercase
* http - only fetch the answer file via an HTTP POST request.

Additionally it is possible to specify the HTTP URL directly in the ISO.

Placing the SSL fingerprint on a partition is not possible anymore. If
one wants to provide it right away (besides DHCP or DNS), it must be
incluced in the ISO itself. This reduced the need for another USB flash
drive.

Signed-off-by: Aaron Lauterer <a.lauterer at proxmox.com>
---
 proxmox-fetch-answer/Cargo.toml               |  1 +
 .../src/fetch_plugins/http.rs                 | 65 +++++++------------
 proxmox-fetch-answer/src/main.rs              | 64 ++++++++++++++----
 3 files changed, 77 insertions(+), 53 deletions(-)

diff --git a/proxmox-fetch-answer/Cargo.toml b/proxmox-fetch-answer/Cargo.toml
index fbcca46..797c185 100644
--- a/proxmox-fetch-answer/Cargo.toml
+++ b/proxmox-fetch-answer/Cargo.toml
@@ -17,6 +17,7 @@ log = "0.4.20"
 ureq = { version = "2.6", features = [ "native-certs", "native-tls" ] }
 rustls = { version = "0.20", features = [ "dangerous_configuration" ] }
 rustls-native-certs = "0.6"
+toml = "0.7"
 native-tls = "0.2"
 sha2 = "0.10"
 hex = "0.4"
diff --git a/proxmox-fetch-answer/src/fetch_plugins/http.rs b/proxmox-fetch-answer/src/fetch_plugins/http.rs
index 5772c42..4093131 100644
--- a/proxmox-fetch-answer/src/fetch_plugins/http.rs
+++ b/proxmox-fetch-answer/src/fetch_plugins/http.rs
@@ -2,16 +2,12 @@ use anyhow::{bail, Error, Result};
 use log::info;
 use std::{
     fs::{self, read_to_string},
-    path::Path,
     process::Command,
 };
 
 use crate::fetch_plugins::utils::post;
-use proxmox_auto_installer::sysinfo;
+use proxmox_auto_installer::{sysinfo, utils::AutoInstSettings};
 
-use super::utils;
-
-static CERT_FINGERPRINT_FILE: &str = "cert_fingerprint.txt";
 static ANSWER_SUBDOMAIN: &str = "proxmoxinst";
 static ANSWER_SUBDOMAIN_FP: &str = "proxmoxinst-fp";
 
@@ -37,30 +33,33 @@ pub struct FetchFromHTTP;
 
 impl FetchFromHTTP {
     /// Will try to fetch the answer.toml by sending a HTTP POST request. The URL can be configured
-    /// either via DHCP or DNS.
-    /// DHCP options are checked first. The SSL certificate need to be either trusted by the root
-    /// certs or a SHA256 fingerprint needs to be provided. The SHA256 SSL fingerprint can either
-    /// be placed in a `cert_fingerprint.txt` file in the `proxmoxinst` partition, as DHCP option,
-    /// or as DNS TXT record. If provided, the `cert_fingerprint.txt` file has preference.
-    pub fn get_answer() -> Result<String> {
-        info!("Checking for certificate fingerprint in file.");
-        let mut fingerprint: Option<String> = match Self::get_cert_fingerprint_from_file() {
-            Ok(fp) => Some(fp),
-            Err(err) => {
-                info!("{err}");
-                None
+    /// either via DHCP or DNS or preconfigured in the ISO.
+    /// If the URL is not defined in the ISO, it will first check DHCP options. The SSL certificate
+    /// needs to be either trusted by the root certs or a SHA256 fingerprint needs to be provided.
+    /// The SHA256 SSL fingerprint can either be defined in the ISO, as DHCP option, or as DNS TXT
+    /// record. If provided, the fingerprint provided in the ISO has preference.
+    pub fn get_answer(settings: &AutoInstSettings) -> Result<String> {
+        let mut fingerprint: Option<String> = match settings.cert_fingerprint.clone() {
+            Some(fp) => {
+                info!("SSL fingerprint provided through ISO.");
+                Some(fp)
             }
+            None => None,
         };
 
         let answer_url: String;
-
-        (answer_url, fingerprint) = match Self::fetch_dhcp(fingerprint.clone()) {
-            Ok((url, fp)) => (url, fp),
-            Err(err) => {
-                info!("{err}");
-                Self::fetch_dns(fingerprint.clone())?
-            }
-        };
+        if let Some(url) = settings.http_url.clone() {
+            info!("URL specified in ISO");
+            answer_url = url;
+        } else {
+            (answer_url, fingerprint) = match Self::fetch_dhcp(fingerprint.clone()) {
+                Ok((url, fp)) => (url, fp),
+                Err(err) => {
+                    info!("{err}");
+                    Self::fetch_dns(fingerprint.clone())?
+                }
+            };
+        }
 
         if fingerprint.is_some() {
             let fp = fingerprint.clone();
@@ -74,22 +73,6 @@ impl FetchFromHTTP {
         Ok(answer)
     }
 
-    /// Reads certificate fingerprint from file
-    pub fn get_cert_fingerprint_from_file() -> Result<String> {
-        let mount_path = utils::mount_proxmoxinst_part()?;
-        let cert_path = Path::new(mount_path.as_str()).join(CERT_FINGERPRINT_FILE);
-        match cert_path.try_exists() {
-            Ok(true) => {
-                info!("Found certifacte fingerprint file.");
-                Ok(fs::read_to_string(cert_path)?.trim().into())
-            }
-            _ => Err(Error::msg(format!(
-                "could not find cert fingerprint file expected at: {}",
-                cert_path.display()
-            ))),
-        }
-    }
-
     /// Fetches search domain from resolv.conf file
     fn get_search_domain() -> Result<String> {
         info!("Retrieving default search domain.");
diff --git a/proxmox-fetch-answer/src/main.rs b/proxmox-fetch-answer/src/main.rs
index 8c762e9..fe5d599 100644
--- a/proxmox-fetch-answer/src/main.rs
+++ b/proxmox-fetch-answer/src/main.rs
@@ -1,13 +1,15 @@
 use anyhow::{anyhow, Error, Result};
 use fetch_plugins::{http::FetchFromHTTP, partition::FetchFromPartition};
 use log::{error, info, LevelFilter};
-use proxmox_auto_installer::log::AutoInstLogger;
+use proxmox_auto_installer::{log::AutoInstLogger, utils::{AutoInstModes, AutoInstSettings}};
+use std::{fs, path::PathBuf};
 use std::io::Write;
 use std::process::{Command, ExitCode, Stdio};
 
 mod fetch_plugins;
 
 static LOGGER: AutoInstLogger = AutoInstLogger;
+static AUTOINST_MODE_FILE: &str = "/cdrom/autoinst-mode.toml";
 
 pub fn init_log() -> Result<()> {
     AutoInstLogger::init("/tmp/fetch_answer.log")?;
@@ -16,16 +18,40 @@ pub fn init_log() -> Result<()> {
         .map_err(|err| anyhow!(err))
 }
 
-fn fetch_answer() -> Result<String> {
-    match FetchFromPartition::get_answer() {
-        Ok(answer) => return Ok(answer),
-        Err(err) => info!("Fetching answer file from partition failed: {err}"),
-    }
-    match FetchFromHTTP::get_answer() {
-        Ok(answer) => return Ok(answer),
-        Err(err) => info!("Fetching answer file via HTTP failed: {err}"),
-    }
+fn fetch_answer(install_settings: &AutoInstSettings) -> Result<String> {
+    info!("Fetching answer file in mode {:?}:", &install_settings.mode);
+    match install_settings.mode {
+        AutoInstModes::Auto => {
+            match FetchFromPartition::get_answer() {
+                Ok(answer) => return Ok(answer),
+                Err(err) => info!("Fetching answer file from partition failed: {err}"),
+            }
+            match FetchFromHTTP::get_answer(install_settings) {
+                Ok(answer) => return Ok(answer),
+                Err(err) => info!("Fetching answer file via HTTP failed: {err}"),
+            }
+        },
+        AutoInstModes::Included => {
+            let answer_path = PathBuf::from("/cdrom/answer.toml");
+            match fetch_plugins::utils::get_answer_file(&answer_path) {
+                Ok(answer) => return Ok(answer),
+                Err(err) => info!("Fetching answer file from ISO failed: {err}"),
+            }
+        },
+        AutoInstModes::Partition => {
+            match FetchFromPartition::get_answer() {
+                Ok(answer) => return Ok(answer),
+                Err(err) => info!("Fetching answer file from partition failed: {err}"),
+            }
+        },
+        AutoInstModes::Http => {
+            match FetchFromHTTP::get_answer(install_settings) {
+                Ok(answer) => return Ok(answer),
+                Err(err) => info!("Fetching answer file via HTTP failed: {err}"),
+            }
+        },
 
+    }
     Err(Error::msg("Could not find any answer file!"))
 }
 
@@ -34,8 +60,22 @@ fn main() -> ExitCode {
         panic!("could not initialize logging: {err}");
     }
 
-    info!("Fetching answer file");
-    let answer = match fetch_answer() {
+    let raw_install_settings = match fs::read_to_string(AUTOINST_MODE_FILE) {
+        Ok(f) => f,
+        Err(err) => {
+            error!("Could not find needed file '{AUTOINST_MODE_FILE}' in live environment: {err}");
+            return ExitCode::FAILURE;
+        },
+    };
+    let install_settings: AutoInstSettings = match toml::from_str(raw_install_settings.as_str()) {
+        Ok(content) => content,
+        Err(err) => {
+            error!("Failed to parse '{AUTOINST_MODE_FILE}': {err}");
+            return ExitCode::FAILURE;
+        },
+    };
+
+    let answer = match fetch_answer(&install_settings) {
         Ok(answer) => answer,
         Err(err) => {
             error!("Aborting: {}", err);
-- 
2.39.2





More information about the pve-devel mailing list