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
next prev parent reply other threads:[~2024-11-13 13:59 UTC|newest]
Thread overview: 16+ 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-15 9:34 ` Christoph Heiss
2024-11-15 9:49 ` Thomas Lamprecht
2024-11-15 13:34 ` Christoph Heiss
2024-11-15 13:39 ` Thomas Lamprecht
2024-11-15 13:43 ` Christoph Heiss
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-15 9:25 ` Christoph Heiss
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.