]> git.proxmox.com Git - proxmox-backup.git/commitdiff
manager: add 'subscription set-offline-key' command
authorFabian Grünbichler <f.gruenbichler@proxmox.com>
Wed, 29 Jun 2022 11:40:10 +0000 (13:40 +0200)
committerFabian Grünbichler <f.gruenbichler@proxmox.com>
Mon, 25 Jul 2022 08:03:10 +0000 (10:03 +0200)
and disallow updating offline subscription keys via the regular
check/update code path.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
src/api2/node/subscription.rs
src/bin/proxmox_backup_manager/subscription.rs

index cba532b9470a7529902cdea50e692630a40d7c65..a1ac356f982eed35a8452ed22a6ecc512b686008 100644 (file)
@@ -21,7 +21,7 @@ const PRODUCT_URL: &str = "https://www.proxmox.com/en/proxmox-backup-server/pric
 const APT_AUTH_FN: &str = "/etc/apt/auth.conf.d/pbs.conf";
 const APT_AUTH_URL: &str = "enterprise.proxmox.com/debian/pbs";
 
-fn subscription_file_opts() -> Result<CreateOptions, Error> {
+pub fn subscription_file_opts() -> Result<CreateOptions, Error> {
     let backup_user = pbs_config::backup_user()?;
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
     Ok(CreateOptions::new()
@@ -35,7 +35,7 @@ fn apt_auth_file_opts() -> CreateOptions {
     CreateOptions::new().perm(mode).owner(nix::unistd::ROOT)
 }
 
-pub(crate) fn subscription_signature_key() -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
+pub fn subscription_signature_key() -> Result<openssl::pkey::PKey<openssl::pkey::Public>, Error> {
     let key = file_get_contents(PROXMOX_BACKUP_SUBSCRIPTION_SIGNATURE_KEY_FN)?;
     openssl::pkey::PKey::public_key_from_pem(&key).map_err(|err| {
         format_err!(
@@ -127,6 +127,10 @@ pub fn check_subscription(force: bool) -> Result<(), Error> {
         String::new()
     };
 
+    if info.is_signed() {
+        bail!("Updating offline key not possible - please remove and re-add subscription key to switch to online key.");
+    }
+
     if !force && info.status == SubscriptionStatus::Active {
         // will set to INVALID if last check too long ago
         info.check_age(true);
index a0adab3479d4d1ea70503239c188051704b950cd..31423c3fe49f39ef05ac67acf447438a6e02e50f 100644 (file)
@@ -1,10 +1,16 @@
-use anyhow::Error;
+use anyhow::{bail, Error};
 use serde_json::Value;
 
 use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
 use proxmox_schema::api;
+use proxmox_subscription::SubscriptionInfo;
 
-use proxmox_backup::api2;
+use proxmox_backup::api2::{
+    self,
+    node::subscription::{subscription_file_opts, subscription_signature_key},
+};
+
+use pbs_buildcfg::PROXMOX_BACKUP_SUBSCRIPTION_FN;
 
 #[api(
     input: {
@@ -32,6 +38,33 @@ fn get(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
     Ok(Value::Null)
 }
 
+#[api(
+    input: {
+        properties: {
+            "data": {
+                type: String,
+                description: "base64-encoded signed subscription info"
+            },
+        }
+    }
+)]
+/// (Internal use only!) Set a signed subscription info blob as offline key
+pub fn set_offline_subscription_key(data: String) -> Result<(), Error> {
+    let mut info: SubscriptionInfo = serde_json::from_slice(&base64::decode(data)?)?;
+    if !info.is_signed() {
+        bail!("Offline subscription key must be signed!");
+    }
+    info.check_signature(&subscription_signature_key()?);
+    info.check_age(false);
+    info.check_server_id();
+    proxmox_subscription::files::write_subscription(
+        PROXMOX_BACKUP_SUBSCRIPTION_FN,
+        subscription_file_opts()?,
+        &info,
+    )?;
+    Ok(())
+}
+
 pub fn subscription_commands() -> CommandLineInterface {
     let cmd_def = CliCommandMap::new()
         .insert("get", CliCommand::new(&API_METHOD_GET))
@@ -41,6 +74,10 @@ pub fn subscription_commands() -> CommandLineInterface {
                 .fixed_param("node", "localhost".into())
                 .arg_param(&["key"]),
         )
+        .insert(
+            "set-offline-key",
+            CliCommand::new(&API_METHOD_SET_OFFLINE_SUBSCRIPTION_KEY).arg_param(&["data"]),
+        )
         .insert(
             "update",
             CliCommand::new(&api2::node::subscription::API_METHOD_CHECK_SUBSCRIPTION)