tape: add hardware encryption key managenent api

This commit is contained in:
Dietmar Maurer
2021-01-18 07:16:06 +01:00
parent 4e9cc3e97c
commit d5a48b5ce4
15 changed files with 518 additions and 37 deletions

View File

@ -9,6 +9,7 @@ pub mod verify;
pub mod drive;
pub mod changer;
pub mod media_pool;
pub mod tape_encryption_keys;
const SUBDIRS: SubdirMap = &[
("access", &access::ROUTER),
@ -18,6 +19,7 @@ const SUBDIRS: SubdirMap = &[
("media-pool", &media_pool::ROUTER),
("remote", &remote::ROUTER),
("sync", &sync::ROUTER),
("tape-encryption-keys", &tape_encryption_keys::ROUTER),
("verify", &verify::ROUTER),
];

View File

@ -0,0 +1,166 @@
use anyhow::{bail, Error};
use serde_json::Value;
use proxmox::{
api::{
api,
ApiMethod,
Router,
RpcEnvironment,
},
tools::fs::open_file_locked,
};
use crate::{
config::{
tape_encryption_keys::{
TAPE_KEYS_LOCKFILE,
EncryptionKeyInfo,
load_keys,
save_keys,
},
},
api2::types::{
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
PROXMOX_CONFIG_DIGEST_SCHEMA,
TapeKeyMetadata,
},
backup::Fingerprint,
tools::format::as_fingerprint,
};
#[api(
protected: true,
input: {
properties: {},
},
returns: {
description: "The list of tape encryption keys (with config digest).",
type: Array,
items: { type: TapeKeyMetadata },
},
)]
/// List existing keys
pub fn list_keys(
_param: Value,
_info: &ApiMethod,
mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<Vec<TapeKeyMetadata>, Error> {
let (key_map, digest) = load_keys()?;
let mut list = Vec::new();
for (_fingerprint, item) in key_map {
list.push(TapeKeyMetadata {
hint: item.hint,
fingerprint: as_fingerprint(item.fingerprint.bytes()),
});
}
rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
Ok(list)
}
#[api(
protected: true,
input: {
properties: {
password: {
description: "A secret password.",
min_length: 5,
},
hint: {
description: "Password restore hint",
min_length: 1,
},
},
},
)]
/// Create a new encryption key
pub fn create_key(
password: String,
hint: String,
_rpcenv: &mut dyn RpcEnvironment
) -> Result<Fingerprint, Error> {
let key = openssl::sha::sha256(password.as_bytes()); // fixme: better KDF ??
let item = EncryptionKeyInfo::new(&key, hint);
let _lock = open_file_locked(
TAPE_KEYS_LOCKFILE,
std::time::Duration::new(10, 0),
true,
)?;
let (mut key_map, _) = load_keys()?;
let fingerprint = item.fingerprint.clone();
if let Some(_) = key_map.get(&fingerprint) {
bail!("encryption key '{}' already exists.", fingerprint);
}
key_map.insert(fingerprint.clone(), item);
save_keys(key_map)?;
Ok(fingerprint)
}
#[api(
protected: true,
input: {
properties: {
fingerprint: {
schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
},
digest: {
optional: true,
schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
},
},
},
)]
/// Remove a encryption key from the database
///
/// Please note that you can no longer access tapes using this key.
pub fn delete_key(
fingerprint: Fingerprint,
digest: Option<String>,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let _lock = open_file_locked(
TAPE_KEYS_LOCKFILE,
std::time::Duration::new(10, 0),
true,
)?;
let (mut key_map, expected_digest) = load_keys()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
}
match key_map.get(&fingerprint) {
Some(_) => { key_map.remove(&fingerprint); },
None => bail!("tape encryption key '{}' does not exist.", fingerprint),
}
save_keys(key_map)?;
Ok(())
}
const ITEM_ROUTER: Router = Router::new()
//.get(&API_METHOD_READ_KEY_METADATA)
//.put(&API_METHOD_UPDATE_KEY_METADATA)
.delete(&API_METHOD_DELETE_KEY);
pub const ROUTER: Router = Router::new()
.get(&API_METHOD_LIST_KEYS)
.post(&API_METHOD_CREATE_KEY)
.match_all("fingerprint", &ITEM_ROUTER);

View File

@ -1,7 +1,7 @@
use std::path::Path;
use std::sync::Arc;
use anyhow::{bail, format_err, Error};
use anyhow::{bail, Error};
use serde_json::Value;
use proxmox::{
@ -880,8 +880,7 @@ pub fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
let (config, _digest) = config::drive::config()?;
let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
let mut handle = drive_config.open()
.map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, drive_config.path, err))?;
let mut handle = drive_config.open()?;
handle.cartridge_memory()
}
@ -906,8 +905,7 @@ pub fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
// Note: use open_linux_tape_device, because this also works if no medium loaded
let file = open_linux_tape_device(&drive_config.path)
.map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, drive_config.path, err))?;
let file = open_linux_tape_device(&drive_config.path)?;
let mut handle = LinuxTapeHandle::new(file);

View File

@ -77,7 +77,7 @@ const_regex!{
pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
pub FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
pub ACL_PATH_REGEX = concat!(r"^(?:/|", r"(?:/", PROXMOX_SAFE_ID_REGEX_STR!(), ")+", r")$");
@ -103,8 +103,8 @@ pub const IP_FORMAT: ApiStringFormat =
pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
pub const CERT_FINGERPRINT_SHA256_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&CERT_FINGERPRINT_SHA256_REGEX);
pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX);
pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
@ -163,17 +163,22 @@ pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema = StringSchema::new(
"X509 certificate fingerprint (sha256)."
)
.format(&CERT_FINGERPRINT_SHA256_FORMAT)
.format(&FINGERPRINT_SHA256_FORMAT)
.schema();
pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(r#"\
Prevent changes if current configuration file has different SHA256 digest.
This can be used to prevent concurrent modifications.
"#
pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema = StringSchema::new(
"Tape encryption key fingerprint (sha256)."
)
.format(&PVE_CONFIG_DIGEST_FORMAT)
.format(&FINGERPRINT_SHA256_FORMAT)
.schema();
pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
"Prevent changes if current configuration file has different \
SHA256 digest. This can be used to prevent concurrent \
modifications."
)
.format(&PVE_CONFIG_DIGEST_FORMAT) .schema();
pub const CHUNK_DIGEST_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&SHA256_HEX_REGEX);

View File

@ -12,7 +12,8 @@ use proxmox::api::{
use crate::api2::types::{
PROXMOX_SAFE_ID_FORMAT,
CHANGER_NAME_SCHEMA,
};
CERT_FINGERPRINT_SHA256_SCHEMA,
};
pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT)
@ -205,3 +206,18 @@ pub struct LinuxDriveAndMediaStatus {
#[serde(skip_serializing_if="Option::is_none")]
pub medium_passes: Option<u64>,
}
#[api(
properties: {
fingerprint: {
schema: CERT_FINGERPRINT_SHA256_SCHEMA,
},
},
)]
#[derive(Deserialize, Serialize)]
/// Hardware Encryption key Metadata
pub struct TapeKeyMetadata {
/// Password hint
pub hint: String,
pub fingerprint: String,
}