From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id CF6E565A84 for ; Wed, 4 Nov 2020 14:11:11 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id CC970E098 for ; Wed, 4 Nov 2020 14:10:41 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 5E6EDE03C for ; Wed, 4 Nov 2020 14:10:37 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 2928A42E12 for ; Wed, 4 Nov 2020 14:10:37 +0100 (CET) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pbs-devel@lists.proxmox.com Date: Wed, 4 Nov 2020 14:10:25 +0100 Message-Id: <20201104131026.4017010-3-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201104131026.4017010-1-f.gruenbichler@proxmox.com> References: <20201104131026.4017010-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.025 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [proxmox-backup-manager.rs, remote.rs, pull.rs] Subject: [pbs-devel] [PATCH proxmox-backup 3/4] api: refactor remote client and add remote scan X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 04 Nov 2020 13:11:11 -0000 to allow on-demand scanning of remote datastores accessible for the configured remote user. Signed-off-by: Fabian Grünbichler --- Notes: not 100% sure about PRIV_REMOTE_AUDIT vs PRIV_REMOTE_READ.. the latter is required to use a datastore for syncing/pull purposes src/api2/config/remote.rs | 66 ++++++++++++++++++++++++++++++- src/api2/pull.rs | 12 +----- src/bin/proxmox-backup-manager.rs | 26 +++--------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/api2/config/remote.rs b/src/api2/config/remote.rs index ffbba1d2..b415f63d 100644 --- a/src/api2/config/remote.rs +++ b/src/api2/config/remote.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Error}; +use anyhow::{bail, format_err, Error}; use serde_json::Value; use ::serde::{Deserialize, Serialize}; @@ -6,6 +6,7 @@ use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; +use crate::client::{HttpClient, HttpClientOptions}; use crate::config::cached_user_info::CachedUserInfo; use crate::config::remote; use crate::config::acl::{PRIV_REMOTE_AUDIT, PRIV_REMOTE_MODIFY}; @@ -301,10 +302,71 @@ pub fn delete_remote(name: String, digest: Option) -> Result<(), Error> Ok(()) } +/// Helper to get client for remote.cfg entry +pub async fn remote_client(remote: remote::Remote) -> Result { + let options = HttpClientOptions::new() + .password(Some(remote.password.clone())) + .fingerprint(remote.fingerprint.clone()); + + let client = HttpClient::new( + &remote.host, + remote.port.unwrap_or(8007), + &remote.userid, + options)?; + let _auth_info = client.login() // make sure we can auth + .await + .map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?; + + Ok(client) +} + + +#[api( + input: { + properties: { + name: { + schema: REMOTE_ID_SCHEMA, + }, + }, + }, + access: { + permission: &Permission::Privilege(&["remote", "{name}"], PRIV_REMOTE_AUDIT, false), + }, + returns: { + description: "List the accessible datastores.", + type: Array, + items: { + description: "Datastore name and description.", + type: DataStoreListItem, + }, + }, +)] +/// List datastores of a remote.cfg entry +pub async fn scan_remote_datastores(name: String) -> Result, Error> { + let (remote_config, _digest) = remote::config()?; + let remote: remote::Remote = remote_config.lookup("remote", &name)?; + + let client = remote_client(remote).await?; + let api_res = client.get("api2/json/admin/datastore", None).await?; + let parse_res = match api_res.get("data") { + Some(data) => serde_json::from_value::>(data.to_owned()), + None => bail!("remote {} did not return any datastore list data", &name), + }; + + match parse_res { + Ok(parsed) => Ok(parsed), + Err(_) => bail!("Failed to parse remote scan api result."), + } +} + +const SCAN_ROUTER: Router = Router::new() + .get(&API_METHOD_SCAN_REMOTE_DATASTORES); + const ITEM_ROUTER: Router = Router::new() .get(&API_METHOD_READ_REMOTE) .put(&API_METHOD_UPDATE_REMOTE) - .delete(&API_METHOD_DELETE_REMOTE); + .delete(&API_METHOD_DELETE_REMOTE) + .subdirs(&[("scan", &SCAN_ROUTER)]); pub const ROUTER: Router = Router::new() .get(&API_METHOD_LIST_REMOTES) diff --git a/src/api2/pull.rs b/src/api2/pull.rs index d9e9d31d..87015c72 100644 --- a/src/api2/pull.rs +++ b/src/api2/pull.rs @@ -9,7 +9,7 @@ use proxmox::api::{ApiMethod, Router, RpcEnvironment, Permission}; use crate::server::{WorkerTask, jobstate::Job}; use crate::backup::DataStore; -use crate::client::{HttpClient, HttpClientOptions, BackupRepository, pull::pull_store}; +use crate::client::{HttpClient, BackupRepository, pull::pull_store}; use crate::api2::types::*; use crate::config::{ remote, @@ -50,17 +50,9 @@ pub async fn get_pull_parameters( let (remote_config, _digest) = remote::config()?; let remote: remote::Remote = remote_config.lookup("remote", remote)?; - let options = HttpClientOptions::new() - .password(Some(remote.password.clone())) - .fingerprint(remote.fingerprint.clone()); - let src_repo = BackupRepository::new(Some(remote.userid.clone()), Some(remote.host.clone()), remote.port, remote_store.to_string()); - let client = HttpClient::new(&src_repo.host(), src_repo.port(), &src_repo.auth_id(), options)?; - let _auth_info = client.login() // make sure we can auth - .await - .map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?; - + let client = crate::api2::config::remote::remote_client(remote).await?; Ok((client, src_repo, tgt_store)) } diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index 7499446b..e52c2f76 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -413,29 +413,13 @@ pub fn complete_remote_datastore_name(_arg: &str, param: &HashMap