tape: add hardware encryption key managenent api
This commit is contained in:
@ -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),
|
||||
];
|
||||
|
||||
|
166
src/api2/config/tape_encryption_keys.rs
Normal file
166
src/api2/config/tape_encryption_keys.rs
Normal 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);
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
|
Reference in New Issue
Block a user