all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs
@ 2026-05-07 11:59 Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox 1/8] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 UTC (permalink / raw)
  To: pve-devel

instead of only supporting one variant of server IDs, get a list of possible
candidates via the proxmox-subscription crate. if a subscription is already
configured, the matching server ID will be reused to avoid reissuing.

v1:
- drop proxmox-systemd part, already applied
- add PMG changes
- add PDM changes
- add POM changes
- rebase

order of bumping:
- proxmox-subscription (breaks PBS/PDM/POM)

- pve-rs/pmg-rs (needs proxmox-subscription)
- pve-manager (needs pve-rs)
- pmg-api (needs pmg-rs)

- pbs (needs proxmox-subscription)

- pdm (needs proxmox-subscription)

- pom (needs proxmox-subscription)

tested PBS/PVE, additional testing of PMG/POM/PDM would be highly
appreciated.

sending to pve-devel, since it's our main list - this of course is a
cross-product patch series ;)


proxmox:

Fabian Grünbichler (1):
  proxmox-subscription: add new machine-id based serverid

 proxmox-subscription/Cargo.toml               |   3 +-
 proxmox-subscription/src/lib.rs               |   2 +-
 proxmox-subscription/src/subscription_info.rs | 105 ++++++++++++++++--
 3 files changed, 96 insertions(+), 14 deletions(-)


proxmox-backup:

Fabian Grünbichler (1):
  subscription: adapt to multiple server ID variants

 src/api2/node/subscription.rs | 38 ++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 10 deletions(-)


proxmox-perl-rs:

Fabian Grünbichler (1):
  common: subscription: expose server ID candidates

 common/src/bindings/subscription.rs | 11 +++++++++++
 1 file changed, 11 insertions(+)


pve-manager:

Fabian Grünbichler (2):
  subscription: adapt to multiple server ID variants
  api2tools: remove unused get_hwaddress

 PVE/API2/Subscription.pm | 26 ++++++++++++++++++++------
 PVE/API2Tools.pm         | 23 -----------------------
 2 files changed, 20 insertions(+), 29 deletions(-)


pmg-api:

Fabian Grünbichler (2):
  subscription: adapt to multiple server ID variants
  utils: drop now unused get_hwaddress

 src/PMG/API2/Subscription.pm | 27 +++++++++++++++++++++------
 src/PMG/Utils.pm             | 23 -----------------------
 2 files changed, 21 insertions(+), 29 deletions(-)


proxmox-offline-mirror:

Fabian Grünbichler (1):
  subscription handling: adapt to multiple server ID candidates

 src/bin/proxmox-offline-mirror-helper.rs      | 36 +++++++++++++++----
 src/bin/proxmox-offline-mirror.rs             |  6 +++-
 .../subscription.rs                           |  5 ++-
 3 files changed, 39 insertions(+), 8 deletions(-)


Summary over all repositories:
  12 files changed, 215 insertions(+), 90 deletions(-)

-- 
Generated by murpp 0.11.0




^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH proxmox 1/8] proxmox-subscription: add new machine-id based serverid
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox-backup 2/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 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/src/lib.rs               |   2 +-
 proxmox-subscription/src/subscription_info.rs | 105 ++++++++++++++++--
 3 files changed, 96 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/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 related	[flat|nested] 9+ messages in thread

* [PATCH proxmox-backup 2/8] subscription: adapt to multiple server ID variants
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox 1/8] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox-perl-rs 3/8] common: subscription: expose server ID candidates Fabian Grünbichler
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 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 related	[flat|nested] 9+ messages in thread

* [PATCH proxmox-perl-rs 3/8] common: subscription: expose server ID candidates
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox 1/8] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox-backup 2/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH manager 4/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 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 related	[flat|nested] 9+ messages in thread

* [PATCH manager 4/8] subscription: adapt to multiple server ID variants
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
                   ` (2 preceding siblings ...)
  2026-05-07 11:59 ` [PATCH proxmox-perl-rs 3/8] common: subscription: expose server ID candidates Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH manager 5/8] api2tools: remove unused get_hwaddress Fabian Grünbichler
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 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 related	[flat|nested] 9+ messages in thread

* [PATCH manager 5/8] api2tools: remove unused get_hwaddress
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
                   ` (3 preceding siblings ...)
  2026-05-07 11:59 ` [PATCH manager 4/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH pmg-api 6/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 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 related	[flat|nested] 9+ messages in thread

* [PATCH pmg-api 6/8] subscription: adapt to multiple server ID variants
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
                   ` (4 preceding siblings ...)
  2026-05-07 11:59 ` [PATCH manager 5/8] api2tools: remove unused get_hwaddress Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH pmg-api 7/8] utils: drop now unused get_hwaddress Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox-offline-mirror 8/8] subscription handling: adapt to multiple server ID candidates Fabian Grünbichler
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 UTC (permalink / raw)
  To: pve-devel

if there already is a subscription info with ID, reusite. if not, use the first
candidate.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---

Notes:
    requires bumped pmg-rs with proxmox-subscription changes included

 src/PMG/API2/Subscription.pm | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/PMG/API2/Subscription.pm b/src/PMG/API2/Subscription.pm
index edc1d18..3004b90 100644
--- a/src/PMG/API2/Subscription.pm
+++ b/src/PMG/API2/Subscription.pm
@@ -33,7 +33,7 @@ sub parse_key {
 }
 
 sub read_etc_subscription {
-    my $server_id = PMG::Utils::get_hwaddress();
+    my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
 
     my $info = Proxmox::RS::Subscription::read_subscription($filename);
     return $info if !$info;
@@ -50,7 +50,7 @@ sub read_etc_subscription {
 sub write_etc_subscription {
     my ($info) = @_;
 
-    my $server_id = PMG::Utils::get_hwaddress();
+    my $server_id_candidates = Proxmox::RS::Subscription::get_hardware_address_candidates();
 
     Proxmox::RS::Subscription::write_subscription(
         $filename,
@@ -77,7 +77,8 @@ __PACKAGE__->register_method({
     code => sub {
         my ($param) = @_;
 
-        my $server_id = PMG::Utils::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/proxmox-mail-gateway/pricing";
         my $info = read_etc_subscription();
         if (!$info) {
@@ -89,7 +90,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->{url} = $url;
 
         return $info;
@@ -124,8 +131,13 @@ __PACKAGE__->register_method({
         my $info = read_etc_subscription();
         return undef if !$info;
 
-        my $server_id = PMG::Utils::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";
+        }
+
 
         # key has been recently checked
         return undef
@@ -179,7 +191,10 @@ __PACKAGE__->register_method({
             checktime => time(),
         };
 
-        my $server_id = PMG::Utils::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;
 
         write_etc_subscription($info);
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH pmg-api 7/8] utils: drop now unused get_hwaddress
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
                   ` (5 preceding siblings ...)
  2026-05-07 11:59 ` [PATCH pmg-api 6/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  2026-05-07 11:59 ` [PATCH proxmox-offline-mirror 8/8] subscription handling: adapt to multiple server ID candidates Fabian Grünbichler
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 UTC (permalink / raw)
  To: pve-devel

server ID generation is now only implemented in the proxmox-subscription crate.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
 src/PMG/Utils.pm | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/src/PMG/Utils.pm b/src/PMG/Utils.pm
index b9c645e..8fd97fc 100644
--- a/src/PMG/Utils.pm
+++ b/src/PMG/Utils.pm
@@ -1482,29 +1482,6 @@ sub scan_journal_for_rbl_rejects {
     return ($rbl_count, $pregreet_count);
 }
 
-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(Digest::MD5::md5_hex($sshkey));
-    $hwaddress_st->@{ 'mtime', 'ino', 'dev' } = ($st->mtime, $st->ino, $st->dev);
-
-    return $hwaddress;
-}
-
 my $default_locale = "en_US.UTF-8 UTF-8";
 
 sub cond_add_default_locale {
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH proxmox-offline-mirror 8/8] subscription handling: adapt to multiple server ID candidates
  2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
                   ` (6 preceding siblings ...)
  2026-05-07 11:59 ` [PATCH pmg-api 7/8] utils: drop now unused get_hwaddress Fabian Grünbichler
@ 2026-05-07 11:59 ` Fabian Grünbichler
  7 siblings, 0 replies; 9+ messages in thread
From: Fabian Grünbichler @ 2026-05-07 11:59 UTC (permalink / raw)
  To: pve-devel

when setting a new POM key, pick the first candidate. when a key is already
configured, keep re-using its server ID.

for managed offline keys, set any matching key.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---

requires bumped proxmox-subscription

 src/bin/proxmox-offline-mirror-helper.rs      | 36 +++++++++++++++----
 src/bin/proxmox-offline-mirror.rs             |  6 +++-
 .../subscription.rs                           |  5 ++-
 3 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/src/bin/proxmox-offline-mirror-helper.rs b/src/bin/proxmox-offline-mirror-helper.rs
index 0c40338..dff41c5 100644
--- a/src/bin/proxmox-offline-mirror-helper.rs
+++ b/src/bin/proxmox-offline-mirror-helper.rs
@@ -252,11 +252,20 @@ async fn setup(_param: Value) -> Result<(), Error> {
                 println!();
             }
             Action::UpdateOfflineSubscription => {
-                let server_id = proxmox_subscription::get_hardware_address()?;
+                let server_id_candidates = proxmox_subscription::get_hardware_address_candidates()?
+                    .into_iter()
+                    .map(|server_id| server_id.to_string())
+                    .collect::<Vec<_>>();
                 let mut subscriptions: Vec<((ProductType, &SubscriptionInfo), &str)> = state
                     .subscriptions
                     .iter()
-                    .filter(|sub| sub.serverid.as_ref() == Some(&server_id))
+                    .filter(|sub| {
+                        if let Some(server_id) = sub.serverid.as_ref() {
+                            server_id_candidates.contains(server_id)
+                        } else {
+                            false
+                        }
+                    })
                     .filter_map(|sub| {
                         sub.get_product_type()
                             .ok()
@@ -277,7 +286,10 @@ async fn setup(_param: Value) -> Result<(), Error> {
                 });
 
                 if subscriptions.is_empty() {
-                    println!("No matching subscription key found for server ID '{server_id}'");
+                    println!("No matching subscription key found for server ID candidates:");
+                    for server_id in server_id_candidates {
+                        println!("- {server_id}");
+                    }
                 } else {
                     let (product, info) =
                         read_selection_from_tty("Select key", &subscriptions, None)?;
@@ -327,12 +339,21 @@ async fn setup_offline_key(
         epoch_to_rfc3339_utc(state.last_sync)?
     );
 
-    let server_id = proxmox_subscription::get_hardware_address()?;
+    let server_id_candidates = proxmox_subscription::get_hardware_address_candidates()?
+        .into_iter()
+        .map(|server_id| server_id.to_string())
+        .collect::<Vec<_>>();
 
     let subscriptions: HashMap<ProductType, &SubscriptionInfo> = state
         .subscriptions
         .iter()
-        .filter(|sub| sub.serverid.as_ref() == Some(&server_id))
+        .filter(|sub| {
+            if let Some(server_id) = sub.serverid.as_ref() {
+                server_id_candidates.contains(server_id)
+            } else {
+                false
+            }
+        })
         .filter_map(|sub| sub.get_product_type().ok().map(|prod| (prod, sub)))
         .filter(|(found_product, _)| {
             (product.is_none() || Some(found_product) == product.as_ref())
@@ -350,7 +371,10 @@ async fn setup_offline_key(
         });
 
     if subscriptions.is_empty() {
-        bail!("No matching subscription key found for server ID '{server_id}'");
+        bail!(
+            "No matching subscription key found for server ID candidates:\n{}",
+            server_id_candidates.join(", ")
+        );
     }
 
     for (product, subscription) in subscriptions {
diff --git a/src/bin/proxmox-offline-mirror.rs b/src/bin/proxmox-offline-mirror.rs
index 1a752d2..750bedb 100644
--- a/src/bin/proxmox-offline-mirror.rs
+++ b/src/bin/proxmox-offline-mirror.rs
@@ -703,7 +703,11 @@ fn action_add_key(config: &SectionConfigData) -> Result<SubscriptionKey, Error>
     }
 
     let server_id = if product == &ProductType::Pom {
-        let server_id = proxmox_subscription::get_hardware_address()?;
+        let server_id_candidates = proxmox_subscription::get_hardware_address_candidates()?;
+        let server_id = server_id_candidates
+            .first()
+            .ok_or_else(|| format_err!("Failed to generate server ID for this system."))?
+            .to_string();
         println!("Server ID of this system is '{server_id}'");
         server_id
     } else {
diff --git a/src/bin/proxmox_offline_mirror_cmds/subscription.rs b/src/bin/proxmox_offline_mirror_cmds/subscription.rs
index eea7f9a..b486278 100644
--- a/src/bin/proxmox_offline_mirror_cmds/subscription.rs
+++ b/src/bin/proxmox_offline_mirror_cmds/subscription.rs
@@ -220,7 +220,10 @@ async fn add_mirror_key(config: Option<String>, key: String, _param: Value) -> R
         );
     }
 
-    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 server ID."))?
+        .to_string();
     let mut data = SubscriptionKey {
         key,
         server_id,
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-05-07 12:01 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 11:59 [PATCH manager/pmg-api/proxmox{,-backup,-perl-rs,-offline-mirror} 0/8] adapt subscription handling to alternative server IDs Fabian Grünbichler
2026-05-07 11:59 ` [PATCH proxmox 1/8] proxmox-subscription: add new machine-id based serverid Fabian Grünbichler
2026-05-07 11:59 ` [PATCH proxmox-backup 2/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
2026-05-07 11:59 ` [PATCH proxmox-perl-rs 3/8] common: subscription: expose server ID candidates Fabian Grünbichler
2026-05-07 11:59 ` [PATCH manager 4/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
2026-05-07 11:59 ` [PATCH manager 5/8] api2tools: remove unused get_hwaddress Fabian Grünbichler
2026-05-07 11:59 ` [PATCH pmg-api 6/8] subscription: adapt to multiple server ID variants Fabian Grünbichler
2026-05-07 11:59 ` [PATCH pmg-api 7/8] utils: drop now unused get_hwaddress Fabian Grünbichler
2026-05-07 11:59 ` [PATCH proxmox-offline-mirror 8/8] subscription handling: adapt to multiple server ID candidates Fabian Grünbichler

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.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal