[pbs-devel] [PATCH proxmox-backup v7] fix #3854 paperkey import to proxmox-tape

Markus Frank m.frank at proxmox.com
Thu Mar 24 12:49:30 CET 2022


added a parameter to the cli for importing tape key via a json-parameter or
via reading a exported paperkey-file or json-file.
For this i also added a backupkey parameter to the api, but here it only
accepts json.

The cli interprets the parameter first as json-string, then json-file
and last as paperkey-file.

functionality:
proxmox-tape key paperkey [fingerprint of existing key] > paperkey.backup
proxmox-tape key restore --backupkey paperkey.backup # key from line above
proxmox-tape key restore --backupkey paperkey.json # only the json
proxmox-tape key restore --backupkey '{"kdf": {"Scrypt": ...' # json as string

for importing the key as paperkey-file it is irrelevant, if the paperkey got exported as html
or txt.

Signed-off-by: Markus Frank <m.frank at proxmox.com>
---
Comments:
 * I would disallow giving this functions both a drivename and a
 backupkey, so that there is no confusion about the behavour.

version 7:
 * moved to restore
 * added json-parameter to cli

version 6:
 * api: removed identical tuple-matching arms and returned to checking parameter-errors
 earlier and adding hint if set afterwards

version 5:
 * removed extract_text_between. I would say we can move it to a
 public function if another component does a similar text-extraction.
 * simplified text extraction

version 4:
 * ParameterError::from to param_bail!
 * when hint and paperkey-file used at the same time, old hint get overwritten
 * added text in pbs-tools with function extract_text_between

version 3:
 * ParameterError with method ParameterError::from
 * changed --paperkey_file to --paperkey-file

