mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
Simplify rclone backend (using rclone > 1.52.2)
This commit is contained in:
parent
13caa4291b
commit
89b4c96085
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -1593,14 +1593,13 @@ dependencies = [
|
||||
"rstest",
|
||||
"scrypt",
|
||||
"self_update",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-aux",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"simplelog",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
@ -1796,17 +1795,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
|
||||
@ -67,8 +67,7 @@ filetime = "0.2"
|
||||
reqwest = {version = "0.11", default-features = false, features = ["json", "rustls-tls", "stream"] }
|
||||
backoff = { version = "0.4", features = ["tokio"] }
|
||||
# rclone backend
|
||||
sha1 = "0.10"
|
||||
tempfile = "3"
|
||||
semver = "1"
|
||||
# cache
|
||||
dirs = "4"
|
||||
cachedir = "0.3"
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
@ -10,31 +9,10 @@ use bytes::Bytes;
|
||||
use log::*;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use rand::thread_rng;
|
||||
use sha1::{Digest, Sha1};
|
||||
use tempfile::{Builder, TempDir};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
use super::{FileType, Id, ReadBackend, RestBackend, WriteBackend};
|
||||
|
||||
// create a .htpasswd file with random user/password
|
||||
fn htpasswd() -> Result<(TempDir, PathBuf, String, String)> {
|
||||
let dir = Builder::new().prefix("rustic").tempdir()?;
|
||||
|
||||
let file_path = dir.path().join(".htpasswd");
|
||||
let mut file = File::create(&file_path)?;
|
||||
|
||||
let user = Alphanumeric.sample_string(&mut thread_rng(), 12);
|
||||
let password = Alphanumeric.sample_string(&mut thread_rng(), 12);
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let pass = base64::encode(hasher.finalize());
|
||||
|
||||
writeln!(file, "{}:{{SHA}}{}", user, pass)?;
|
||||
|
||||
Ok((dir, file_path, user, password))
|
||||
}
|
||||
|
||||
struct ChildToKill(Child);
|
||||
impl Drop for ChildToKill {
|
||||
fn drop(&mut self) {
|
||||
@ -46,24 +24,47 @@ impl Drop for ChildToKill {
|
||||
#[derive(Clone)]
|
||||
pub struct RcloneBackend {
|
||||
rest: RestBackend,
|
||||
_child_data: Arc<(ChildToKill, TempDir)>,
|
||||
_child_data: Arc<ChildToKill>,
|
||||
}
|
||||
|
||||
impl RcloneBackend {
|
||||
pub fn new(url: &str) -> Result<Self> {
|
||||
let (tmp_dir, file, user, pass) = htpasswd()?;
|
||||
let rclone_version_output = Command::new("rclone").arg("version").output()?.stdout;
|
||||
let rclone_version = str::from_utf8(&rclone_version_output)?
|
||||
.lines()
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("'rclone version' doesn't give any output"))?
|
||||
.strip_prefix("rclone v")
|
||||
.ok_or_else(|| anyhow!("output of 'rclone version' doesn't start with 'rclone v'"))?;
|
||||
|
||||
let args = [
|
||||
"serve",
|
||||
"restic",
|
||||
url,
|
||||
"--addr",
|
||||
"localhost:0",
|
||||
"--htpasswd",
|
||||
file.to_str().unwrap(),
|
||||
];
|
||||
let versions: Vec<&str> = rclone_version.split(&['.', '-'][..]).collect();
|
||||
let major = versions[0].parse::<i32>()?;
|
||||
let minor = versions[1].parse::<i32>()?;
|
||||
let patch = versions[2].parse::<i32>()?;
|
||||
|
||||
if major
|
||||
.cmp(&1)
|
||||
.then(minor.cmp(&52))
|
||||
.then(patch.cmp(&2))
|
||||
.is_lt()
|
||||
{
|
||||
// for rclone < 1.52.2 setting user/password via env variable doesn't work. This means
|
||||
// we are setting up an rclone without authentication which is a security issue!
|
||||
// (however, it still works, so we give a warning)
|
||||
warn!(
|
||||
"Using rclone without authentication! Upgrade to rclone >= 1.52.2 (current version: {rclone_version})!"
|
||||
);
|
||||
}
|
||||
|
||||
let user = Alphanumeric.sample_string(&mut thread_rng(), 12);
|
||||
let password = Alphanumeric.sample_string(&mut thread_rng(), 12);
|
||||
|
||||
let args = ["serve", "restic", url, "--addr", "localhost:0"];
|
||||
debug!("starting rclone with args {args:?}");
|
||||
|
||||
let mut child = Command::new("rclone")
|
||||
.env("RCLONE_USER", &user)
|
||||
.env("RCLONE_PASS", &password)
|
||||
.args(args)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
@ -106,12 +107,12 @@ impl RcloneBackend {
|
||||
bail!("url must start with http://! url: {url}");
|
||||
}
|
||||
|
||||
let url = "http://".to_string() + &user + ":" + &pass + "@" + &url[7..];
|
||||
let url = "http://".to_string() + &user + ":" + &password + "@" + &url[7..];
|
||||
|
||||
debug!("using REST backend with url {url}.");
|
||||
let rest = RestBackend::new(&url);
|
||||
Ok(Self {
|
||||
_child_data: Arc::new((ChildToKill(child), tmp_dir)),
|
||||
_child_data: Arc::new(ChildToKill(child)),
|
||||
rest,
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user