src/backup/manifest.rs: include signature inside the manifest
This is more flexible, because we can choose what fileds we want to sign.
This commit is contained in:
parent
3638341aa4
commit
2107a5aebc
@ -4,7 +4,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::backup::{BackupDir, CryptMode};
|
use crate::backup::{BackupDir, CryptMode, CryptConfig};
|
||||||
|
|
||||||
pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
|
pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
|
||||||
pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
|
pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
|
||||||
@ -84,8 +84,41 @@ impl BackupManifest {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_json(self) -> Value {
|
pub fn signature(&self, crypt_config: &CryptConfig) -> [u8; 32] {
|
||||||
json!({
|
|
||||||
|
let mut data = String::new();
|
||||||
|
|
||||||
|
data.push_str(self.snapshot.group().backup_type());
|
||||||
|
data.push('\n');
|
||||||
|
data.push_str(self.snapshot.group().backup_id());
|
||||||
|
data.push('\n');
|
||||||
|
data.push_str(&format!("{}", self.snapshot.backup_time().timestamp()));
|
||||||
|
data.push('\n');
|
||||||
|
data.push('\n');
|
||||||
|
|
||||||
|
for info in self.files.iter() {
|
||||||
|
data.push_str(&info.filename);
|
||||||
|
data.push('\n');
|
||||||
|
data.push_str(match info.crypt_mode {
|
||||||
|
CryptMode::None => "None",
|
||||||
|
CryptMode::SignOnly => "SignOnly",
|
||||||
|
CryptMode::Encrypt => "Encrypt",
|
||||||
|
});
|
||||||
|
data.push('\n');
|
||||||
|
data.push_str(&format!("{}", info.size));
|
||||||
|
data.push('\n');
|
||||||
|
data.push_str(&proxmox::tools::digest_to_hex(&info.csum));
|
||||||
|
data.push('\n');
|
||||||
|
|
||||||
|
data.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
crypt_config.compute_auth_tag(data.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_json(self, crypt_config: Option<&CryptConfig>) -> Value {
|
||||||
|
|
||||||
|
let mut manifest = json!({
|
||||||
"backup-type": self.snapshot.group().backup_type(),
|
"backup-type": self.snapshot.group().backup_type(),
|
||||||
"backup-id": self.snapshot.group().backup_id(),
|
"backup-id": self.snapshot.group().backup_id(),
|
||||||
"backup-time": self.snapshot.backup_time().timestamp(),
|
"backup-time": self.snapshot.backup_time().timestamp(),
|
||||||
@ -99,7 +132,14 @@ impl BackupManifest {
|
|||||||
}));
|
}));
|
||||||
acc
|
acc
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if let Some(crypt_config) = crypt_config {
|
||||||
|
let sig = self.signature(crypt_config);
|
||||||
|
manifest["signature"] = proxmox::tools::digest_to_hex(&sig).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1081,7 +1081,7 @@ async fn create_backup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create manifest (index.json)
|
// create manifest (index.json)
|
||||||
let manifest = manifest.into_json();
|
let manifest = manifest.into_json(crypt_config.as_ref().map(Arc::as_ref));
|
||||||
|
|
||||||
println!("Upload index.json to '{:?}'", repo);
|
println!("Upload index.json to '{:?}'", repo);
|
||||||
let manifest = serde_json::to_string_pretty(&manifest)?.into();
|
let manifest = serde_json::to_string_pretty(&manifest)?.into();
|
||||||
@ -1272,18 +1272,17 @@ async fn restore(param: Value) -> Result<Value, Error> {
|
|||||||
true,
|
true,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
let manifest = client.download_manifest().await?;
|
let (manifest, backup_index_data) = client.download_manifest().await?;
|
||||||
|
|
||||||
let (archive_name, archive_type) = parse_archive_type(archive_name);
|
let (archive_name, archive_type) = parse_archive_type(archive_name);
|
||||||
|
|
||||||
if archive_name == MANIFEST_BLOB_NAME {
|
if archive_name == MANIFEST_BLOB_NAME {
|
||||||
let backup_index_data = manifest.into_json().to_string();
|
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
replace_file(target, backup_index_data.as_bytes(), CreateOptions::new())?;
|
replace_file(target, &backup_index_data, CreateOptions::new())?;
|
||||||
} else {
|
} else {
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
let mut writer = stdout.lock();
|
let mut writer = stdout.lock();
|
||||||
writer.write_all(backup_index_data.as_bytes())
|
writer.write_all(&backup_index_data)
|
||||||
.map_err(|err| format_err!("unable to pipe data - {}", err))?;
|
.map_err(|err| format_err!("unable to pipe data - {}", err))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
|
|||||||
true,
|
true,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
let manifest = client.download_manifest().await?;
|
let (manifest, _) = client.download_manifest().await?;
|
||||||
|
|
||||||
let index = client.download_dynamic_index(&manifest, CATALOG_NAME).await?;
|
let index = client.download_dynamic_index(&manifest, CATALOG_NAME).await?;
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> {
|
|||||||
.custom_flags(libc::O_TMPFILE)
|
.custom_flags(libc::O_TMPFILE)
|
||||||
.open("/tmp")?;
|
.open("/tmp")?;
|
||||||
|
|
||||||
let manifest = client.download_manifest().await?;
|
let (manifest, _) = client.download_manifest().await?;
|
||||||
|
|
||||||
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
|
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
|
||||||
let most_used = index.find_most_used_chunks(8);
|
let most_used = index.find_most_used_chunks(8);
|
||||||
|
@ -139,7 +139,7 @@ async fn mount_do(param: Value, pipe: Option<RawFd>) -> Result<Value, Error> {
|
|||||||
true,
|
true,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
let manifest = client.download_manifest().await?;
|
let (manifest, _) = client.download_manifest().await?;
|
||||||
|
|
||||||
if server_archive_name.ends_with(".didx") {
|
if server_archive_name.ends_with(".didx") {
|
||||||
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
|
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use anyhow::{format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use std::io::{Read, Write, Seek, SeekFrom};
|
use std::io::{Read, Write, Seek, SeekFrom};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -123,7 +123,9 @@ impl BackupReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Download backup manifest (index.json)
|
/// Download backup manifest (index.json)
|
||||||
pub async fn download_manifest(&self) -> Result<BackupManifest, Error> {
|
///
|
||||||
|
/// The manifest signature is verified if we have a crypt_config.
|
||||||
|
pub async fn download_manifest(&self) -> Result<(BackupManifest, Vec<u8>), Error> {
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
@ -131,10 +133,25 @@ impl BackupReader {
|
|||||||
self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
|
self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
|
||||||
let blob = DataBlob::from_raw(raw_data)?;
|
let blob = DataBlob::from_raw(raw_data)?;
|
||||||
blob.verify_crc()?;
|
blob.verify_crc()?;
|
||||||
let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
|
let data = blob.decode(None)?;
|
||||||
let json: Value = serde_json::from_slice(&data[..])?;
|
let json: Value = serde_json::from_slice(&data[..])?;
|
||||||
|
|
||||||
BackupManifest::try_from(json)
|
let signature = json["signature"].as_str().map(String::from);
|
||||||
|
|
||||||
|
let manifest = BackupManifest::try_from(json)?;
|
||||||
|
|
||||||
|
if let Some(ref crypt_config) = self.crypt_config {
|
||||||
|
if let Some(signature) = signature {
|
||||||
|
let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(crypt_config));
|
||||||
|
if signature != expected_signature {
|
||||||
|
bail!("wrong signature in manifest");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// warn/fail?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((manifest, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Download a .blob file
|
/// Download a .blob file
|
||||||
|
Loading…
Reference in New Issue
Block a user