Simplify rclone backend (using rclone > 1.52.2)

This commit is contained in:
Alexander Weiss 2022-09-06 11:41:37 +02:00
parent 13caa4291b
commit 89b4c96085
3 changed files with 40 additions and 52 deletions

14
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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,
})
}