From 34b21106ddf8244bc2382fca3b7c4044bab44715 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 29 May 2024 17:59:03 +0200 Subject: [PATCH] config-digest: split out config digest api type into separate crate Signed-off-by: Dietmar Maurer --- Cargo.toml | 2 + proxmox-config-digest/Cargo.toml | 20 ++++ proxmox-config-digest/debian/changelog | 5 + proxmox-config-digest/debian/control | 59 +++++++++++ proxmox-config-digest/debian/copyright | 18 ++++ proxmox-config-digest/debian/debcargo.toml | 7 ++ proxmox-config-digest/src/lib.rs | 112 +++++++++++++++++++++ 7 files changed, 223 insertions(+) create mode 100644 proxmox-config-digest/Cargo.toml create mode 100644 proxmox-config-digest/debian/changelog create mode 100644 proxmox-config-digest/debian/control create mode 100644 proxmox-config-digest/debian/copyright create mode 100644 proxmox-config-digest/debian/debcargo.toml create mode 100644 proxmox-config-digest/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4d0a2538..ba0f95a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "proxmox-borrow", "proxmox-client", "proxmox-compression", + "proxmox-config-digest", "proxmox-http", "proxmox-http-error", "proxmox-human-byte", @@ -113,6 +114,7 @@ proxmox-io = { version = "1.0.0", path = "proxmox-io" } proxmox-lang = { version = "1.1", path = "proxmox-lang" } proxmox-login = { version = "0.1.0", path = "proxmox-login" } proxmox-product-config = { version = "0.1.0", path = "proxmox-product-config" } +proxmox-config-digest = { version = "0.1.0", path = "proxmox-config-digest" } proxmox-rest-server = { version = "0.5.2", path = "proxmox-rest-server" } proxmox-router = { version = "2.1.3", path = "proxmox-router" } proxmox-schema = { version = "3.1.1", path = "proxmox-schema" } diff --git a/proxmox-config-digest/Cargo.toml b/proxmox-config-digest/Cargo.toml new file mode 100644 index 00000000..ea5d0a1b --- /dev/null +++ b/proxmox-config-digest/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "proxmox-config-digest" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +description = "Configuration file digest API type." + +exclude.workspace = true + +[dependencies] +anyhow.workspace = true +hex.workspace = true +serde.workspace = true +serde_plain.workspace = true +proxmox-schema = { workspace = true, features = ["api-types"] } + +# feature "openssl", allows to compute the SHA256 digest +openssl = { workspace = true, optional = true } diff --git a/proxmox-config-digest/debian/changelog b/proxmox-config-digest/debian/changelog new file mode 100644 index 00000000..039514c1 --- /dev/null +++ b/proxmox-config-digest/debian/changelog @@ -0,0 +1,5 @@ +rust-proxmox-config-digest (0.1.0-1) bookworm; urgency=medium + + * initial packaging (split out from proxmox-product-config) + + -- Proxmox Support Team Wed, 29 May 2024 17:49:33 +0200 diff --git a/proxmox-config-digest/debian/control b/proxmox-config-digest/debian/control new file mode 100644 index 00000000..afdbff53 --- /dev/null +++ b/proxmox-config-digest/debian/control @@ -0,0 +1,59 @@ +Source: rust-proxmox-config-digest +Section: rust +Priority: optional +Build-Depends: debhelper (>= 12), + dh-cargo (>= 25), + cargo:native , + rustc:native , + libstd-rust-dev , + librust-anyhow-1+default-dev , + librust-hex-0.4+default-dev , + librust-proxmox-schema-3+api-types-dev (>= 3.1.1-~~) , + librust-proxmox-schema-3+default-dev (>= 3.1.1-~~) , + librust-serde-1+default-dev , + librust-serde-plain-1+default-dev +Maintainer: Proxmox Support Team +Standards-Version: 4.6.2 +Vcs-Git: git://git.proxmox.com/git/proxmox.git +Vcs-Browser: https://git.proxmox.com/?p=proxmox.git +X-Cargo-Crate: proxmox-config-digest +Rules-Requires-Root: no + +Package: librust-proxmox-config-digest-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-anyhow-1+default-dev, + librust-hex-0.4+default-dev, + librust-proxmox-schema-3+api-types-dev (>= 3.1.1-~~), + librust-proxmox-schema-3+default-dev (>= 3.1.1-~~), + librust-serde-1+default-dev, + librust-serde-plain-1+default-dev +Suggests: + librust-proxmox-config-digest+openssl-dev (= ${binary:Version}) +Provides: + librust-proxmox-config-digest+default-dev (= ${binary:Version}), + librust-proxmox-config-digest-0-dev (= ${binary:Version}), + librust-proxmox-config-digest-0+default-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1+default-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1.0-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1.0+default-dev (= ${binary:Version}) +Description: Configuration file digest API type - Rust source code + Source code for Debianized Rust crate "proxmox-config-digest" + +Package: librust-proxmox-config-digest+openssl-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-proxmox-config-digest-dev (= ${binary:Version}), + librust-openssl-0.10+default-dev +Provides: + librust-proxmox-config-digest-0+openssl-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1+openssl-dev (= ${binary:Version}), + librust-proxmox-config-digest-0.1.0+openssl-dev (= ${binary:Version}) +Description: Configuration file digest API type - feature "openssl" + This metapackage enables feature "openssl" for the Rust proxmox-config-digest + crate, by pulling in any additional dependencies needed by that feature. diff --git a/proxmox-config-digest/debian/copyright b/proxmox-config-digest/debian/copyright new file mode 100644 index 00000000..0d9eab3e --- /dev/null +++ b/proxmox-config-digest/debian/copyright @@ -0,0 +1,18 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: + * +Copyright: 2019 - 2023 Proxmox Server Solutions GmbH +License: AGPL-3.0-or-later + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + . + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + . + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . diff --git a/proxmox-config-digest/debian/debcargo.toml b/proxmox-config-digest/debian/debcargo.toml new file mode 100644 index 00000000..b7864cdb --- /dev/null +++ b/proxmox-config-digest/debian/debcargo.toml @@ -0,0 +1,7 @@ +overlay = "." +crate_src_path = ".." +maintainer = "Proxmox Support Team " + +[source] +vcs_git = "git://git.proxmox.com/git/proxmox.git" +vcs_browser = "https://git.proxmox.com/?p=proxmox.git" diff --git a/proxmox-config-digest/src/lib.rs b/proxmox-config-digest/src/lib.rs new file mode 100644 index 00000000..d583412a --- /dev/null +++ b/proxmox-config-digest/src/lib.rs @@ -0,0 +1,112 @@ +use anyhow::{bail, Error}; + +#[cfg(feature = "openssl")] +use openssl::sha; + +use proxmox_schema::api_types::SHA256_HEX_REGEX; +use proxmox_schema::ApiStringFormat; +use proxmox_schema::ApiType; +use proxmox_schema::Schema; +use proxmox_schema::StringSchema; + +pub const PROXMOX_CONFIG_DIGEST_FORMAT: ApiStringFormat = + ApiStringFormat::Pattern(&SHA256_HEX_REGEX); + +pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new( + "Prevent changes if current configuration file has different \ + SHA256 digest. This can be used to prevent concurrent \ + modifications.", +) +.format(&PROXMOX_CONFIG_DIGEST_FORMAT) +.schema(); + +#[derive(Clone, Debug, Eq, PartialEq)] +/// A configuration digest - a SHA256 hash. +pub struct ConfigDigest([u8; 32]); + +impl ConfigDigest { + pub fn to_hex(&self) -> String { + hex::encode(&self.0[..]) + } + + #[cfg(feature = "openssl")] + pub fn from_slice>(data: T) -> ConfigDigest { + let digest = sha::sha256(data.as_ref()); + ConfigDigest(digest) + } + + /// Detect modified configuration files + /// + /// This function fails with a reasonable error message if checksums do not match. + pub fn detect_modification(&self, user_digest: Option<&Self>) -> Result<(), Error> { + if let Some(user_digest) = user_digest { + if user_digest != self { + bail!("detected modified configuration - file changed by other user? Try again."); + } + } + Ok(()) + } +} + +impl ApiType for ConfigDigest { + const API_SCHEMA: Schema = PROXMOX_CONFIG_DIGEST_SCHEMA; +} + +impl From<[u8; 32]> for ConfigDigest { + #[inline] + fn from(digest: [u8; 32]) -> Self { + Self(digest) + } +} + +impl From for [u8; 32] { + #[inline] + fn from(digest: ConfigDigest) -> Self { + digest.0 + } +} + +impl AsRef<[u8]> for ConfigDigest { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8; 32]> for ConfigDigest { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl std::ops::Deref for ConfigDigest { + type Target = [u8; 32]; + + fn deref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl std::ops::DerefMut for ConfigDigest { + fn deref_mut(&mut self) -> &mut [u8; 32] { + &mut self.0 + } +} + +impl std::fmt::Display for ConfigDigest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_hex()) + } +} + +impl std::str::FromStr for ConfigDigest { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + let mut digest = [0u8; 32]; + hex::decode_to_slice(s, &mut digest)?; + Ok(ConfigDigest(digest)) + } +} + +serde_plain::derive_deserialize_from_fromstr!(ConfigDigest, "valid configuration digest"); +serde_plain::derive_serialize_from_display!(ConfigDigest);