From a7fd52ccfc4b984bc3417c8259334bff32a1e20f Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sat, 21 Dec 2024 01:23:06 +0100 Subject: [PATCH 01/29] Fixes #26089: Implement augeas module --- Cargo.lock | 8 +- policies/module-types/augeas/Cargo.toml | 4 +- policies/module-types/augeas/README.md | 5 + policies/module-types/augeas/src/augeas.rs | 61 +- policies/module-types/augeas/src/check.rs | 33 -- policies/module-types/augeas/src/dsl.rs | 84 +-- .../module-types/augeas/src/dsl/changes.rs | 529 ++++++++++++++++++ .../module-types/augeas/src/dsl/checks.rs | 57 ++ policies/module-types/augeas/src/main.rs | 1 - .../module-types/augeas/src/parameters.rs | 34 +- 10 files changed, 677 insertions(+), 139 deletions(-) delete mode 100644 policies/module-types/augeas/src/check.rs create mode 100644 policies/module-types/augeas/src/dsl/changes.rs create mode 100644 policies/module-types/augeas/src/dsl/checks.rs diff --git a/Cargo.lock b/Cargo.lock index 044f449165..778731d806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3071,8 +3071,7 @@ dependencies = [ [[package]] name = "raugeas" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0aeef2b5e9cb2aca61802a4622597b2e0be5467b2344d91eb6340d0ea050cb" +source = "git+https://github.com/Normation/raugeas.git#ca2790e6ecc127a9062324f60227a907cc56e7db" dependencies = [ "bitflags 2.6.0", "libc", @@ -3082,8 +3081,7 @@ dependencies = [ [[package]] name = "raugeas_sys" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9dc7ba072353d6dff50ade0ffc23a679c7f645ac0c21ae7edb7efa22f8a1f9" +source = "git+https://github.com/Normation/raugeas.git#ca2790e6ecc127a9062324f60227a907cc56e7db" dependencies = [ "bindgen 0.70.1", "pkg-config", @@ -3302,7 +3300,9 @@ name = "rudder-module-augeas" version = "0.0.0-dev" dependencies = [ "anyhow", + "nom", "raugeas", + "regex", "rudder_commons_test", "rudder_module_type", "serde", diff --git a/policies/module-types/augeas/Cargo.toml b/policies/module-types/augeas/Cargo.toml index 1568c859a4..aa7494b10f 100644 --- a/policies/module-types/augeas/Cargo.toml +++ b/policies/module-types/augeas/Cargo.toml @@ -10,11 +10,13 @@ license.workspace = true [dependencies] anyhow = "1" +nom = "7" serde = { version = "1", features = ["derive"] } serde_json = "1" serde-inline-default = "0.2" +regex = "1.11.1" rudder_module_type = { path = "../../rudder-module-type" } -raugeas = "0.2.2" +raugeas = { git = "https://github.com/Normation/raugeas.git" } [dev-dependencies] rudder_commons_test = { path = "../../rudder-commons-test" } diff --git a/policies/module-types/augeas/README.md b/policies/module-types/augeas/README.md index 6370adf0ef..d7c09eafa5 100644 --- a/policies/module-types/augeas/README.md +++ b/policies/module-types/augeas/README.md @@ -18,3 +18,8 @@ There are different ways to use this module: * By passing `commands`. This is the most flexible way to use the module, but it also bypasses all safeguards and makes reporting less precise. It exposes the full power of Augeas (but also the full danger). Use with caution. + +## License + +from https://github.com/puppetlabs/puppetlabs-augeas_core.git +under Apache-2.0 \ No newline at end of file diff --git a/policies/module-types/augeas/src/augeas.rs b/policies/module-types/augeas/src/augeas.rs index de25035a48..d0541d7d6b 100644 --- a/policies/module-types/augeas/src/augeas.rs +++ b/policies/module-types/augeas/src/augeas.rs @@ -6,8 +6,40 @@ use raugeas::{CommandsNumber, Flags, SaveMode}; use rudder_module_type::{rudder_debug, CheckApplyResult, Outcome, PolicyMode}; use std::env; +/// Augeas module implementation. +/// +/// NOTE: We only support UTF-8 paths and values. This is constrained by +/// the usage of JSON in the API. +/// +/// We don't store the Augeas instance across runs. +/// +/// We never load the tree. Reading the lenses takes time, but is quite convenient, so we offer +/// the options. +/// +/// Below are some metrics for the different ways to run Augeas, based on `augtool` options: +/// +/// * `-L` is for skipping loading the tree. +/// * `-A` is for skipping autoloading lenses (and hence the tree) +/// +/// | Command | Mean \[ms\] | Min \[ms\] | Max \[ms\] | Relative | +/// |:---|---:|---:|---:|---:| +/// | `augtool -LA get /augeas/version` | 2.6 ± 0.5 | 1.6 | 4.6 | 1.00 | +/// | `augtool -L get /augeas/version` | 209.5 ± 5.2 | 200.2 | 221.5 | 80.72 ± 15.16 | +/// | `augtool get /augeas/version` | 663.0 ± 37.2 | 632.0 | 755.7 | 255.46 ± 49.69 | +/// +/// Using: +/// +/// ```shell +/// hyperfine -N --export-markdown augtool.md +/// "augtool -LA get /augeas/version" +/// "augtool -L get /augeas/version" +/// "augtool get /augeas/version" +/// ``` pub struct Augeas {} +// TODO: study allowing to store the instance and keeping it until we see +// potential problems (e.g. commands). + impl Augeas { pub(crate) fn new() -> anyhow::Result { // Cleanup the environment first to avoid any interference. @@ -43,7 +75,9 @@ impl Augeas { flags.insert(Flags::TYPE_CHECK); } - let mut aug = raugeas::Augeas::init(p.root.as_deref(), &p.load_paths(), flags)?; + let root: Option<&str> = p.root.as_deref(); + // FIXME root + let mut aug = raugeas::Augeas::init("/", &p.load_paths(), flags)?; // Show version for debugging purposes. let version = aug.version()?; @@ -77,12 +111,14 @@ impl Augeas { // * We guarantee that the policy mode is respected. ////////////////////////////////// - let path = p.path.clone().unwrap(); + let lens_opt = p.lens_name(); + let _context = p.context(); + let path = p.path.as_deref().unwrap().to_string(); - if let Some(l) = p.lens() { + if let Some(l) = lens_opt { // If we have a lens, we need to load it and load the file. - aug.set(&format!("/augeas/load/${l}/lens"), &l.to_string())?; - aug.set(&format!("/augeas/load/${l}/incl"), &path.to_string())?; + aug.set(&format!("/augeas/load/${l}/lens"), l.as_ref())?; + aug.set(&format!("/augeas/load/${l}/incl"), &path)?; aug.load()?; } else { // Else load it with the detected lens. @@ -90,10 +126,22 @@ impl Augeas { aug.load_file(&path.to_string())?; } - if !p.changes.is_empty() { + // Do the changes before the checks. + + let do_changes = if !p.change_if.is_empty() { + rudder_debug!("Running change conditions: {:?}", p.change_if); + // FIXME handle policy mode + todo!() + } else { + true + }; + + if do_changes && !p.changes.is_empty() { rudder_debug!("Running changes: {:?}", p.changes); + // FIXME handle policy mode todo!() } + if !p.checks.is_empty() { rudder_debug!("Running checks: {:?}", p.checks); todo!() @@ -112,7 +160,6 @@ impl Augeas { // Get information about changes // make backups - todo!() } } diff --git a/policies/module-types/augeas/src/check.rs b/policies/module-types/augeas/src/check.rs deleted file mode 100644 index f3beccafac..0000000000 --- a/policies/module-types/augeas/src/check.rs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: 2024 Normation SAS - -//! Implements augeas-based checks, used either for audits or conditions. - -/* -pub enum Constraint { - /// Is present - Present, - /// Is absent - Absent, - /// Equals a value - Equal, - /// Does not equal a value - NotEqual, - /// Match a regular expression - Match, - /// Does not match a regular expression - NotMatch, - /// Is greater than a value - GreaterThan, - /// Is greater than or equal to a value - GreaterThanOrEqual, - /// Is less than a value - LessThan, - /// Is less than or equal to a value - LessThanOrEqual, - // Is in a list of values - InList(Vec), - // Is not in a list of forbidden values - NotInList(Vec), -} -*/ diff --git a/policies/module-types/augeas/src/dsl.rs b/policies/module-types/augeas/src/dsl.rs index 4e6ac8bbd2..7d64a08ec7 100644 --- a/policies/module-types/augeas/src/dsl.rs +++ b/policies/module-types/augeas/src/dsl.rs @@ -1,82 +1,2 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: 2024 Normation SAS - -//! Implements two DSLs to define a safe subset of augeas commands -//! and handle checks. - -/* - -use anyhow::Error; -use std::str::FromStr; - -pub type Path = String; -pub type Value = String; -pub type Sub = String; - -pub enum Changes { - Set(Path, Value), - SetM(Path, Sub, Value), - Rm(Path), - Remove(Path), - Clear(Path), - ClearM(Path, Sub), - Touch(Path), - Ins(String, String, Path), - Insert(String, String, Path), - Mv(Path, Path), - Move(Path, Path), - Rename(Path, String), - DefVar(String, Path), - DefNode(String, Path, Value), -} - -impl FromStr for Changes { - type Err = Error; - - fn from_str(_s: &str) -> Result { - todo!() - } -} - - - -from https://github.com/puppetlabs/puppetlabs-augeas_core.git -under Apache-2.0 - -changes: - set --- Sets the value VALUE at location PATH - setm --- Sets multiple nodes (matching SUB relative to PATH) to VALUE - rm --- Removes the node at location PATH - remove --- Synonym for rm - clear --- Sets the node at PATH to NULL, creating it if needed - clearm --- Sets multiple nodes (matching SUB relative to PATH) to NULL - touch --- Creates PATH with the value NULL if it does not exist - ins