mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-14 16:12:19 +00:00
product-config: add rust API type for configuration digest
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
9fc48d96d2
commit
e64575b6a7
@ -11,6 +11,11 @@ exclude.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
hex.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
nix.workspace = true
|
nix.workspace = true
|
||||||
|
openssl.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_plain.workspace = true
|
||||||
proxmox-sys = { workspace = true, features = [ "timer" ] }
|
proxmox-sys = { workspace = true, features = [ "timer" ] }
|
||||||
|
proxmox-schema = { workspace = true, features = [ "api-types" ] }
|
117
proxmox-product-config/src/digest.rs
Normal file
117
proxmox-product-config/src/digest.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use anyhow::{bail, Error};
|
||||||
|
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[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice<T: AsRef<[u8]>>(data: T) -> ConfigDigest {
|
||||||
|
let digest = sha::sha256(data.as_ref());
|
||||||
|
ConfigDigest(digest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// Detect modified configuration files
|
||||||
|
///
|
||||||
|
/// This function fails with a reasonable error message if checksums do not match.
|
||||||
|
pub fn detect_modified_configuration_file(
|
||||||
|
user_digest: Option<&[u8; 32]>,
|
||||||
|
config_digest: &[u8; 32],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
use hex::FromHex;
|
||||||
|
|
||||||
|
let user_digest = match user_digest {
|
||||||
|
Some(digest) => <[u8; 32]>::from_hex(digest)?,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if user_digest != *config_digest {
|
||||||
|
bail!("detected modified configuration - file changed by other user? Try again.");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -7,6 +7,12 @@ use nix::fcntl::OFlag;
|
|||||||
use nix::sys::stat::Mode;
|
use nix::sys::stat::Mode;
|
||||||
use nix::unistd::{Gid, Uid};
|
use nix::unistd::{Gid, Uid};
|
||||||
|
|
||||||
|
mod digest;
|
||||||
|
pub use digest::{
|
||||||
|
detect_modified_configuration_file, ConfigDigest, PROXMOX_CONFIG_DIGEST_FORMAT,
|
||||||
|
PROXMOX_CONFIG_DIGEST_SCHEMA,
|
||||||
|
};
|
||||||
|
|
||||||
static mut PRODUCT_CONFIG: Option<ProxmoxProductConfig> = None;
|
static mut PRODUCT_CONFIG: Option<ProxmoxProductConfig> = None;
|
||||||
|
|
||||||
/// Initialize the global product configuration.
|
/// Initialize the global product configuration.
|
||||||
@ -43,8 +49,6 @@ impl ProxmoxProductConfig {
|
|||||||
path.push(rel_path);
|
path.push(rel_path);
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check file/directory permissions
|
// Check file/directory permissions
|
||||||
@ -104,7 +108,6 @@ pub fn mkdir_permissions(dir: &str, uid: Uid, gid: Gid, mode: u32) -> Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Atomically write data to file owned by `root:api-user` with permission `0640`
|
/// Atomically write data to file owned by `root:api-user` with permission `0640`
|
||||||
///
|
///
|
||||||
/// Only the superuser can write those files, but group 'api-user' can read them.
|
/// Only the superuser can write those files, but group 'api-user' can read them.
|
||||||
|
Loading…
Reference in New Issue
Block a user