From 4768ad22003a303ce599a0ccdef402e879fe572d Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 29 May 2024 18:40:11 +0200 Subject: [PATCH] product-config: remove digest implementation (move to proxmox-config-digest crate) And use the new proxmox-config-digest crate instead. Signed-off-by: Dietmar Maurer --- proxmox-acme-api/Cargo.toml | 13 +- proxmox-acme-api/src/plugin_api_impl.rs | 15 +-- proxmox-acme-api/src/plugin_config.rs | 5 +- proxmox-product-config/Cargo.toml | 14 +-- proxmox-product-config/src/digest.rs | 112 ------------------ proxmox-product-config/src/lib.rs | 7 -- proxmox-system-management-api/Cargo.toml | 18 +-- .../src/dns/api_types.rs | 2 +- .../src/dns/resolv_conf.rs | 2 +- .../src/network/api_impl.rs | 2 +- .../src/network/config/mod.rs | 5 +- 11 files changed, 30 insertions(+), 165 deletions(-) delete mode 100644 proxmox-product-config/src/digest.rs diff --git a/proxmox-acme-api/Cargo.toml b/proxmox-acme-api/Cargo.toml index 10f2a302..b3d3fa35 100644 --- a/proxmox-acme-api/Cargo.toml +++ b/proxmox-acme-api/Cargo.toml @@ -12,19 +12,15 @@ description = "ACME API implementation" anyhow.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } - base64 = { workspace = true, optional = true } -hex = { workspace = true, optional = true } - - tokio = { workspace = true, optional = true, features = ["fs"] } hyper = { workspace = true, optional = true } futures = { workspace = true, optional = true } http = { workspace = true, optional = true } log = { workspace = true, optional = true } nix = { workspace = true, optional = true } -openssl = { workspace = true, optional = true } lazy_static = { workspace = true, optional = true } + proxmox-serde = { workspace = true, optional = true } proxmox-section-config = { workspace = true, optional = true } proxmox-rest-server = { workspace = true, optional = true } @@ -32,6 +28,7 @@ proxmox-router = { workspace = true, optional = true } proxmox-sys = { workspace = true, optional = true } proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] } proxmox-acme = { workspace = true, optional = true, features = ["api-types"] } +proxmox-config-digest = { workspace = true, optional = true } proxmox-product-config = { workspace = true, optional = true } [features] @@ -39,13 +36,14 @@ default = ["api-types"] api-types = ["dep:proxmox-acme", "dep:proxmox-serde"] impl = [ "api-types", + "dep:proxmox-config-digest", + "proxmox-config-digest?/openssl", "dep:proxmox-product-config", - "proxmox-product-config?/impl", "dep:proxmox-acme", "proxmox-acme?/impl", "proxmox-acme?/async-client", "dep:proxmox-section-config", - "dep:openssl", + #"dep:openssl", "dep:lazy_static", "dep:log", "dep:nix", @@ -57,5 +55,4 @@ impl = [ "dep:proxmox-rest-server", "dep:proxmox-router", "dep:base64", - "dep:hex", ] diff --git a/proxmox-acme-api/src/plugin_api_impl.rs b/proxmox-acme-api/src/plugin_api_impl.rs index 6205612e..e01ffe1d 100644 --- a/proxmox-acme-api/src/plugin_api_impl.rs +++ b/proxmox-acme-api/src/plugin_api_impl.rs @@ -1,12 +1,12 @@ //! ACME plugin configuration API implementation use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde::Deserialize; use serde_json::Value; use proxmox_schema::param_bail; +use proxmox_config_digest::ConfigDigest; use crate::types::{ DeletablePluginProperty, DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PluginConfig, @@ -17,7 +17,7 @@ use proxmox_router::{http_bail, RpcEnvironment}; pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result, Error> { let (plugins, digest) = super::plugin_config::plugin_config()?; - rpcenv["digest"] = hex::encode(digest).into(); + rpcenv["digest"] = digest.to_hex().into(); Ok(plugins .iter() .map(|(id, (ty, data))| modify_cfg_for_api(id, ty, data)) @@ -29,7 +29,7 @@ pub fn get_plugin( rpcenv: &mut dyn RpcEnvironment, ) -> Result { let (plugins, digest) = super::plugin_config::plugin_config()?; - rpcenv["digest"] = hex::encode(digest).into(); + rpcenv["digest"] = digest.to_hex().into(); match plugins.get(&id) { Some((ty, data)) => Ok(modify_cfg_for_api(&id, ty, data)), @@ -69,7 +69,7 @@ pub fn update_plugin( update: DnsPluginCoreUpdater, data: Option, delete: Option>, - digest: Option, + digest: Option, ) -> Result<(), Error> { let data = data .as_deref() @@ -83,12 +83,7 @@ pub fn update_plugin( let (mut plugins, expected_digest) = super::plugin_config::plugin_config()?; - if let Some(digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - if digest != expected_digest { - bail!("detected modified configuration - file changed by other user? Try again."); - } - } + expected_digest.detect_modification(digest.as_ref())?; match plugins.get_mut(&id) { Some((ty, ref mut entry)) => { diff --git a/proxmox-acme-api/src/plugin_config.rs b/proxmox-acme-api/src/plugin_config.rs index 346752cd..fa4d23db 100644 --- a/proxmox-acme-api/src/plugin_config.rs +++ b/proxmox-acme-api/src/plugin_config.rs @@ -6,6 +6,7 @@ use serde_json::Value; use proxmox_schema::{ApiType, Schema}; use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin}; +use proxmox_config_digest::ConfigDigest; use proxmox_product_config::{ApiLockGuard, open_api_lockfile, replace_config}; use crate::types::{DnsPlugin, StandalonePlugin, PLUGIN_ID_SCHEMA}; @@ -60,13 +61,13 @@ pub(crate) fn lock_plugin_config() -> Result { open_api_lockfile(plugin_cfg_lockfile, None, true) } -pub(crate) fn plugin_config() -> Result<(PluginData, [u8; 32]), Error> { +pub(crate) fn plugin_config() -> Result<(PluginData, ConfigDigest), Error> { let plugin_cfg_filename = super::config::plugin_cfg_filename(); let content = proxmox_sys::fs::file_read_optional_string(&plugin_cfg_filename)?.unwrap_or_default(); - let digest = openssl::sha::sha256(content.as_bytes()); + let digest = ConfigDigest::from_slice(content.as_bytes()); let mut data = CONFIG.parse(&plugin_cfg_filename, &content)?; if data.sections.get("standalone").is_none() { diff --git a/proxmox-product-config/Cargo.toml b/proxmox-product-config/Cargo.toml index f74f1986..25c323a6 100644 --- a/proxmox-product-config/Cargo.toml +++ b/proxmox-product-config/Cargo.toml @@ -12,14 +12,6 @@ exclude.workspace = true [dependencies] anyhow.workspace = true hex.workspace = true -log = { workspace = true, optional = true } -nix = { workspace = true, optional = true } -openssl = { workspace = true, optional = true } -serde.workspace = true -serde_plain.workspace = true -proxmox-sys = { workspace = true, optional = true, features = ["timer"] } -proxmox-schema = { workspace = true, features = ["api-types"] } - -[features] -default = [] -impl = ["dep:log", "dep:nix", "dep:openssl", "dep:proxmox-sys"] +log.workspace = true +nix.workspace = true +proxmox-sys = { workspace = true, features = ["timer"] } diff --git a/proxmox-product-config/src/digest.rs b/proxmox-product-config/src/digest.rs deleted file mode 100644 index 14e2c29f..00000000 --- a/proxmox-product-config/src/digest.rs +++ /dev/null @@ -1,112 +0,0 @@ -use anyhow::{bail, Error}; - -#[cfg(feature = "impl")] -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 = "impl")] - 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); diff --git a/proxmox-product-config/src/lib.rs b/proxmox-product-config/src/lib.rs index f0650a0b..a5c4abb3 100644 --- a/proxmox-product-config/src/lib.rs +++ b/proxmox-product-config/src/lib.rs @@ -1,12 +1,5 @@ -mod digest; -pub use digest::{ConfigDigest, PROXMOX_CONFIG_DIGEST_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA}; - -#[cfg(feature = "impl")] mod filesystem_helpers; -#[cfg(feature = "impl")] pub use filesystem_helpers::*; -#[cfg(feature = "impl")] mod product_config; -#[cfg(feature = "impl")] pub use product_config::*; diff --git a/proxmox-system-management-api/Cargo.toml b/proxmox-system-management-api/Cargo.toml index 4cda8aa4..aa70188e 100644 --- a/proxmox-system-management-api/Cargo.toml +++ b/proxmox-system-management-api/Cargo.toml @@ -23,37 +23,37 @@ log = { workspace = true, optional = true } proxmox-sys = { workspace = true, optional = true } proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] } proxmox-time = { workspace = true, optional = true } +proxmox-config-digest = { workspace = true, optional = true } proxmox-product-config = { workspace = true, optional = true } [features] default = [] -dns-api-types = ["dep:proxmox-product-config"] +dns-api-types = ["dep:proxmox-config-digest"] dns-impl = [ "dns-api-types", - "dep:proxmox-product-config", - "proxmox-product-config?/impl", + "dep:proxmox-config-digest", + "proxmox-config-digest?/openssl", "dep:proxmox-sys", "dep:proxmox-time", ] time-api-types = [] time-impl = [ "time-api-types", + "dep:proxmox-config-digest", + "proxmox-config-digest?/openssl", "dep:proxmox-product-config", - "proxmox-product-config?/impl", "dep:proxmox-sys", "dep:proxmox-time", ] network-api-types = [] network-impl = [ "network-api-types", + "dep:proxmox-config-digest", + "proxmox-config-digest?/openssl", "dep:proxmox-product-config", - "proxmox-product-config?/impl", "dep:nix", "dep:libc", "dep:proxmox-sys", ] syslog-api-types = [] -syslog-impl = [ - "syslog-api-types", - "dep:log", -] +syslog-impl = ["syslog-api-types", "dep:log"] diff --git a/proxmox-system-management-api/src/dns/api_types.rs b/proxmox-system-management-api/src/dns/api_types.rs index 4b9b8fb9..f0ff70f4 100644 --- a/proxmox-system-management-api/src/dns/api_types.rs +++ b/proxmox-system-management-api/src/dns/api_types.rs @@ -5,7 +5,7 @@ use proxmox_schema::api_types::IP_FORMAT; use proxmox_schema::Schema; use proxmox_schema::StringSchema; -use proxmox_product_config::ConfigDigest; +use proxmox_config_digest::ConfigDigest; pub const SEARCH_DOMAIN_SCHEMA: Schema = StringSchema::new("Search domain for host-name lookup.").schema(); diff --git a/proxmox-system-management-api/src/dns/resolv_conf.rs b/proxmox-system-management-api/src/dns/resolv_conf.rs index 50419c50..1b09d07f 100644 --- a/proxmox-system-management-api/src/dns/resolv_conf.rs +++ b/proxmox-system-management-api/src/dns/resolv_conf.rs @@ -4,7 +4,7 @@ use std::sync::Mutex; use anyhow::Error; use const_format::concatcp; use lazy_static::lazy_static; -use proxmox_product_config::ConfigDigest; +use proxmox_config_digest::ConfigDigest; use regex::Regex; use proxmox_sys::fs::file_get_contents; diff --git a/proxmox-system-management-api/src/network/api_impl.rs b/proxmox-system-management-api/src/network/api_impl.rs index 59e55f8d..1a438b81 100644 --- a/proxmox-system-management-api/src/network/api_impl.rs +++ b/proxmox-system-management-api/src/network/api_impl.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Error}; -use proxmox_product_config::ConfigDigest; +use proxmox_config_digest::ConfigDigest; use crate::network::{self, parse_vlan_id_from_name, parse_vlan_raw_device_from_name}; use crate::network::{ diff --git a/proxmox-system-management-api/src/network/config/mod.rs b/proxmox-system-management-api/src/network/config/mod.rs index 848385fb..b53279e2 100644 --- a/proxmox-system-management-api/src/network/config/mod.rs +++ b/proxmox-system-management-api/src/network/config/mod.rs @@ -20,9 +20,8 @@ use helper::compute_file_diff; use helper::get_network_interfaces; use parser::NetworkParser; -use proxmox_product_config::{ - open_api_lockfile, replace_system_config, ApiLockGuard, ConfigDigest, -}; +use proxmox_config_digest::ConfigDigest; +use proxmox_product_config::{open_api_lockfile, replace_system_config, ApiLockGuard}; lazy_static! { static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();