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] [RFC PATCH installer 4/5] fix #5579: auto-installer: add optional first-boot hook script
Date: Wed, 13 Nov 2024 14:59:06 +0100	[thread overview]
Message-ID: <20241113135908.1622968-5-c.heiss@proxmox.com> (raw)
In-Reply-To: <20241113135908.1622968-1-c.heiss@proxmox.com>

Users can specifying an optional file - either fetched from an URL or
backed into the ISO - to execute on the first boot after the
installation, using the 'proxmox-first-boot' oneshot service.

Essentially adds an (optional) `[first-boot]` section to the answer
file. If specified, the `source` key must be at least set, which gives
the location of the hook script.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 proxmox-auto-installer/Cargo.toml             |  2 +-
 proxmox-auto-installer/src/answer.rs          | 27 ++++++++++++
 .../src/bin/proxmox-auto-installer.rs         | 42 +++++++++++++++++--
 proxmox-auto-installer/src/utils.rs           | 15 ++++++-
 4 files changed, 80 insertions(+), 6 deletions(-)

diff --git a/proxmox-auto-installer/Cargo.toml b/proxmox-auto-installer/Cargo.toml
index 21ed538..7e3d90c 100644
--- a/proxmox-auto-installer/Cargo.toml
+++ b/proxmox-auto-installer/Cargo.toml
@@ -13,7 +13,7 @@ homepage = "https://www.proxmox.com"
 [dependencies]
 anyhow.workspace = true
 log.workspace = true
-proxmox-installer-common.workspace = true
+proxmox-installer-common = { workspace = true, features = ["http"] }
 serde = { workspace = true, features = ["derive"] }
 serde_json.workspace = true
 serde_plain.workspace = true
