* [PATCH proxmox 1/6] systemd: add support for machine-id generation
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
2026-04-10 10:02 ` [RFC proxmox 2/6] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
the plain machine-id should not be leaked to external systems, but libsystemd
provides helpers for deriving application-id based identifiers that are useful
for identifying a machine externally.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
to be used in the next patch by proxmox-subscription
proxmox-systemd/src/lib.rs | 2 +
proxmox-systemd/src/sd_id128.rs | 70 +++++++++++++++++++++++++++++++++
proxmox-systemd/src/sys.rs | 6 +++
3 files changed, 78 insertions(+)
create mode 100644 proxmox-systemd/src/sd_id128.rs
diff --git a/proxmox-systemd/src/lib.rs b/proxmox-systemd/src/lib.rs
index 456d88c3..f79c204c 100644
--- a/proxmox-systemd/src/lib.rs
+++ b/proxmox-systemd/src/lib.rs
@@ -7,3 +7,5 @@ pub use escape::{escape_unit, unescape_unit, unescape_unit_path, UnescapeError};
pub mod journal;
pub mod notify;
+
+pub mod sd_id128;
diff --git a/proxmox-systemd/src/sd_id128.rs b/proxmox-systemd/src/sd_id128.rs
new file mode 100644
index 00000000..a98a6663
--- /dev/null
+++ b/proxmox-systemd/src/sd_id128.rs
@@ -0,0 +1,70 @@
+use std::fmt;
+
+use crate::sys::{self, sd_id128_t};
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum SystemdId128Error {
+ InvalidAppId,
+ GenerationError,
+}
+
+impl std::error::Error for SystemdId128Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+
+impl fmt::Display for SystemdId128Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SystemdId128Error::InvalidAppId => f.write_str("Provided application ID is invalid."),
+ SystemdId128Error::GenerationError => {
+ f.write_str("Failed to generate machine-id based on application ID.")
+ }
+ }
+ }
+}
+
+pub fn get_app_specific_id(app_id: [u8; 16]) -> Result<[u8; 16], SystemdId128Error> {
+ let mut res = sd_id128_t { bytes: [0; 16] };
+
+ if app_id.iter().all(|b| *b == 0) {
+ return Err(SystemdId128Error::InvalidAppId);
+ }
+ unsafe {
+ sys::sd_id128_get_machine_app_specific(sd_id128_t { bytes: app_id }, &mut res);
+ }
+ if res.bytes.iter().all(|b| *b == 0) {
+ return Err(SystemdId128Error::GenerationError);
+ }
+ Ok(res.bytes)
+}
+
+#[test]
+fn test_invalid_app_id() {
+ let invalid = [0; 16];
+ let res = get_app_specific_id(invalid);
+ assert!(res.is_err());
+ assert_eq!(res, Err(SystemdId128Error::InvalidAppId));
+}
+
+#[test]
+fn test_valid_app_id() {
+ // no machine-id, no app-specific ID either..
+ if !std::path::Path::new("/etc/machine-id").exists() {
+ return;
+ }
+
+ // UUID generated with `systemd-id128 new` and converted from hex
+ let valid = 950247666410175165299169499632875718_u128.to_le_bytes();
+
+ let res = get_app_specific_id(valid);
+ assert!(res.is_ok());
+
+ let res2 = get_app_specific_id(valid);
+ assert!(res2.is_ok());
+
+ // cannot verify the expected result, since that depends on the machine the test runs on
+ // we can verify that two generations using the same machine and app-id give identical results
+ assert_eq!(res, res2);
+}
diff --git a/proxmox-systemd/src/sys.rs b/proxmox-systemd/src/sys.rs
index eabd44d1..ea2b6061 100644
--- a/proxmox-systemd/src/sys.rs
+++ b/proxmox-systemd/src/sys.rs
@@ -4,6 +4,11 @@ use std::os::fd::RawFd;
pub const LISTEN_FDS_START: RawFd = 3;
+#[repr(C)]
+pub struct sd_id128_t {
+ pub bytes: [u8; 16],
+}
+
#[link(name = "systemd")]
unsafe extern "C" {
pub fn sd_journal_stream_fd(
@@ -24,6 +29,7 @@ unsafe extern "C" {
unset_environment: c_int,
names: *mut *mut *mut c_char,
) -> c_int;
+ pub fn sd_id128_get_machine_app_specific(app_id: sd_id128_t, ret: *mut sd_id128_t) -> c_int;
}
pub fn check_call(ret: c_int) -> Result<c_int, io::Error> {
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC proxmox 2/6] proxmox-subscription: add new machine-id based serverid
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
2026-04-10 10:02 ` [PATCH proxmox 1/6] systemd: add support for machine-id generation Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
2026-04-10 10:02 ` [RFC proxmox-backup 3/6] subscription: adapt to multiple server ID variants Fabian Grünbichler
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
and adapt the code to allow querying all possible serverids, and accepting the
existing one if it matches one of the candidates.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
requires bumped librust-proxmox-systemd-dev
proxmox-subscription/Cargo.toml | 3 +-
proxmox-subscription/debian/control | 2 +
proxmox-subscription/src/lib.rs | 2 +-
proxmox-subscription/src/subscription_info.rs | 105 ++++++++++++++++--
4 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/proxmox-subscription/Cargo.toml b/proxmox-subscription/Cargo.toml
index dda31a69..4db7ca71 100644
--- a/proxmox-subscription/Cargo.toml
+++ b/proxmox-subscription/Cargo.toml
@@ -23,11 +23,12 @@ proxmox-base64 = { workspace = true, optional = true }
proxmox-http = { workspace = true, optional = true, features = ["client-trait", "http-helpers"] }
proxmox-serde.workspace = true
proxmox-sys = { workspace = true, optional = true }
+proxmox-systemd = { workspace = true, optional = true }
proxmox-time = { workspace = true, optional = true }
proxmox-schema = { workspace = true, features = ["api-macro"], optional = true }
[features]
default = ["impl"]
-impl = [ "dep:proxmox-base64", "dep:hex", "dep:openssl", "dep:proxmox-http", "dep:proxmox-sys", "dep:proxmox-time"]
+impl = [ "dep:proxmox-base64", "dep:hex", "dep:openssl", "dep:proxmox-http", "dep:proxmox-sys", "dep:proxmox-systemd", "dep:proxmox-time"]
api-types = ["dep:proxmox-schema"]
diff --git a/proxmox-subscription/debian/control b/proxmox-subscription/debian/control
index 5fbfcccb..5584d0c1 100644
--- a/proxmox-subscription/debian/control
+++ b/proxmox-subscription/debian/control
@@ -16,6 +16,7 @@ Build-Depends-Arch: cargo:native <!nocheck>,
librust-proxmox-serde-1+default-dev <!nocheck>,
librust-proxmox-serde-1+serde-json-dev <!nocheck>,
librust-proxmox-sys-1+default-dev <!nocheck>,
+ librust-proxmox-systemd-1+default-dev <!nocheck>,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~) <!nocheck>,
librust-regex-1+default-dev (>= 1.5-~~) <!nocheck>,
librust-serde-1+default-dev <!nocheck>,
@@ -78,6 +79,7 @@ Depends:
librust-proxmox-http-1+default-dev (>= 1.0.5-~~),
librust-proxmox-http-1+http-helpers-dev (>= 1.0.5-~~),
librust-proxmox-sys-1+default-dev,
+ librust-proxmox-systemd-1+default-dev,
librust-proxmox-time-2+default-dev (>= 2.1.0-~~)
Provides:
librust-proxmox-subscription+default-dev (= ${binary:Version}),
diff --git a/proxmox-subscription/src/lib.rs b/proxmox-subscription/src/lib.rs
index 2ed96903..eb1573e6 100644
--- a/proxmox-subscription/src/lib.rs
+++ b/proxmox-subscription/src/lib.rs
@@ -3,7 +3,7 @@
mod subscription_info;
#[cfg(feature = "impl")]
pub use subscription_info::{
- get_hardware_address, ProductType, SubscriptionInfo, SubscriptionStatus,
+ get_hardware_address_candidates, ProductType, ServerId, SubscriptionInfo, SubscriptionStatus,
};
#[cfg(not(feature = "impl"))]
diff --git a/proxmox-subscription/src/subscription_info.rs b/proxmox-subscription/src/subscription_info.rs
index f53b3ce3..f0daa51f 100644
--- a/proxmox-subscription/src/subscription_info.rs
+++ b/proxmox-subscription/src/subscription_info.rs
@@ -47,6 +47,43 @@ impl std::fmt::Display for SubscriptionStatus {
}
}
+/// Variant discriminator for `ServerId`
+#[derive(Clone, Copy, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
+pub enum ServerIdType {
+ /// Legacy variant tied to SSH host key, for backwards compatibility
+ SshMd5,
+ /// Tied to /etc/machine-id
+ MachineId,
+}
+
+impl Display for ServerIdType {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let txt = match self {
+ ServerIdType::SshMd5 => "SSH MD5",
+ ServerIdType::MachineId => "machine-id",
+ };
+ f.write_str(txt)
+ }
+}
+
+/// Serverid used to bind subscription key to system
+pub struct ServerId {
+ ty: ServerIdType,
+ id: String,
+}
+
+impl ServerId {
+ pub fn kind(&self) -> ServerIdType {
+ self.ty
+ }
+}
+
+impl Display for ServerId {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.id)
+ }
+}
+
#[cfg_attr(feature = "api-types", api())]
#[cfg_attr(feature = "api-types", derive(Updater))]
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
@@ -133,7 +170,7 @@ pub struct SubscriptionInfo {
}
#[cfg(feature = "impl")]
-pub use _impl::get_hardware_address;
+pub use _impl::get_hardware_address_candidates;
#[cfg(feature = "impl")]
pub(crate) use _impl::{md5sum, SHARED_KEY_DATA};
@@ -151,6 +188,10 @@ mod _impl {
use crate::sign::Verifier;
+ // Generated using `systemd-sd128 new`
+ pub(crate) const PMX_APPLICATION_ID: [u8; 16] =
+ 0x0e6b456a63fe4892997e9f42ebfaf980_u128.to_le_bytes();
+
pub(crate) const SHARED_KEY_DATA: &str = "kjfdlskfhiuewhfk947368";
/// How long the local key is valid for in between remote checks
@@ -245,7 +286,7 @@ mod _impl {
/// `status` is set to [SubscriptionStatus::Invalid] and `message` to a human-readable
/// message in case it does not.
pub fn check_server_id(&mut self) {
- match (self.serverid.as_ref(), get_hardware_address()) {
+ match (self.serverid.as_ref(), get_hardware_address_candidates()) {
(_, Err(err)) => {
self.status = SubscriptionStatus::Invalid;
self.message = Some(format!("Failed to obtain server ID - {err}."));
@@ -256,7 +297,9 @@ mod _impl {
self.message = Some("Missing server ID.".to_string());
self.signature = None;
}
- (Some(contained), Ok(expected)) if &expected != contained => {
+ (Some(contained), Ok(expected))
+ if !expected.iter().any(|serverid| serverid.id == *contained) =>
+ {
self.status = SubscriptionStatus::Invalid;
self.message = Some("Server ID mismatch.".to_string());
self.signature = None;
@@ -316,16 +359,54 @@ mod _impl {
hash(MessageDigest::md5(), data).map_err(Error::from)
}
+ fn get_hardware_address(ty: super::ServerIdType) -> Result<super::ServerId, Error> {
+ fn get_ssh_key() -> Result<Vec<u8>, Error> {
+ static FILENAME: &str = "/etc/ssh/ssh_host_rsa_key.pub";
+
+ proxmox_sys::fs::file_get_contents(FILENAME)
+ .map_err(|e| format_err!("Error getting host key - {}", e))
+ }
+
+ let id = match ty {
+ crate::subscription_info::ServerIdType::SshMd5 => {
+ let digest = md5sum(&get_ssh_key()?)
+ .map_err(|e| format_err!("Error digesting host key - {}", e))?;
+
+ hex::encode(digest).to_uppercase()
+ }
+ crate::subscription_info::ServerIdType::MachineId => {
+ let machine_id =
+ proxmox_systemd::sd_id128::get_app_specific_id(PMX_APPLICATION_ID)?;
+ hex::encode(machine_id).to_uppercase()
+ }
+ };
+ Ok(super::ServerId { ty, id })
+ }
+
/// Generate the current system's "server ID".
- pub fn get_hardware_address() -> Result<String, Error> {
- static FILENAME: &str = "/etc/ssh/ssh_host_rsa_key.pub";
-
- let contents = proxmox_sys::fs::file_get_contents(FILENAME)
- .map_err(|e| format_err!("Error getting host key - {}", e))?;
- let digest =
- md5sum(&contents).map_err(|e| format_err!("Error digesting host key - {}", e))?;
-
- Ok(hex::encode(digest).to_uppercase())
+ pub fn get_hardware_address_candidates() -> Result<Vec<super::ServerId>, Error> {
+ let mut res = Vec::new();
+ let mut errors = Vec::new();
+ let variants = [super::ServerIdType::MachineId, super::ServerIdType::SshMd5];
+ for ty in variants {
+ match get_hardware_address(ty) {
+ Ok(id) => res.push(id),
+ Err(err) => errors.push((ty, err)),
+ }
+ }
+ if res.is_empty() {
+ let error_strings: Vec<String> = errors
+ .into_iter()
+ .map(|(ty, err)| format!("{ty}: {err}"))
+ .collect();
+ let msg = if error_strings.is_empty() {
+ "unknown error".to_string()
+ } else {
+ error_strings.join(", ")
+ };
+ bail!("Failed to get any hardware address candidate: {msg}",);
+ }
+ Ok(res)
}
fn parse_next_due(value: &str) -> Result<i64, Error> {
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC proxmox-backup 3/6] subscription: adapt to multiple server ID variants
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
2026-04-10 10:02 ` [PATCH proxmox 1/6] systemd: add support for machine-id generation Fabian Grünbichler
2026-04-10 10:02 ` [RFC proxmox 2/6] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
2026-04-10 10:02 ` [RFC proxmox-perl-rs 4/6] common: subscription: expose server ID candidates Fabian Grünbichler
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
if there already is a subscription info, re-use the server ID contained within.
if not, use the first candidate.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
requires bumped librust-proxmox-subscription-dev
src/api2/node/subscription.rs | 38 ++++++++++++++++++++++++++---------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/src/api2/node/subscription.rs b/src/api2/node/subscription.rs
index 03d613ee2..6935e4e3f 100644
--- a/src/api2/node/subscription.rs
+++ b/src/api2/node/subscription.rs
@@ -102,7 +102,15 @@ pub fn check_subscription(force: bool) -> Result<(), Error> {
Ok(None) => return Ok(()),
};
- let server_id = proxmox_subscription::get_hardware_address()?;
+ let server_id = if let Some(existing) = info.serverid.as_ref() {
+ existing.to_owned()
+ } else {
+ proxmox_subscription::get_hardware_address_candidates()?
+ .first()
+ .ok_or_else(|| format_err!("Failed to generate serverid"))?
+ .to_string()
+ };
+
let key = if let Some(key) = info.key.as_ref() {
// always update apt auth if we have a key to ensure user can access enterprise repo
proxmox_subscription::files::update_apt_auth(
@@ -110,7 +118,7 @@ pub fn check_subscription(force: bool) -> Result<(), Error> {
apt_auth_file_opts(),
APT_AUTH_URL,
Some(key.to_owned()),
- Some(server_id.to_owned()),
+ Some(server_id.clone()),
)?;
key.to_owned()
} else {
@@ -124,6 +132,7 @@ pub fn check_subscription(force: bool) -> Result<(), Error> {
if !force && info.status == SubscriptionStatus::Active {
// will set to INVALID if last check too long ago
info.check_age(true);
+
if info.status == SubscriptionStatus::Active {
return Ok(());
}
@@ -156,13 +165,19 @@ pub fn get_subscription(
) {
Err(err) => bail!("could not read subscription status: {}", err),
Ok(Some(info)) => info,
- Ok(None) => SubscriptionInfo {
- status: SubscriptionStatus::NotFound,
- message: Some("There is no subscription key".into()),
- serverid: Some(proxmox_subscription::get_hardware_address()?),
- url: Some(PRODUCT_URL.into()),
- ..Default::default()
- },
+ Ok(None) => {
+ let candidates = proxmox_subscription::get_hardware_address_candidates()?;
+ let serverid = candidates
+ .first()
+ .ok_or_else(|| format_err!("Failed to generate serverid"))?;
+ SubscriptionInfo {
+ status: SubscriptionStatus::NotFound,
+ message: Some("There is no subscription key".into()),
+ serverid: Some(serverid.to_string()),
+ url: Some(PRODUCT_URL.into()),
+ ..Default::default()
+ }
+ }
};
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
@@ -200,7 +215,10 @@ pub fn get_subscription(
)]
/// Set a subscription key and check it.
pub fn set_subscription(key: String) -> Result<(), Error> {
- let server_id = proxmox_subscription::get_hardware_address()?;
+ let server_id = proxmox_subscription::get_hardware_address_candidates()?
+ .first()
+ .ok_or_else(|| format_err!("Failed to generate serverid"))?
+ .to_string();
check_and_write_subscription(key, server_id)
}
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC proxmox-perl-rs 4/6] common: subscription: expose server ID candidates
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
` (2 preceding siblings ...)
2026-04-10 10:02 ` [RFC proxmox-backup 3/6] subscription: adapt to multiple server ID variants Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
2026-04-10 10:02 ` [RFC manager 5/6] subscription: adapt to multiple server ID variants Fabian Grünbichler
2026-04-10 10:02 ` [RFC manager 6/6] api2tools: remove unused get_hwaddress Fabian Grünbichler
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
requires bumped librust-proxmox-subscription-dev
common/src/bindings/subscription.rs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/common/src/bindings/subscription.rs b/common/src/bindings/subscription.rs
index 313683c..286f4ac 100644
--- a/common/src/bindings/subscription.rs
+++ b/common/src/bindings/subscription.rs
@@ -126,4 +126,15 @@ pub mod proxmox_rs_subscription {
Ok(info)
}
+
+ /// Get server ID candidates.
+ ///
+ /// See [`proxmox_subscription::get_hardware_address_candidates()`]
+ #[export]
+ pub fn get_hardware_address_candidates() -> Result<Vec<(String, String)>, Error> {
+ Ok(proxmox_subscription::get_hardware_address_candidates()?
+ .into_iter()
+ .map(|id| (id.kind().to_string(), id.to_string()))
+ .collect())
+ }
}
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC manager 5/6] subscription: adapt to multiple server ID variants
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
` (3 preceding siblings ...)
2026-04-10 10:02 ` [RFC proxmox-perl-rs 4/6] common: subscription: expose server ID candidates Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
2026-04-10 10:02 ` [RFC manager 6/6] api2tools: remove unused get_hwaddress Fabian Grünbichler
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
if there already is a subscription info with ID, reuse it. if not, use the
first candidate.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
requires bumped libpve-rs-perl
PVE/API2/Subscription.pm | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/PVE/API2/Subscription.pm b/PVE/API2/Subscription.pm
index 59e7fe747..0a9e59810 100644
--- a/PVE/API2/Subscription.pm
+++ b/PVE/API2/Subscription.pm
@@ -58,7 +58,7 @@ sub check_key {
sub read_etc_subscription {
my $req_sockets = get_sockets();
- my $server_id = PVE::API2Tools::get_hwaddress();
+ my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
my $info = Proxmox::RS::Subscription::read_subscription($filename);
@@ -89,7 +89,7 @@ my sub cache_is_valid {
sub write_etc_subscription {
my ($info) = @_;
- my $server_id = PVE::API2Tools::get_hwaddress();
+ my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
mkdir "/etc/apt/auth.conf.d";
Proxmox::RS::Subscription::write_subscription(
$filename,
@@ -205,7 +205,8 @@ __PACKAGE__->register_method({
my $authuser = $rpcenv->get_user();
my $has_permission = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
- my $server_id = PVE::API2Tools::get_hwaddress();
+ my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
+ my $server_id = $server_id_candidates->[0]->[1];
my $url = "https://www.proxmox.com/en/proxmox-virtual-environment/pricing";
my $info = read_etc_subscription();
@@ -227,7 +228,13 @@ __PACKAGE__->register_method({
};
}
- $info->{serverid} = $server_id;
+ # none set yet
+ $info->{serverid} = $server_id if !defined($info->{serverid});
+
+ if ((grep { my $id = $_->[1]; $id eq $info->{serverid} } $server_id_candidates->@*) < 1) {
+ # mismatch, reset
+ $info->{serverid} = $server_id;
+ }
$info->{sockets} = get_sockets();
$info->{url} = $url;
@@ -264,8 +271,12 @@ __PACKAGE__->register_method({
my $info = read_etc_subscription();
return undef if !$info;
- my $server_id = PVE::API2Tools::get_hwaddress();
+ my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
my $key = $info->{key};
+ my $server_id = $info->{serverid} // $server_id_candidates->[0]->[1];
+ if ((grep { my $id = $_->[1]; $id eq $server_id } $server_id_candidates->@*) < 1) {
+ die "no matching server ID found\n";
+ }
die
"Updating offline key not possible - please remove and re-add subscription key to switch to online key.\n"
@@ -324,7 +335,10 @@ __PACKAGE__->register_method({
};
my $req_sockets = get_sockets();
- my $server_id = PVE::API2Tools::get_hwaddress();
+ my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
+ my $server_id = $server_id_candidates->[0]->[1];
+
+ die "Failed to generate server ID\n" if !$server_id;
check_key($key, $req_sockets);
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC manager 6/6] api2tools: remove unused get_hwaddress
2026-04-10 10:02 [RFC manager/proxmox{,-backup,-perl-rs} 0/6] adapt subscription handling to alternative server IDs Fabian Grünbichler
` (4 preceding siblings ...)
2026-04-10 10:02 ` [RFC manager 5/6] subscription: adapt to multiple server ID variants Fabian Grünbichler
@ 2026-04-10 10:02 ` Fabian Grünbichler
5 siblings, 0 replies; 7+ messages in thread
From: Fabian Grünbichler @ 2026-04-10 10:02 UTC (permalink / raw)
To: pve-devel
it is now only implemented in the proxmox-subscription crate.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
PVE/API2Tools.pm | 23 -----------------------
1 file changed, 23 deletions(-)
diff --git a/PVE/API2Tools.pm b/PVE/API2Tools.pm
index a1a6f39e1..bca21aa5f 100644
--- a/PVE/API2Tools.pm
+++ b/PVE/API2Tools.pm
@@ -18,29 +18,6 @@ use PVE::SafeSyslog;
use PVE::Storage::Plugin;
use PVE::Tools;
-my $hwaddress;
-my $hwaddress_st = {};
-
-sub get_hwaddress {
- my $fn = '/etc/ssh/ssh_host_rsa_key.pub';
- my $st = stat($fn);
-
- if (
- defined($hwaddress)
- && $hwaddress_st->{mtime} == $st->mtime
- && $hwaddress_st->{ino} == $st->ino
- && $hwaddress_st->{dev} == $st->dev
- ) {
- return $hwaddress;
- }
-
- my $sshkey = PVE::Tools::file_get_contents($fn);
- $hwaddress = uc(md5_hex($sshkey));
- $hwaddress_st->@{ 'mtime', 'ino', 'dev' } = ($st->mtime, $st->ino, $st->dev);
-
- return $hwaddress;
-}
-
# each rrd key for a resource will only exist once. The key format might be different though. Therefore return on first hit
sub get_rrd_key {
my ($rrd, $type, $id) = @_;
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread