public inbox for pve-devel@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 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