diff --git a/proxmox-auto-installer/src/answer.rs b/proxmox-auto-installer/src/answer.rs
index c23f1f3..23d6878 100644
--- a/proxmox-auto-installer/src/answer.rs
+++ b/proxmox-auto-installer/src/answer.rs
@@ -22,6 +22,8 @@ pub struct Answer {
     pub disks: Disks,
     #[serde(default)]
     pub posthook: Option<PostNotificationHookInfo>,
+    #[serde(default)]
+    pub first_boot: Option<FirstBootHookInfo>,
 }
 
 impl Answer {
@@ -62,6 +64,31 @@ pub struct PostNotificationHookInfo {
     pub cert_fingerprint: Option<String>,
 }
 
+/// Possible sources for the optional first-boot hook script/executable file.
+#[derive(Clone, Deserialize, Debug, PartialEq)]
+#[serde(rename_all = "kebab-case", deny_unknown_fields)]
+pub enum FirstBootHookSourceMode {
+    /// Fetch the executable file from an URL, specified in the parent.
+    FromUrl,
+    /// The executable file has been baked into the ISO at a known location,
+    /// and should be retrieved from there.
+    FromIso,
+}
+
+/// Describes from where to fetch the first-boot hook script, either being baked into the ISO or
+/// from a URL.
+#[derive(Clone, Deserialize, Debug)]
+#[serde(rename_all = "kebab-case", deny_unknown_fields)]
+pub struct FirstBootHookInfo {
+    /// Mode how to retrieve the first-boot executable file, either from an URL or from the ISO if
+    /// it has been baked-in.
+    pub source: FirstBootHookSourceMode,
+    /// Retrieve the post-install script from a URL, if source == "from-url".
+    pub url: Option<String>,
+    /// SHA256 cert fingerprint if certificate pinning should be used, if source == "from-url".
+    pub cert_fingerprint: Option<String>,
+}
+
 #[derive(Clone, Deserialize, Debug, Default, PartialEq)]
 #[serde(deny_unknown_fields)]
 enum NetworkConfigMode {
diff --git a/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs b/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs
index ea45c29..9d8a2e5 100644
--- a/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs
+++ b/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs
@@ -1,18 +1,22 @@
 use anyhow::{bail, format_err, Result};
 use log::{error, info, LevelFilter};
 use std::{
-    env,
+    env, fs,
     io::{BufRead, BufReader, Write},
     path::PathBuf,
     process::ExitCode,
 };
 
-use proxmox_installer_common::setup::{
-    installer_setup, read_json, spawn_low_level_installer, LocaleInfo, RuntimeInfo, SetupInfo,
+use proxmox_installer_common::{
+    http,
+    setup::{
+        installer_setup, read_json, spawn_low_level_installer, LocaleInfo, RuntimeInfo, SetupInfo,
+    },
+    FIRST_BOOT_EXEC_NAME, RUNTIME_DIR,
 };
 
 use proxmox_auto_installer::{
-    answer::Answer,
+    answer::{Answer, FirstBootHookInfo, FirstBootSourceMode},
     log::AutoInstLogger,
     udevinfo::UdevInfo,
     utils::{parse_answer, LowLevelMessage},
@@ -27,6 +31,31 @@ pub fn init_log() -> Result<()> {
         .map_err(|err| format_err!(err))
 }
 
+fn setup_first_boot_executable(first_boot: &FirstBootHookInfo) -> Result<()> {
+    let content = match first_boot.source {
+        FirstBootSourceMode::FromUrl => {
+            if let Some(url) = &first_boot.url {
+                info!("Fetching first-boot hook from {url} ..");
+                Some(http::get(url, first_boot.cert_fingerprint.as_deref())?)
+            } else {
+                bail!("first-boot hook source set to URL, but none specified!");
+            }
+        }
+        FirstBootSourceMode::FromIso => Some(fs::read_to_string(format!(
+            "/cdrom/{FIRST_BOOT_EXEC_NAME}"
+        ))?),
+    };
+
+    if let Some(content) = content {
+        Ok(fs::write(
+            format!("/{RUNTIME_DIR}/{FIRST_BOOT_EXEC_NAME}"),
+            content,
+        )?)
+    } else {
+        Ok(())
+    }
+}
+
 fn auto_installer_setup(in_test_mode: bool) -> Result<(Answer, UdevInfo)> {
     let base_path = if in_test_mode { "./testdir" } else { "/" };
     let mut path = PathBuf::from(base_path);
@@ -43,6 +72,11 @@ fn auto_installer_setup(in_test_mode: bool) -> Result<(Answer, UdevInfo)> {
     };
 
     let answer = Answer::try_from_reader(std::io::stdin().lock())?;
+
+    if let Some(first_boot) = &answer.first_boot {
+        setup_first_boot_executable(first_boot)?;
+    }
+
     Ok((answer, udev_info))
 }
 
diff --git a/proxmox-auto-installer/src/utils.rs b/proxmox-auto-installer/src/utils.rs
index 83f3f12..25f537d 100644
--- a/proxmox-auto-installer/src/utils.rs
+++ b/proxmox-auto-installer/src/utils.rs
@@ -5,7 +5,7 @@ use log::info;
 use std::{collections::BTreeMap, process::Command};
 
 use crate::{
-    answer::{self, Answer},
+    answer::{self, Answer, FirstBootSourceMode},
     udevinfo::UdevInfo,
 };
 use proxmox_installer_common::{
@@ -320,6 +320,18 @@ fn verify_email_and_root_password_settings(answer: &Answer) -> Result<()> {
     }
 }
 
+fn verify_first_boot_settings(answer: &Answer) -> Result<()> {
+    info!("Verifying first boot settings");
+
+    if let Some(first_boot) = &answer.first_boot {
+        if first_boot.source == FirstBootSourceMode::FromUrl && first_boot.url.is_none() {
+            bail!("first-boot executable source set to URL, but none specified!");
+        }
+    }
+
+    Ok(())
+}
+
 pub fn parse_answer(
     answer: &Answer,
     udev_info: &UdevInfo,
@@ -336,6 +348,7 @@ pub fn parse_answer(
 
     verify_locale_settings(answer, locales)?;
     verify_email_and_root_password_settings(answer)?;
+    verify_first_boot_settings(answer)?;
 
     let mut config = InstallConfig {
         autoreboot: 1_usize,
-- 
2.47.0



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


  parent reply	other threads:[~2024-11-13 13:59 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-13 13:59 [pve-devel] [RFC PATCH installer 0/5] fix #5579: allow specifying optional first-boot script Christoph Heiss
2024-11-13 13:59 ` [pve-devel] [RFC PATCH installer 1/5] common: add function for issuing HTTP GET requests Christoph Heiss
2024-11-14 20:22   ` [pve-devel] applied: " Thomas Lamprecht
2024-11-13 13:59 ` [pve-devel] [RFC PATCH installer 2/5] fix #5579: first-boot: add initial service packaging Christoph Heiss
2024-11-14 20:23   ` Thomas Lamprecht
2024-11-13 13:59 ` [pve-devel] [RFC PATCH installer 3/5] fix #5579: auto-install-assistant: enable baking in first-boot script Christoph Heiss
2024-11-13 13:59 ` Christoph Heiss [this message]
2024-11-14 20:33   ` [pve-devel] [RFC PATCH installer 4/5] fix #5579: auto-installer: add optional first-boot hook script Thomas Lamprecht
2024-11-14 21:02   ` Thomas Lamprecht
2024-11-13 13:59 ` [pve-devel] [RFC PATCH installer 5/5] fix #5579: install: copy over `proxmox-first-boot` script if present 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=20241113135908.1622968-5-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