mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-14 07:05:47 +00:00
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 <dietmar@proxmox.com>
This commit is contained in:
parent
34b21106dd
commit
4768ad2200
@ -12,19 +12,15 @@ description = "ACME API implementation"
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
base64 = { workspace = true, optional = true }
|
base64 = { workspace = true, optional = true }
|
||||||
hex = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
|
|
||||||
tokio = { workspace = true, optional = true, features = ["fs"] }
|
tokio = { workspace = true, optional = true, features = ["fs"] }
|
||||||
hyper = { workspace = true, optional = true }
|
hyper = { workspace = true, optional = true }
|
||||||
futures = { workspace = true, optional = true }
|
futures = { workspace = true, optional = true }
|
||||||
http = { workspace = true, optional = true }
|
http = { workspace = true, optional = true }
|
||||||
log = { workspace = true, optional = true }
|
log = { workspace = true, optional = true }
|
||||||
nix = { workspace = true, optional = true }
|
nix = { workspace = true, optional = true }
|
||||||
openssl = { workspace = true, optional = true }
|
|
||||||
lazy_static = { workspace = true, optional = true }
|
lazy_static = { workspace = true, optional = true }
|
||||||
|
|
||||||
proxmox-serde = { workspace = true, optional = true }
|
proxmox-serde = { workspace = true, optional = true }
|
||||||
proxmox-section-config = { workspace = true, optional = true }
|
proxmox-section-config = { workspace = true, optional = true }
|
||||||
proxmox-rest-server = { 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-sys = { workspace = true, optional = true }
|
||||||
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
|
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
|
||||||
proxmox-acme = { workspace = true, optional = true, features = ["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 }
|
proxmox-product-config = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -39,13 +36,14 @@ default = ["api-types"]
|
|||||||
api-types = ["dep:proxmox-acme", "dep:proxmox-serde"]
|
api-types = ["dep:proxmox-acme", "dep:proxmox-serde"]
|
||||||
impl = [
|
impl = [
|
||||||
"api-types",
|
"api-types",
|
||||||
|
"dep:proxmox-config-digest",
|
||||||
|
"proxmox-config-digest?/openssl",
|
||||||
"dep:proxmox-product-config",
|
"dep:proxmox-product-config",
|
||||||
"proxmox-product-config?/impl",
|
|
||||||
"dep:proxmox-acme",
|
"dep:proxmox-acme",
|
||||||
"proxmox-acme?/impl",
|
"proxmox-acme?/impl",
|
||||||
"proxmox-acme?/async-client",
|
"proxmox-acme?/async-client",
|
||||||
"dep:proxmox-section-config",
|
"dep:proxmox-section-config",
|
||||||
"dep:openssl",
|
#"dep:openssl",
|
||||||
"dep:lazy_static",
|
"dep:lazy_static",
|
||||||
"dep:log",
|
"dep:log",
|
||||||
"dep:nix",
|
"dep:nix",
|
||||||
@ -57,5 +55,4 @@ impl = [
|
|||||||
"dep:proxmox-rest-server",
|
"dep:proxmox-rest-server",
|
||||||
"dep:proxmox-router",
|
"dep:proxmox-router",
|
||||||
"dep:base64",
|
"dep:base64",
|
||||||
"dep:hex",
|
|
||||||
]
|
]
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
//! ACME plugin configuration API implementation
|
//! ACME plugin configuration API implementation
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use hex::FromHex;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use proxmox_schema::param_bail;
|
use proxmox_schema::param_bail;
|
||||||
|
use proxmox_config_digest::ConfigDigest;
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
DeletablePluginProperty, DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PluginConfig,
|
DeletablePluginProperty, DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PluginConfig,
|
||||||
@ -17,7 +17,7 @@ use proxmox_router::{http_bail, RpcEnvironment};
|
|||||||
pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<PluginConfig>, Error> {
|
pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<PluginConfig>, Error> {
|
||||||
let (plugins, digest) = super::plugin_config::plugin_config()?;
|
let (plugins, digest) = super::plugin_config::plugin_config()?;
|
||||||
|
|
||||||
rpcenv["digest"] = hex::encode(digest).into();
|
rpcenv["digest"] = digest.to_hex().into();
|
||||||
Ok(plugins
|
Ok(plugins
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, (ty, data))| modify_cfg_for_api(id, ty, data))
|
.map(|(id, (ty, data))| modify_cfg_for_api(id, ty, data))
|
||||||
@ -29,7 +29,7 @@ pub fn get_plugin(
|
|||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<PluginConfig, Error> {
|
) -> Result<PluginConfig, Error> {
|
||||||
let (plugins, digest) = super::plugin_config::plugin_config()?;
|
let (plugins, digest) = super::plugin_config::plugin_config()?;
|
||||||
rpcenv["digest"] = hex::encode(digest).into();
|
rpcenv["digest"] = digest.to_hex().into();
|
||||||
|
|
||||||
match plugins.get(&id) {
|
match plugins.get(&id) {
|
||||||
Some((ty, data)) => Ok(modify_cfg_for_api(&id, ty, data)),
|
Some((ty, data)) => Ok(modify_cfg_for_api(&id, ty, data)),
|
||||||
@ -69,7 +69,7 @@ pub fn update_plugin(
|
|||||||
update: DnsPluginCoreUpdater,
|
update: DnsPluginCoreUpdater,
|
||||||
data: Option<String>,
|
data: Option<String>,
|
||||||
delete: Option<Vec<DeletablePluginProperty>>,
|
delete: Option<Vec<DeletablePluginProperty>>,
|
||||||
digest: Option<String>,
|
digest: Option<ConfigDigest>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let data = data
|
let data = data
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@ -83,12 +83,7 @@ pub fn update_plugin(
|
|||||||
|
|
||||||
let (mut plugins, expected_digest) = super::plugin_config::plugin_config()?;
|
let (mut plugins, expected_digest) = super::plugin_config::plugin_config()?;
|
||||||
|
|
||||||
if let Some(digest) = digest {
|
expected_digest.detect_modification(digest.as_ref())?;
|
||||||
let digest = <[u8; 32]>::from_hex(digest)?;
|
|
||||||
if digest != expected_digest {
|
|
||||||
bail!("detected modified configuration - file changed by other user? Try again.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match plugins.get_mut(&id) {
|
match plugins.get_mut(&id) {
|
||||||
Some((ty, ref mut entry)) => {
|
Some((ty, ref mut entry)) => {
|
||||||
|
@ -6,6 +6,7 @@ use serde_json::Value;
|
|||||||
|
|
||||||
use proxmox_schema::{ApiType, Schema};
|
use proxmox_schema::{ApiType, Schema};
|
||||||
use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
|
use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
|
||||||
|
use proxmox_config_digest::ConfigDigest;
|
||||||
use proxmox_product_config::{ApiLockGuard, open_api_lockfile, replace_config};
|
use proxmox_product_config::{ApiLockGuard, open_api_lockfile, replace_config};
|
||||||
|
|
||||||
use crate::types::{DnsPlugin, StandalonePlugin, PLUGIN_ID_SCHEMA};
|
use crate::types::{DnsPlugin, StandalonePlugin, PLUGIN_ID_SCHEMA};
|
||||||
@ -60,13 +61,13 @@ pub(crate) fn lock_plugin_config() -> Result<ApiLockGuard, Error> {
|
|||||||
open_api_lockfile(plugin_cfg_lockfile, None, true)
|
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 plugin_cfg_filename = super::config::plugin_cfg_filename();
|
||||||
|
|
||||||
let content =
|
let content =
|
||||||
proxmox_sys::fs::file_read_optional_string(&plugin_cfg_filename)?.unwrap_or_default();
|
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)?;
|
let mut data = CONFIG.parse(&plugin_cfg_filename, &content)?;
|
||||||
|
|
||||||
if data.sections.get("standalone").is_none() {
|
if data.sections.get("standalone").is_none() {
|
||||||
|
@ -12,14 +12,6 @@ exclude.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
log = { workspace = true, optional = true }
|
log.workspace = true
|
||||||
nix = { workspace = true, optional = true }
|
nix.workspace = true
|
||||||
openssl = { workspace = true, optional = true }
|
proxmox-sys = { workspace = true, features = ["timer"] }
|
||||||
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"]
|
|
||||||
|
@ -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<T: AsRef<[u8]>>(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<ConfigDigest> 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<Self, hex::FromHexError> {
|
|
||||||
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);
|
|
@ -1,12 +1,5 @@
|
|||||||
mod digest;
|
|
||||||
pub use digest::{ConfigDigest, PROXMOX_CONFIG_DIGEST_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA};
|
|
||||||
|
|
||||||
#[cfg(feature = "impl")]
|
|
||||||
mod filesystem_helpers;
|
mod filesystem_helpers;
|
||||||
#[cfg(feature = "impl")]
|
|
||||||
pub use filesystem_helpers::*;
|
pub use filesystem_helpers::*;
|
||||||
|
|
||||||
#[cfg(feature = "impl")]
|
|
||||||
mod product_config;
|
mod product_config;
|
||||||
#[cfg(feature = "impl")]
|
|
||||||
pub use product_config::*;
|
pub use product_config::*;
|
||||||
|
@ -23,37 +23,37 @@ log = { workspace = true, optional = true }
|
|||||||
proxmox-sys = { workspace = true, optional = true }
|
proxmox-sys = { workspace = true, optional = true }
|
||||||
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
|
proxmox-schema = { workspace = true, features = ["api-macro", "api-types"] }
|
||||||
proxmox-time = { workspace = true, optional = true }
|
proxmox-time = { workspace = true, optional = true }
|
||||||
|
proxmox-config-digest = { workspace = true, optional = true }
|
||||||
proxmox-product-config = { workspace = true, optional = true }
|
proxmox-product-config = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
dns-api-types = ["dep:proxmox-product-config"]
|
dns-api-types = ["dep:proxmox-config-digest"]
|
||||||
dns-impl = [
|
dns-impl = [
|
||||||
"dns-api-types",
|
"dns-api-types",
|
||||||
"dep:proxmox-product-config",
|
"dep:proxmox-config-digest",
|
||||||
"proxmox-product-config?/impl",
|
"proxmox-config-digest?/openssl",
|
||||||
"dep:proxmox-sys",
|
"dep:proxmox-sys",
|
||||||
"dep:proxmox-time",
|
"dep:proxmox-time",
|
||||||
]
|
]
|
||||||
time-api-types = []
|
time-api-types = []
|
||||||
time-impl = [
|
time-impl = [
|
||||||
"time-api-types",
|
"time-api-types",
|
||||||
|
"dep:proxmox-config-digest",
|
||||||
|
"proxmox-config-digest?/openssl",
|
||||||
"dep:proxmox-product-config",
|
"dep:proxmox-product-config",
|
||||||
"proxmox-product-config?/impl",
|
|
||||||
"dep:proxmox-sys",
|
"dep:proxmox-sys",
|
||||||
"dep:proxmox-time",
|
"dep:proxmox-time",
|
||||||
]
|
]
|
||||||
network-api-types = []
|
network-api-types = []
|
||||||
network-impl = [
|
network-impl = [
|
||||||
"network-api-types",
|
"network-api-types",
|
||||||
|
"dep:proxmox-config-digest",
|
||||||
|
"proxmox-config-digest?/openssl",
|
||||||
"dep:proxmox-product-config",
|
"dep:proxmox-product-config",
|
||||||
"proxmox-product-config?/impl",
|
|
||||||
"dep:nix",
|
"dep:nix",
|
||||||
"dep:libc",
|
"dep:libc",
|
||||||
"dep:proxmox-sys",
|
"dep:proxmox-sys",
|
||||||
]
|
]
|
||||||
syslog-api-types = []
|
syslog-api-types = []
|
||||||
syslog-impl = [
|
syslog-impl = ["syslog-api-types", "dep:log"]
|
||||||
"syslog-api-types",
|
|
||||||
"dep:log",
|
|
||||||
]
|
|
||||||
|
@ -5,7 +5,7 @@ use proxmox_schema::api_types::IP_FORMAT;
|
|||||||
use proxmox_schema::Schema;
|
use proxmox_schema::Schema;
|
||||||
use proxmox_schema::StringSchema;
|
use proxmox_schema::StringSchema;
|
||||||
|
|
||||||
use proxmox_product_config::ConfigDigest;
|
use proxmox_config_digest::ConfigDigest;
|
||||||
|
|
||||||
pub const SEARCH_DOMAIN_SCHEMA: Schema =
|
pub const SEARCH_DOMAIN_SCHEMA: Schema =
|
||||||
StringSchema::new("Search domain for host-name lookup.").schema();
|
StringSchema::new("Search domain for host-name lookup.").schema();
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::Mutex;
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use proxmox_product_config::ConfigDigest;
|
use proxmox_config_digest::ConfigDigest;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use proxmox_sys::fs::file_get_contents;
|
use proxmox_sys::fs::file_get_contents;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use anyhow::{bail, Error};
|
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::{self, parse_vlan_id_from_name, parse_vlan_raw_device_from_name};
|
||||||
use crate::network::{
|
use crate::network::{
|
||||||
|
@ -20,9 +20,8 @@ use helper::compute_file_diff;
|
|||||||
use helper::get_network_interfaces;
|
use helper::get_network_interfaces;
|
||||||
use parser::NetworkParser;
|
use parser::NetworkParser;
|
||||||
|
|
||||||
use proxmox_product_config::{
|
use proxmox_config_digest::ConfigDigest;
|
||||||
open_api_lockfile, replace_system_config, ApiLockGuard, ConfigDigest,
|
use proxmox_product_config::{open_api_lockfile, replace_system_config, ApiLockGuard};
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
|
static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user