Skip to content

Commit

Permalink
Adds mutating settings field.
Browse files Browse the repository at this point in the history
Adds the "allowed_to_mutate" field in the settings. This allows policy server
to check if a policy is allowed to mutate a request. If the policy mutate
the request, but its "allowed_to_mutate" settings is false, the request is
rejected.

Co-authored-by: Flavio Castelli <[email protected]>
  • Loading branch information
jvanz and flavio committed Jan 24, 2022
1 parent da618ad commit 4de21a5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 5 deletions.
39 changes: 38 additions & 1 deletion src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use std::path::{Path, PathBuf};
#[derive(Deserialize, Debug, Clone)]
pub struct Policy {
pub url: String,

#[serde(rename = "allowedToMutate")]
pub allowed_to_mutate: Option<bool>,
#[serde(skip)]
pub wasm_module_path: PathBuf,

Expand Down Expand Up @@ -65,10 +66,46 @@ example:
assert!(!policies.is_empty());

let policy = policies.get("example").unwrap();
assert!(policy.allowed_to_mutate.is_none());
let settings = policy.settings();
assert!(settings.is_some());
}

#[test]
fn test_allowed_to_mutate_settings() {
let input = r#"
---
example:
url: file:///tmp/namespace-validate-policy.wasm
allowedToMutate: true
settings:
valid_namespace: valid
"#;
let policies: HashMap<String, Policy> = serde_yaml::from_str(input).unwrap();
assert!(!policies.is_empty());

let policy = policies.get("example").unwrap();
assert!(policy.allowed_to_mutate.unwrap());
let settings = policy.settings();
assert!(settings.is_some());

let input2 = r#"
---
example:
url: file:///tmp/namespace-validate-policy.wasm
allowedToMutate: false
settings:
valid_namespace: valid
"#;
let policies2: HashMap<String, Policy> = serde_yaml::from_str(input2).unwrap();
assert!(!policies2.is_empty());

let policy2 = policies2.get("example").unwrap();
assert_eq!(policy2.allowed_to_mutate.unwrap(), false);
let settings2 = policy2.settings();
assert!(settings2.is_some());
}

#[test]
fn get_settings_when_empty_map_is_provided() {
let input = r#"
Expand Down
36 changes: 32 additions & 4 deletions src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use policy_evaluator::{
policy_evaluator::{PolicyEvaluator, ValidateRequest},
policy_evaluator_builder::PolicyEvaluatorBuilder,
policy_metadata::Metadata,
validation_response::ValidationResponse,
validation_response::{ValidationResponse, ValidationResponseStatus},
};
use std::collections::HashMap;
use std::fmt;
Expand All @@ -18,8 +18,13 @@ use crate::metrics;
use crate::settings::Policy;
use crate::utils::convert_yaml_map_to_json;

struct PolicyEvaluatorWithSettings {
policy_evaluator: PolicyEvaluator,
allowed_to_mutate: Option<bool>,
}

pub(crate) struct Worker {
evaluators: HashMap<String, PolicyEvaluator>,
evaluators: HashMap<String, PolicyEvaluatorWithSettings>,
channel_rx: Receiver<EvalRequest>,
}

Expand Down Expand Up @@ -118,7 +123,12 @@ impl Worker {
continue;
}

evs.insert(id.to_string(), policy_evaluator);
let policy_evaluator_with_settings = PolicyEvaluatorWithSettings {
policy_evaluator,
allowed_to_mutate: policy.allowed_to_mutate,
};

evs.insert(id.to_string(), policy_evaluator_with_settings);
}

if !evs_errors.is_empty() {
Expand All @@ -137,7 +147,10 @@ impl Worker {
let _enter = span.enter();

let res = match self.evaluators.get_mut(&req.policy_id) {
Some(policy_evaluator) => match serde_json::to_value(req.req.clone()) {
Some(PolicyEvaluatorWithSettings {
policy_evaluator,
allowed_to_mutate,
}) => match serde_json::to_value(req.req.clone()) {
Ok(json) => {
let start_time = Instant::now();
let resp = policy_evaluator.validate(ValidateRequest::new(json));
Expand All @@ -149,6 +162,21 @@ impl Worker {
} else {
None
};
let resp = if mutated && allowed_to_mutate.as_ref() == Some(&false) {
ValidationResponse {
allowed: false,
status: Some(ValidationResponseStatus {
message: Some(format!("Request rejected by policy {}. The policy attempted to mutate the request, but it is currently configured to not allow mutations.", &req.policy_id)),
code: None,
}),
// if `allowed_to_mutate` is false, we are in a validating webhook. If we send a patch, k8s will fail because validating webhook do not expect this field
patch: None,
patch_type: None,
..resp
}
} else {
resp
};
let res = req.resp_chan.send(Some(resp));
let policy_evaluation = metrics::PolicyEvaluation {
policy_name: policy_evaluator.policy.id.clone(),
Expand Down

0 comments on commit 4de21a5

Please sign in to comment.