tape: finish api permission checks

This commit is contained in:
Dietmar Maurer 2021-03-05 11:40:52 +01:00
parent ee33795b72
commit b4975d3102
4 changed files with 151 additions and 4 deletions

View File

@ -20,7 +20,9 @@ use crate::{
self, self,
cached_user_info::CachedUserInfo, cached_user_info::CachedUserInfo,
acl::{ acl::{
PRIV_DATASTORE_READ,
PRIV_TAPE_AUDIT, PRIV_TAPE_AUDIT,
PRIV_TAPE_WRITE,
}, },
tape_job::{ tape_job::{
TapeBackupJobConfig, TapeBackupJobConfig,
@ -71,6 +73,33 @@ pub const ROUTER: Router = Router::new()
.post(&API_METHOD_BACKUP) .post(&API_METHOD_BACKUP)
.match_all("id", &TAPE_BACKUP_JOB_ROUTER); .match_all("id", &TAPE_BACKUP_JOB_ROUTER);
fn check_backup_permission(
auth_id: &Authid,
store: &str,
pool: &str,
drive: &str,
) -> Result<(), Error> {
let user_info = CachedUserInfo::new()?;
let privs = user_info.lookup_privs(auth_id, &["datastore", store]);
if (privs & PRIV_DATASTORE_READ) == 0 {
bail!("no permissions on /datastore/{}", store);
}
let privs = user_info.lookup_privs(auth_id, &["tape", "drive", drive]);
if (privs & PRIV_TAPE_WRITE) == 0 {
bail!("no permissions on /tape/drive/{}", drive);
}
let privs = user_info.lookup_privs(auth_id, &["tape", "pool", pool]);
if (privs & PRIV_TAPE_WRITE) == 0 {
bail!("no permissions on /tape/pool/{}", pool);
}
Ok(())
}
#[api( #[api(
returns: { returns: {
description: "List configured thape backup jobs and their status", description: "List configured thape backup jobs and their status",
@ -202,6 +231,12 @@ pub fn do_tape_backup_job(
}, },
}, },
}, },
access: {
// Note: parameters are from job config, so we need to test inside function body
description: "The user needs Tape.Write privilege on /tape/pool/{pool} \
and /tape/drive/{drive}, Datastore.Read privilege on /datastore/{store}.",
permission: &Permission::Anybody,
},
)] )]
/// Runs a tape backup job manually. /// Runs a tape backup job manually.
pub fn run_tape_backup_job( pub fn run_tape_backup_job(
@ -213,6 +248,13 @@ pub fn run_tape_backup_job(
let (config, _digest) = config::tape_job::config()?; let (config, _digest) = config::tape_job::config()?;
let backup_job: TapeBackupJobConfig = config.lookup("backup", &id)?; let backup_job: TapeBackupJobConfig = config.lookup("backup", &id)?;
check_backup_permission(
&auth_id,
&backup_job.setup.store,
&backup_job.setup.pool,
&backup_job.setup.drive,
)?;
let job = Job::new("tape-backup-job", &id)?; let job = Job::new("tape-backup-job", &id)?;
let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None)?; let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None)?;
@ -232,6 +274,12 @@ pub fn run_tape_backup_job(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
// Note: parameters are no uri parameter, so we need to test inside function body
description: "The user needs Tape.Write privilege on /tape/pool/{pool} \
and /tape/drive/{drive}, Datastore.Read privilege on /datastore/{store}.",
permission: &Permission::Anybody,
},
)] )]
/// Backup datastore to tape media pool /// Backup datastore to tape media pool
pub fn backup( pub fn backup(
@ -241,6 +289,13 @@ pub fn backup(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
check_backup_permission(
&auth_id,
&setup.store,
&setup.pool,
&setup.drive,
)?;
let datastore = DataStore::lookup_datastore(&setup.store)?; let datastore = DataStore::lookup_datastore(&setup.store)?;
let (config, _digest) = config::media_pool::config()?; let (config, _digest) = config::media_pool::config()?;

View File

@ -13,6 +13,7 @@ use crate::{
cached_user_info::CachedUserInfo, cached_user_info::CachedUserInfo,
acl::{ acl::{
PRIV_TAPE_AUDIT, PRIV_TAPE_AUDIT,
PRIV_TAPE_READ,
}, },
}, },
api2::types::{ api2::types::{
@ -60,6 +61,9 @@ use crate::{
type: MtxStatusEntry, type: MtxStatusEntry,
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
},
)] )]
/// Get tape changer status /// Get tape changer status
pub async fn get_status( pub async fn get_status(
@ -156,6 +160,9 @@ pub async fn get_status(
}, },
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_READ, false),
},
)] )]
/// Transfers media from one slot to another /// Transfers media from one slot to another
pub async fn transfer( pub async fn transfer(

View File

@ -29,6 +29,8 @@ use crate::{
cached_user_info::CachedUserInfo, cached_user_info::CachedUserInfo,
acl::{ acl::{
PRIV_TAPE_AUDIT, PRIV_TAPE_AUDIT,
PRIV_TAPE_READ,
PRIV_TAPE_WRITE,
}, },
}, },
api2::{ api2::{
@ -143,6 +145,9 @@ where
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Load media with specified label /// Load media with specified label
/// ///
@ -182,6 +187,9 @@ pub fn load_media(
}, },
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Load media from the specified slot /// Load media from the specified slot
/// ///
@ -215,6 +223,9 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
type: u64, type: u64,
minimum: 1, minimum: 1,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Export media with specified label /// Export media with specified label
pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> { pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> {
@ -252,6 +263,9 @@ pub async fn export_media(drive: String, label_text: String) -> Result<u64, Erro
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Unload media via changer /// Unload media via changer
pub fn unload( pub fn unload(
@ -297,6 +311,9 @@ pub fn unload(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
},
)] )]
/// Erase media. Check for label-text if given (cancels if wrong media). /// Erase media. Check for label-text if given (cancels if wrong media).
pub fn erase_media( pub fn erase_media(
@ -381,6 +398,9 @@ pub fn erase_media(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Rewind tape /// Rewind tape
pub fn rewind( pub fn rewind(
@ -413,6 +433,9 @@ pub fn rewind(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Eject/Unload drive media /// Eject/Unload drive media
pub fn eject_media( pub fn eject_media(
@ -456,6 +479,9 @@ pub fn eject_media(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
},
)] )]
/// Label media /// Label media
/// ///
@ -588,6 +614,9 @@ fn write_media_label(
}, },
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Try to restore a tape encryption key /// Try to restore a tape encryption key
pub async fn restore_key( pub async fn restore_key(
@ -631,6 +660,9 @@ pub async fn restore_key(
returns: { returns: {
type: MediaIdFlat, type: MediaIdFlat,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Read media label (optionally inventorize media) /// Read media label (optionally inventorize media)
pub async fn read_label( pub async fn read_label(
@ -706,6 +738,9 @@ pub async fn read_label(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Clean drive /// Clean drive
pub fn clean_drive( pub fn clean_drive(
@ -748,6 +783,9 @@ pub fn clean_drive(
type: LabelUuidMap, type: LabelUuidMap,
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// List known media labels (Changer Inventory) /// List known media labels (Changer Inventory)
/// ///
@ -817,6 +855,9 @@ pub async fn inventory(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Update inventory /// Update inventory
/// ///
@ -911,6 +952,9 @@ pub fn update_inventory(
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
},
)] )]
/// Label media with barcodes from changer device /// Label media with barcodes from changer device
pub fn barcode_label_media( pub fn barcode_label_media(
@ -1020,6 +1064,9 @@ fn barcode_label_media_worker(
type: MamAttribute, type: MamAttribute,
}, },
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
},
)] )]
/// Read Cartridge Memory (Medium auxiliary memory attributes) /// Read Cartridge Memory (Medium auxiliary memory attributes)
pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> { pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
@ -1047,6 +1094,9 @@ pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error>
returns: { returns: {
type: Lp17VolumeStatistics, type: Lp17VolumeStatistics,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
},
)] )]
/// Read Volume Statistics (SCSI log page 17h) /// Read Volume Statistics (SCSI log page 17h)
pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> { pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
@ -1074,6 +1124,9 @@ pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Er
returns: { returns: {
type: LinuxDriveAndMediaStatus, type: LinuxDriveAndMediaStatus,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
},
)] )]
/// Get drive/media status /// Get drive/media status
pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> { pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
@ -1115,6 +1168,9 @@ pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
},
)] )]
/// Scan media and record content /// Scan media and record content
pub fn catalog_media( pub fn catalog_media(

View File

@ -11,6 +11,7 @@ use proxmox::{
RpcEnvironment, RpcEnvironment,
RpcEnvironmentType, RpcEnvironmentType,
Router, Router,
Permission,
section_config::SectionConfigData, section_config::SectionConfigData,
}, },
tools::{ tools::{
@ -33,7 +34,14 @@ use crate::{
UPID_SCHEMA, UPID_SCHEMA,
Authid, Authid,
}, },
config, config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_DATASTORE_BACKUP,
PRIV_TAPE_READ,
},
},
backup::{ backup::{
archive_type, archive_type,
MANIFEST_BLOB_NAME, MANIFEST_BLOB_NAME,
@ -76,7 +84,6 @@ use crate::{
pub const ROUTER: Router = Router::new() pub const ROUTER: Router = Router::new()
.post(&API_METHOD_RESTORE); .post(&API_METHOD_RESTORE);
#[api( #[api(
input: { input: {
properties: { properties: {
@ -95,6 +102,12 @@ pub const ROUTER: Router = Router::new()
returns: { returns: {
schema: UPID_SCHEMA, schema: UPID_SCHEMA,
}, },
access: {
// Note: parameters are no uri parameter, so we need to test inside function body
description: "The user needs Tape.Read privilege on /tape/pool/{pool} \
and /tape/drive/{drive}, Datastore.Backup privilege on /datastore/{store}.",
permission: &Permission::Anybody,
},
)] )]
/// Restore data from media-set /// Restore data from media-set
pub fn restore( pub fn restore(
@ -104,9 +117,18 @@ pub fn restore(
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<Value, Error> { ) -> Result<Value, Error> {
let datastore = DataStore::lookup_datastore(&store)?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let privs = user_info.lookup_privs(&auth_id, &["datastore", &store]);
if (privs & PRIV_DATASTORE_BACKUP) == 0 {
bail!("no permissions on /datastore/{}", store);
}
let privs = user_info.lookup_privs(&auth_id, &["tape", "drive", &drive]);
if (privs & PRIV_TAPE_READ) == 0 {
bail!("no permissions on /tape/drive/{}", drive);
}
let status_path = Path::new(TAPE_STATUS_DIR); let status_path = Path::new(TAPE_STATUS_DIR);
let inventory = Inventory::load(status_path)?; let inventory = Inventory::load(status_path)?;
@ -115,6 +137,13 @@ pub fn restore(
let pool = inventory.lookup_media_set_pool(&media_set_uuid)?; let pool = inventory.lookup_media_set_pool(&media_set_uuid)?;
let privs = user_info.lookup_privs(&auth_id, &["tape", "pool", &pool]);
if (privs & PRIV_TAPE_READ) == 0 {
bail!("no permissions on /tape/pool/{}", pool);
}
let datastore = DataStore::lookup_datastore(&store)?;
let (drive_config, _digest) = config::drive::config()?; let (drive_config, _digest) = config::drive::config()?;
// early check/lock before starting worker // early check/lock before starting worker