[pbs-devel] [PATCH proxmox-backup] fix #3854 paperkey import to proxmox-tape
Dominik Csapak
d.csapak at proxmox.com
Wed Feb 9 14:21:32 CET 2022
comments inline
On 2/8/22 12:51, Markus Frank wrote:
> added a parameter to the cli for reading a old paperkeyfile to restore
> the key from it. For that i added a json parameter for the api and made
> hint optional because hint is already in the proxmox-backupkey-json.
>
> functionality:
> proxmox-tape key paperkey [fingerprint of existing key] > paperkey.backup
> proxmox-tape key create --paperkeyfile paperkey.backup
>
> for importing the key it is irrelevant, if the paperkey got exported as html
> or txt.
>
> Signed-off-by: Markus Frank <m.frank at proxmox.com>
> ---
> src/api2/config/tape_encryption_keys.rs | 41 +++++++++++++++++++------
> src/bin/proxmox_tape/encryption_key.rs | 28 +++++++++++++++++
> 2 files changed, 60 insertions(+), 9 deletions(-)
>
> diff --git a/src/api2/config/tape_encryption_keys.rs b/src/api2/config/tape_encryption_keys.rs
> index 1ad99377..23de4acc 100644
> --- a/src/api2/config/tape_encryption_keys.rs
> +++ b/src/api2/config/tape_encryption_keys.rs
> @@ -145,6 +145,12 @@ pub fn change_passphrase(
> },
> hint: {
> schema: PASSWORD_HINT_SCHEMA,
> + optional: true,
> + },
> + backupkey: {
> + description: "json parameter for importing old backupkey",
since we want to import a previously exported paperkey, i'd use that
in the description instead, for example
'A previously exported paperkey in JSON format.'
> + type: String
while having a regex here is not practical (i think), we should
somewhat limit the input a user can send here, we should at least limit
the length here.
> + optional: true,
> },
> },
> },
> @@ -159,7 +165,8 @@ pub fn change_passphrase(
> pub fn create_key(
> kdf: Option<Kdf>,
> password: String,
> - hint: String,
> + hint: Option<String>,
> + backupkey: Option<String>,
> _rpcenv: &mut dyn RpcEnvironment
> ) -> Result<Fingerprint, Error> {
>
> @@ -169,14 +176,30 @@ pub fn create_key(
> bail!("Please specify a key derivation function (none is not allowed here).");
> }
>
> - let (key, mut key_config) = KeyConfig::new(password.as_bytes(), kdf)?;
> - key_config.hint = Some(hint);
> -
> - let fingerprint = key_config.fingerprint.clone().unwrap();
> -
> - insert_key(key, key_config, false)?;
> -
> - Ok(fingerprint)
> + if let Some(ref backupkey) = backupkey {
> + match serde_json::from_str::<KeyConfig>(backupkey) {
> + Ok(key_config) => {
> + let password_fn = || { Ok(password.as_bytes().to_vec()) };
using 'into_bytes()' here avoids copying it
> + let (key, _created, fingerprint) = key_config.decrypt(&password_fn)?;
> + insert_key(key, key_config, false)?;
> + Ok(fingerprint)
> + }
> + Err(err) => {
> + eprintln!("Couldn't parse data as KeyConfig - {}", err);
> + bail!("Neither a PEM-formatted private key, nor a PBS key file.");
two things: please do use log::error (or warning) instead of 'eprintln'
and when we return an error, there is no need to print an additional error,
this whole statement can be simplified by doing:
---
let key_config: KeyConfig = serde_json::from_str(backupkey)
.map_err(|err| format_err!("<errmsg>: {}", err))?;
...
---
> + }
> + }
> + } else {
> + if hint.is_none() {
> + bail!("Please specify either a hint or a backupkey.");
we have a special error to bubble parameter errors to the gui
look for 'ParameterError' in proxmox-schema
(basically:
let mut err = ParameterError::new();
err.push("parameter".to_string(), Err(...))
return Err(err);
using that, the api behaves similarly to when the schema verification fails
> + } else {
> + let (key, mut key_config) = KeyConfig::new(password.as_bytes(), kdf)?;
> + key_config.hint = hint;
> + let fingerprint = key_config.fingerprint.clone().unwrap();
> + insert_key(key, key_config, false)?;
> + Ok(fingerprint)
> + }
> + }
> }
the whole if/else could be better written as
match (hint, backupkey) {
(_, Some(backupkey)) => { }, // handle backupkey
(Some(hint), _) => { }, // handle hint
(None, None) => { } // handle parameter error
}
>
>
> diff --git a/src/bin/proxmox_tape/encryption_key.rs b/src/bin/proxmox_tape/encryption_key.rs
> index 156295fd..1863c9bc 100644
> --- a/src/bin/proxmox_tape/encryption_key.rs
> +++ b/src/bin/proxmox_tape/encryption_key.rs
> @@ -15,6 +15,8 @@ use pbs_config::tape_encryption_keys::{load_key_configs,complete_key_fingerprint
>
> use proxmox_backup::api2;
>
> +use std::fs;
> +
> pub fn encryption_key_commands() -> CommandLineInterface {
>
> let cmd_def = CliCommandMap::new()
> @@ -222,6 +224,12 @@ async fn restore_key(
> type: String,
> min_length: 1,
> max_length: 32,
> + optional: true,
> + },
> + paperkeyfile: {
nit: i'd prefer 'paperkey-file'
> + description: "Paperkeyfile location for importing old backupkey",
> + type: String,
> + optional: true,
> },
> },
> },
> @@ -230,12 +238,32 @@ async fn restore_key(
> fn create_key(
> mut param: Value,
> rpcenv: &mut dyn RpcEnvironment,
> + paperkeyfile: Option<String>,
> ) -> Result<(), Error> {
>
> if !tty::stdin_isatty() {
> bail!("no password input mechanism available");
> }
>
> + if param["hint"].is_null() && paperkeyfile.is_none(){
> + bail!("Please specify either a hint or a paperkeyfile.");
same as above with ParameterError
> + }
> +
> + // searching for PROXMOX BACKUP KEY if a paperkeyfile is defined
> + if let Some(paperkeyfile) = paperkeyfile {
> + let data = fs::read_to_string(paperkeyfile)?;
i'd use proxmox-sys::fs::file_read_string here
it's just a thin wrapper around read_to_string, but provides
a better error message
> + let begin = "-----BEGIN PROXMOX BACKUP KEY-----";
> + let start = data.find(begin);
> + let end = data.find("-----END PROXMOX BACKUP KEY-----");
> + if let Some(start) = start {
> + if let Some(end) = end {
> + let backupkey = &data[start+begin.len()..end];
i'd check here if start < end
> + param["backupkey"]=backupkey.into();
> + println!("backupkey to import: {}", backupkey);
> + }
> + }
this could be better written as:
match (start, end) {
(Some(start), Some(end)) => {} // set the parameter
(_, _) => {} // throw error
}
else a file with missing markers will be silently ignored..
> + }
> +
> let password = tty::read_and_verify_password("Tape Encryption Key Password: ")?;
>
> param["password"] = String::from_utf8(password)?.into();
More information about the pbs-devel
mailing list