Skip to content

Commit

Permalink
genpolicy: add bind mounts for image volumes
Browse files Browse the repository at this point in the history
Add bind mounts for volumes defined by docker container images, unless
those mounts have been defined in the input K8s YAML file too.

For example, quay.io/opstree/redis defines two mounts:
/data
/node-conf
Before these changes, if these mounts were not defined in the YAML file
too, the auto-generated policy did not allow this container image to
start.

Signed-off-by: Dan Mihai <[email protected]>
  • Loading branch information
danmihai1 committed Aug 13, 2024
1 parent 9973025 commit ce32a03
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 83 deletions.
12 changes: 12 additions & 0 deletions src/tools/genpolicy/genpolicy-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@
"rprivate",
"ro"
]
},
"image_volume": {
"mount_type": "bind",
"mount_source": "$(sfprefix)",
"driver": "local",
"source": "local",
"fstype": "bind",
"options": [
"rbind",
"rprivate",
"rw"
]
}
},
"mount_destinations": [
Expand Down
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/daemon_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,13 @@ impl yaml::K8sResource for DaemonSet {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
)
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,13 @@ impl yaml::K8sResource for Deployment {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ impl yaml::K8sResource for Job {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
60 changes: 60 additions & 0 deletions src/tools/genpolicy/src/mount_and_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ pub fn get_mount_and_storage(
yaml_volume: &volume::Volume,
yaml_mount: &pod::VolumeMount,
) {
debug!(
"get_mount_and_storage: adding mount and storage for: {:?}",
&yaml_volume
);

if let Some(emptyDir) = &yaml_volume.emptyDir {
let settings_volumes = &settings.volumes;
let mut volume: Option<&settings::EmptyDirVolume> = None;
Expand Down Expand Up @@ -351,3 +356,58 @@ fn get_downward_api_mount(yaml_mount: &pod::VolumeMount, p_mounts: &mut Vec<poli
});
}
}

pub fn get_image_mount_and_storage(
settings: &settings::Settings,
p_mounts: &mut Vec<policy::KataMount>,
storages: &mut Vec<agent::Storage>,
destination: &str,
) {
// https://github.com/kubernetes/examples/blob/master/cassandra/image/Dockerfile
// has a volume mount starting with two '/' characters:
//
// CASSANDRA_DATA=/cassandra_data
// VOLUME ["/$CASSANDRA_DATA"]
let mut destination_string = destination.to_string();
while destination_string.contains("//") {
destination_string = destination_string.replace("//", "/");
}
debug!("get_image_mount_and_storage: image dest = {destination}, dest = {destination_string}");

for mount in &mut *p_mounts {
if mount.destination == destination_string {
debug!(
"get_image_mount_and_storage: mount {destination_string} already defined by YAML"
);
return;
}
}

let settings_image = &settings.volumes.image_volume;
debug!(
"get_image_mount_and_storage: settings for container image volumes: {:?}",
settings_image
);

storages.push(agent::Storage {
driver: settings_image.driver.clone(),
driver_options: Vec::new(),
source: settings_image.source.clone(),
fstype: settings_image.fstype.clone(),
options: settings_image.options.clone(),
mount_point: destination_string.clone(),
fs_group: protobuf::MessageField::none(),
special_fields: ::protobuf::SpecialFields::new(),
});

let file_name = Path::new(&destination_string).file_name().unwrap();
let name = OsString::from(file_name).into_string().unwrap();
let source = format!("{}{name}$", &settings_image.mount_source);

p_mounts.push(policy::KataMount {
destination: destination_string,
type_: settings_image.fstype.clone(),
source,
options: settings_image.options.clone(),
});
}
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/pod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,15 +842,13 @@ impl yaml::K8sResource for Pod {
container: &Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
29 changes: 23 additions & 6 deletions src/tools/genpolicy/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ use oci_distribution::{
};
use serde::{Deserialize, Serialize};
use sha2::{digest::typenum::Unsigned, digest::OutputSizeUser, Sha256};
use std::{fs::OpenOptions, io, io::BufWriter, io::Seek, io::Write, path::Path};
use std::{
collections::BTreeMap, fs::OpenOptions, io, io::BufWriter, io::Seek, io::Write, path::Path,
};
use tokio::io::AsyncWriteExt;

/// Container image properties obtained from an OCI repository.
#[derive(Clone, Debug, Default)]
pub struct Container {
pub image: String,
pub config_layer: DockerConfigLayer,
pub image_layers: Vec<ImageLayer>,
}
Expand All @@ -37,19 +40,20 @@ pub struct Container {
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct DockerConfigLayer {
architecture: String,
config: DockerImageConfig,
pub config: DockerImageConfig,
pub rootfs: DockerRootfs,
}

/// Image config properties.
/// See: https://docs.docker.com/reference/dockerfile/.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
struct DockerImageConfig {
pub struct DockerImageConfig {
User: Option<String>,
Tty: Option<bool>,
Env: Option<Vec<String>>,
Cmd: Option<Vec<String>>,
WorkingDir: Option<String>,
Entrypoint: Option<Vec<String>>,
pub Volumes: Option<BTreeMap<String, DockerVolumeHostDirectory>>,
}

/// Container rootfs information.
Expand All @@ -66,11 +70,21 @@ pub struct ImageLayer {
pub verity_hash: String,
}

/// See https://docs.docker.com/reference/dockerfile/#volume.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DockerVolumeHostDirectory {
// This struct is empty because, according to the documentation:
// "The VOLUME instruction does not support specifying a host-dir
// parameter. You must specify the mountpoint when you create or
// run the container."
}

impl Container {
pub async fn new(config: &Config, image: &str) -> Result<Self> {
info!("============================================");
info!("Pulling manifest and config for {:?}", image);
let reference: Reference = image.to_string().parse().unwrap();
info!("Pulling manifest and config for {image}");
let image_string = image.to_string();
let reference: Reference = image_string.parse().unwrap();
let auth = build_auth(&reference);

let mut client = Client::new(ClientConfig {
Expand All @@ -96,6 +110,8 @@ impl Container {

let config_layer: DockerConfigLayer =
serde_json::from_str(&config_layer_str).unwrap();
debug!("config_layer: {:?}", &config_layer);

let image_layers = get_image_layers(
config.layers_cache_file_path.clone(),
&mut client,
Expand All @@ -107,6 +123,7 @@ impl Container {
.unwrap();

Ok(Container {
image: image_string,
config_layer,
image_layers,
})
Expand Down
4 changes: 3 additions & 1 deletion src/tools/genpolicy/src/registry_containerd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ impl Container {
let ctrd_client = containerd_client::Client::from(containerd_channel.clone());
let k8_cri_image_client = ImageServiceClient::new(containerd_channel);

let image_ref: Reference = image.to_string().parse().unwrap();
let image_str = image.to_string();
let image_ref: Reference = image_str.parse().unwrap();

info!("Pulling image: {:?}", image_ref);

Expand All @@ -67,6 +68,7 @@ impl Container {
.await?;

Ok(Container {
image: image_str,
config_layer,
image_layers,
})
Expand Down
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/replica_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ impl yaml::K8sResource for ReplicaSet {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
16 changes: 7 additions & 9 deletions src/tools/genpolicy/src/replication_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ impl yaml::K8sResource for ReplicationController {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
12 changes: 12 additions & 0 deletions src/tools/genpolicy/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub struct Volumes {
pub emptyDir_memory: EmptyDirVolume,
pub configMap: ConfigMapVolume,
pub confidential_configMap: ConfigMapVolume,
pub image_volume: ImageVolume,
}

/// EmptyDir volume settings loaded from genpolicy-settings.json.
Expand All @@ -59,6 +60,17 @@ pub struct ConfigMapVolume {
pub options: Vec<String>,
}

/// Container image volume settings loaded from genpolicy-settings.json.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImageVolume {
pub mount_type: String,
pub mount_source: String,
pub driver: String,
pub source: String,
pub fstype: String,
pub options: Vec<String>,
}

/// Data corresponding to the kata runtime config file data, loaded from
/// genpolicy-settings.json.
#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
18 changes: 8 additions & 10 deletions src/tools/genpolicy/src/stateful_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,6 @@ impl yaml::K8sResource for StatefulSet {
container: &pod::Container,
settings: &settings::Settings,
) {
if let Some(volumes) = &self.spec.template.spec.volumes {
yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
volumes,
);
}

// Example:
//
// containers:
Expand All @@ -150,6 +140,14 @@ impl yaml::K8sResource for StatefulSet {
StatefulSet::get_mounts_and_storages(policy_mounts, volume_mounts, claims);
}
}

yaml::get_container_mounts_and_storages(
policy_mounts,
storages,
container,
settings,
&self.spec.template.spec.volumes,
);
}

fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String {
Expand Down
Loading

0 comments on commit ce32a03

Please sign in to comment.