version 2:
 * added format_err! and ParameterError
 * changed a few "ifs" to "match"

 src/api2/tape/drive.rs                 | 65 +++++++++++++++++++-------
 src/bin/proxmox_tape/encryption_key.rs | 59 +++++++++++++++++++++--
 2 files changed, 102 insertions(+), 22 deletions(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index 526743f6..c79688ff 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -10,7 +10,7 @@ use proxmox_sys::sortable;
 use proxmox_router::{
     list_subdirs_api_method, Permission, Router, RpcEnvironment, RpcEnvironmentType, SubdirMap,
 };
-use proxmox_schema::api;
+use proxmox_schema::{api, param_bail};
 use proxmox_section_config::SectionConfigData;
 use proxmox_uuid::Uuid;
 use proxmox_sys::{task_log, task_warn};
@@ -24,6 +24,8 @@ use pbs_api_types::{
 use pbs_api_types::{PRIV_TAPE_AUDIT, PRIV_TAPE_READ, PRIV_TAPE_WRITE};
 
 use pbs_config::CachedUserInfo;
+use pbs_config::key_config::KeyConfig;
+use pbs_config::tape_encryption_keys::insert_key;
 use pbs_tape::{
     BlockReadError,
     sg_tape::tape_alert_flags_critical,
@@ -607,10 +609,18 @@ fn write_media_label(
         properties: {
             drive: {
                 schema: DRIVE_NAME_SCHEMA,
+                optional: true,
             },
             password: {
                 description: "Encryption key password.",
             },
+            backupkey: {
+                description: "A previously exported paperkey in JSON format.",
+                type: String,
+                min_length: 300,
+                max_length: 600,
+                optional: true,
+            },
         },
     },
     access: {
@@ -619,29 +629,48 @@ fn write_media_label(
 )]
 /// Try to restore a tape encryption key
 pub async fn restore_key(
-    drive: String,
+    drive: Option<String>,
     password: String,
+    backupkey: Option<String>,
 ) -> Result<(), Error> {
-    run_drive_blocking_task(
-        drive.clone(),
-        "restore key".to_string(),
-        move |config| {
-            let mut drive = open_drive(&config, &drive)?;
 
-            let (_media_id, key_config) = drive.read_label()?;
+    if (drive.is_none() && backupkey.is_none()) || (drive.is_some() && backupkey.is_some()) {
+        param_bail!(
+            "drive",
+            format_err!("Please specify either a valid drive name or a backupkey")
+        );
+    }
 
-            if let Some(key_config) = key_config {
-                let password_fn = || { Ok(password.as_bytes().to_vec()) };
-                let (key, ..) = key_config.decrypt(&password_fn)?;
-                pbs_config::tape_encryption_keys::insert_key(key, key_config, true)?;
-            } else {
-                bail!("media does not contain any encryption key configuration");
+    if let Some(drive) = drive {
+        run_drive_blocking_task(
+            drive.clone(),
+            "restore key".to_string(),
+            move |config| {
+                let mut drive = open_drive(&config, &drive)?;
+
+                let (_media_id, key_config) = drive.read_label()?;
+
+                if let Some(key_config) = key_config {
+                    let password_fn = || { Ok(password.as_bytes().to_vec()) };
+                    let (key, ..) = key_config.decrypt(&password_fn)?;
+                    insert_key(key, key_config, true)?;
+                } else {
+                    bail!("media does not contain any encryption key configuration");
+                }
+
+                Ok(())
             }
+        )
+        .await?;
+    }else if let Some(backupkey) = backupkey {
+        let key_config: KeyConfig =
+                serde_json::from_str(&backupkey).map_err(|err| format_err!("<errmsg>: {}", err))?;
+        let password_fn = || Ok(password.as_bytes().to_vec());
+        let (key, ..) = key_config.decrypt(&password_fn)?;
+        insert_key(key, key_config, false)?;
+    }
 
-            Ok(())
-        }
-    )
-    .await
+    Ok(())
 }
 
  #[api(
diff --git a/src/bin/proxmox_tape/encryption_key.rs b/src/bin/proxmox_tape/encryption_key.rs
index 71df9ffa..471a6349 100644
--- a/src/bin/proxmox_tape/encryption_key.rs
+++ b/src/bin/proxmox_tape/encryption_key.rs
@@ -1,8 +1,8 @@
-use anyhow::{bail, Error};
+use anyhow::{bail, format_err, Error};
 use serde_json::Value;
 
 use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
-use proxmox_schema::api;
+use proxmox_schema::{api, param_bail};
 use proxmox_sys::linux::tty;
 
 use pbs_api_types::{
@@ -12,6 +12,7 @@ use pbs_api_types::{
 
 use pbs_datastore::paperkey::{PaperkeyFormat, generate_paper_key};
 use pbs_config::tape_encryption_keys::{load_key_configs,complete_key_fingerprint};
+use pbs_config::key_config::KeyConfig;
 
 use proxmox_backup::api2;
 
@@ -186,6 +187,9 @@ fn change_passphrase(
     Ok(())
 }
 
+pub const BEGIN_MARKER: &str = "-----BEGIN PROXMOX BACKUP KEY-----";
+pub const END_MARKER: &str = "-----END PROXMOX BACKUP KEY-----";
+
 #[api(
     input: {
         properties: {
@@ -193,23 +197,70 @@ fn change_passphrase(
                 schema: DRIVE_NAME_SCHEMA,
                 optional: true,
             },
+            "backupkey": {
+                description: "Importing a previously exported backupkey with either an exported paperkey-file, json-string or a json-file",
+                type: String,
+                optional: true,
+            },
         },
     },
 )]
 /// Restore encryption key from tape (read password from stdin)
 async fn restore_key(
     mut param: Value,
+    backupkey: Option<String>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<(), Error> {
 
     let (config, _digest) = pbs_config::drive::config()?;
-    param["drive"] = crate::extract_drive_name(&mut param, &config)?.into();
+
+    let drive = crate::extract_drive_name(&mut param, &config);
 
     if !tty::stdin_isatty() {
         bail!("no password input mechanism available");
     }
 
-    let password = tty::read_password("Tepe Encryption Key Password: ")?;
+    if (drive.is_err() && backupkey.is_none()) || (drive.is_ok() && backupkey.is_some()) {
+        param_bail!(
+            "drive",
+            format_err!("Please specify either a valid drive name or a backupkey")
+        );
+    }
+
+    if drive.is_ok() {
+        param["drive"] = drive.unwrap().into();
+    }
+
+    if let Some(backupkey) = backupkey {
+        if serde_json::from_str::<KeyConfig>(&backupkey).is_ok() {
+            // json as Parameter
+            println!("backupkey to import: {}", backupkey);
+            param["backupkey"] = backupkey.into();
+        } else {
+            println!("backupkey is not a valid json. Interpreting Parameter as a filename");
+            let data = proxmox_sys::fs::file_read_string(backupkey)?;
+            if serde_json::from_str::<KeyConfig>(&data).is_ok() {
+                // standalone json-file
+                println!("backupkey to import: {}", data);
+                param["backupkey"] = data.into();
+            } else {
+                // exported paperkey-file
+                let start = data
+                    .find(BEGIN_MARKER)
+                    .ok_or_else(|| format_err!("cannot find key start marker"))?
+                    + BEGIN_MARKER.len();
+                let data_remain = &data[start..];
+                let end = data_remain
+                    .find(END_MARKER)
+                    .ok_or_else(|| format_err!("cannot find key end marker below start marker"))?;
+                let backupkey_extract = &data_remain[..end];
+                println!("backupkey to import: {}", backupkey_extract);
+                param["backupkey"] = backupkey_extract.into();
+            }
+        }
+    }
+
+    let password = tty::read_password("Tape Encryption Key Password: ")?;
     param["password"] = String::from_utf8(password)?.into();
 
     let info = &api2::tape::drive::API_METHOD_RESTORE_KEY;
-- 
2.30.2






More information about the pbs-devel mailing list