genpolicy: container.exec_commands args validation

Keep track of individual exec args instead of joining them in the
policy text. Verifying each arg results in a more precise policy,
because some of the args might include space characters.

This improved validation applies to commands specified in K8s YAML
files using:

- livenessProbe
- readinessProbe
- startupProbe
- lifecycle.postStart
- lifecycle.preStop

Signed-off-by: Dan Mihai <dmihai@microsoft.com>
This commit is contained in:
Dan Mihai
2024-07-15 22:59:38 +00:00
parent b23ea508d5
commit 9f4d1ffd43
4 changed files with 9 additions and 16 deletions

View File

@@ -1124,15 +1124,12 @@ ExecProcessRequest {
print("ExecProcessRequest 2: input =", input) print("ExecProcessRequest 2: input =", input)
# TODO: match input container ID with its corresponding container.exec_commands. # TODO: match input container ID with its corresponding container.exec_commands.
i_command = concat(" ", input.process.Args)
print("ExecProcessRequest 3: i_command =", i_command)
some container in policy_data.containers some container in policy_data.containers
some p_command in container.exec_commands some p_command in container.exec_commands
print("ExecProcessRequest 2: p_command =", p_command) print("ExecProcessRequest 2: p_command =", p_command)
# TODO: should other input data fields be validated as well? # TODO: should other input data fields be validated as well?
p_command == i_command p_command == input.process.Args
print("ExecProcessRequest 2: true") print("ExecProcessRequest 2: true")
} }

View File

@@ -614,36 +614,36 @@ impl Container {
(yaml_has_command, yaml_has_args) (yaml_has_command, yaml_has_args)
} }
pub fn get_exec_commands(&self) -> Vec<String> { pub fn get_exec_commands(&self) -> Vec<Vec<String>> {
let mut commands = Vec::new(); let mut commands = Vec::new();
if let Some(probe) = &self.livenessProbe { if let Some(probe) = &self.livenessProbe {
if let Some(exec) = &probe.exec { if let Some(exec) = &probe.exec {
commands.push(exec.command.join(" ")); commands.push(exec.command.clone());
} }
} }
if let Some(probe) = &self.readinessProbe { if let Some(probe) = &self.readinessProbe {
if let Some(exec) = &probe.exec { if let Some(exec) = &probe.exec {
commands.push(exec.command.join(" ")); commands.push(exec.command.clone());
} }
} }
if let Some(probe) = &self.startupProbe { if let Some(probe) = &self.startupProbe {
if let Some(exec) = &probe.exec { if let Some(exec) = &probe.exec {
commands.push(exec.command.join(" ")); commands.push(exec.command.clone());
} }
} }
if let Some(lifecycle) = &self.lifecycle { if let Some(lifecycle) = &self.lifecycle {
if let Some(postStart) = &lifecycle.postStart { if let Some(postStart) = &lifecycle.postStart {
if let Some(exec) = &postStart.exec { if let Some(exec) = &postStart.exec {
commands.push(exec.command.join(" ")); commands.push(exec.command.clone());
} }
} }
if let Some(preStop) = &lifecycle.preStop { if let Some(preStop) = &lifecycle.preStop {
if let Some(exec) = &preStop.exec { if let Some(exec) = &preStop.exec {
commands.push(exec.command.join(" ")); commands.push(exec.command.clone());
} }
} }
} }

View File

@@ -271,7 +271,7 @@ pub struct ContainerPolicy {
/// Allow list of ommand lines that are allowed to be executed using /// Allow list of ommand lines that are allowed to be executed using
/// ExecProcessRequest. By default, all ExecProcessRequest calls are blocked /// ExecProcessRequest. By default, all ExecProcessRequest calls are blocked
/// by the policy. /// by the policy.
exec_commands: Vec<String>, exec_commands: Vec<Vec<String>>,
} }
/// See Reference / Kubernetes API / Config and Storage Resources / Volume. /// See Reference / Kubernetes API / Config and Storage Resources / Volume.

View File

@@ -205,12 +205,8 @@ test_pod_policy_error() {
pod_exec_allowed_command "${pod_name}" "sh" "-c" "ls -l /" pod_exec_allowed_command "${pod_name}" "sh" "-c" "ls -l /"
pod_exec_allowed_command "${pod_name}" "echo" "startupProbe" "test" pod_exec_allowed_command "${pod_name}" "echo" "startupProbe" "test"
# This test should fail but it passes because genpolicy joins the exec args from its
# input K8s YAML file and from the command being executed, and compares the joined
# command lines instead of comparing each argument.
pod_exec_allowed_command "${pod_name}" "echo" "livenessProbe test"
# Try to execute commands disallowed by the policy. # Try to execute commands disallowed by the policy.
pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe test"
pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test2" pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test2"
pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test" "yes" pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test" "yes"
pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test foo" pod_exec_blocked_command "${pod_name}" "echo" "livenessProbe" "test foo"