Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions project/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions project/common/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ rust_library(
"//third-party/rust/crates/chrono/0.4.42:chrono",
"//third-party/rust/crates/etcd-client/0.17.0:etcd-client",
"//third-party/rust/crates/ipnetwork/0.17.0:ipnetwork",
"//third-party/rust/crates/libcontainer/0.5.7:libcontainer",
"//third-party/rust/crates/log/0.4.29:log",
"//third-party/rust/crates/quinn/0.11.9:quinn",
"//third-party/rust/crates/serde/1.0.228:serde",
Expand Down
3 changes: 2 additions & 1 deletion project/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ bytes = { workspace = true }
log = { workspace = true }
libvault = { workspace = true }
uuid = { workspace = true, features = ["v4", "serde"] }
etcd-client = { workspace = true }
etcd-client = { workspace = true }
libcontainer = { workspace = true }
85 changes: 85 additions & 0 deletions project/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod _private {
pub mod lease;
pub mod quic;

use libcontainer::oci_spec::runtime::Capability;
pub use libvault::modules::pki::types::{IssueCertificateRequest, IssueCertificateResponse};

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -224,6 +225,74 @@ pub struct Resource {
pub memory: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct SecurityContext {
#[serde(rename = "runAsUser")]
pub run_as_user: Option<i64>,

#[serde(rename = "runAsGroup")]
pub run_as_group: Option<i64>,

#[serde(default)]
pub privileged: Option<bool>,

#[serde(rename = "allowPrivilegeEscalation", default)]
pub allow_privilege_escalation: Option<bool>,
Comment on lines +230 to +240
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SecurityContext fields run_as_user, run_as_group, and allow_privilege_escalation are defined but never used in the OCISpecGenerator implementation. These fields should either be implemented in the OCI spec generation logic or removed if they're planned for future work.

Copilot uses AI. Check for mistakes.

Comment on lines +228 to +241
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SecurityContext struct lacks documentation explaining its fields and their usage. Given that it controls container security settings with fields like privileged mode and capabilities, it should include doc comments describing each field's purpose, valid values, and security implications.

Suggested change
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct SecurityContext {
#[serde(rename = "runAsUser")]
pub run_as_user: Option<i64>,
#[serde(rename = "runAsGroup")]
pub run_as_group: Option<i64>,
#[serde(default)]
pub privileged: Option<bool>,
#[serde(rename = "allowPrivilegeEscalation", default)]
pub allow_privilege_escalation: Option<bool>,
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
/// Container-level security options controlling user/group identity,
/// privilege escalation and Linux capabilities.
///
/// This roughly mirrors the Kubernetes `SecurityContext` semantics and is
/// serialized/deserialized for use in API objects. All fields are optional:
/// if a value is `None`, the runtime-specific defaults or image metadata
/// typically apply.
pub struct SecurityContext {
/// Override for the effective user ID (UID) inside the container.
///
/// When set, the container process will attempt to run as this UID
/// instead of the image's default user. Using `0` (root) grants full
/// privileges inside the container and increases the risk of container
/// breakout or privilege escalation on the host; prefer non-root UIDs
/// wherever possible.
#[serde(rename = "runAsUser")]
pub run_as_user: Option<i64>,
/// Override for the effective primary group ID (GID) inside the container.
///
/// When set, the container process will attempt to run with this GID as
/// its primary group. Similar to `run_as_user`, using a highly privileged
/// group (for example, a group with write access to sensitive paths)
/// can weaken isolation.
#[serde(rename = "runAsGroup")]
pub run_as_group: Option<i64>,
/// Run the container in "privileged" mode when `Some(true)`.
///
/// Privileged containers are granted almost full access to the host,
/// including device access and most kernel capabilities. This effectively
/// disables many isolation mechanisms and should only be enabled for
/// carefully audited workloads. When `None`, the runtime's default
/// (usually non-privileged) is used.
#[serde(default)]
pub privileged: Option<bool>,
/// Allow the container process to gain more privileges than its parent.
///
/// When `Some(false)`, common escalation paths such as setuid/setgid
/// binaries or acquiring new file-based capabilities are blocked,
/// providing stronger confinement. When `Some(true)` (or if left to the
/// runtime default), processes may increase their privileges, which can
/// be necessary for some legacy applications but reduces overall safety.
#[serde(rename = "allowPrivilegeEscalation", default)]
pub allow_privilege_escalation: Option<bool>,
/// Linux capabilities configuration applied to the container.
///
/// Capabilities provide fine-grained control over privileged operations.
/// Use this field to add or drop specific capabilities instead of running
/// the container as fully privileged; only grant the minimal set required
/// by the workload.

Copilot uses AI. Check for mistakes.
pub capabilities: Option<Capabilities>,
}

/// The pattern should be like: "CAP_AUDIT_CONTROL" refers to <http://man7.org/linux/man-pages/man7/capabilities.7.html>
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Capabilities {
#[serde(default)]
pub add: Vec<Capability>, // List of capabilities to add

#[serde(default)]
pub drop: Vec<Capability>, // List of capabilities to drop
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct EnvVar {
pub name: String,

#[serde(default)]
pub value: Option<String>,
Comment on lines +255 to +260
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EnvVar struct lacks documentation. It should include doc comments explaining its purpose and fields, particularly the relationship between value and the commented-out value_from field.

Suggested change
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct EnvVar {
pub name: String,
#[serde(default)]
pub value: Option<String>,
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
/// Describes a single environment variable for a container, following Kubernetes-style semantics.
///
/// An `EnvVar` typically has a `name` and either a literal `value` or a "value from" source
/// (such as a Secret, ConfigMap, or pod field). In this implementation only literal values
/// are supported via [`value`]; the `value_from` mechanism is currently commented out.
///
/// When/if `value_from` is enabled, only one of `value` or `value_from` should be set for
/// a given `EnvVar`, mirroring Kubernetes' `EnvVar` contract.
pub struct EnvVar {
/// Name of the environment variable. This must be unique within the container's
/// environment and should be a valid shell variable identifier (e.g. `MY_VAR`).
pub name: String,
/// Literal value of the environment variable.
///
/// If `None`, the variable may be omitted or populated by other mechanisms (such as
/// defaults or a future `value_from` source). When `value_from` is supported, this
/// field is intended to be mutually exclusive with `value_from`.
#[serde(default)]
pub value: Option<String>,
// Source for the environment variable's value, such as a Secret, ConfigMap, or pod field.
// Only one of `value` or `value_from` should be set when this is enabled.

Copilot uses AI. Check for mistakes.
// #[serde(rename = "valueFrom", default)]
// pub value_from: Option<EnvVarSource>,
}

// #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
// pub struct EnvVarSource {
// #[serde(rename = "secretKeyRef", default)]
// pub secret_key_ref: Option<SecretKeySelector>, // Selects a key of a Secret

// #[serde(rename = "configMapKeyRef", default)]
// pub config_map_key_ref: Option<ConfigMapKeySelector>, // Selects a key of a ConfigMap

// #[serde(rename = "fieldRef", default)]
// pub field_ref: Option<ObjectFieldSelector>, // Selects a field of the Pod/container
// }

// // Placeholder structures for EnvVarSource fields
// pub struct SecretKeySelector { /* ... */ }
// pub struct ConfigMapKeySelector { /* ... */ }
// pub struct ObjectFieldSelector { /* ... */ }

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct VolumeMount {
pub name: String,

#[serde(rename = "mountPath")]
pub mount_path: String,

#[serde(rename = "readOnly", default)]
pub read_only: Option<bool>,

Comment on lines +282 to +291
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VolumeMount struct lacks documentation. It should include doc comments explaining its purpose and fields, particularly what sub_path is used for and how mount_path is interpreted.

Suggested change
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct VolumeMount {
pub name: String,
#[serde(rename = "mountPath")]
pub mount_path: String,
#[serde(rename = "readOnly", default)]
pub read_only: Option<bool>,
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
/// Describes how a named volume is mounted into a container filesystem.
///
/// This is conceptually similar to Kubernetes' `VolumeMount`. The `name` field
/// must match a defined volume, and the volume (or a sub-path within it) is
/// exposed inside the container at `mount_path`.
pub struct VolumeMount {
/// Name of the volume to mount, matching a volume defined in the pod spec.
pub name: String,
/// Absolute path inside the container where the volume (or `sub_path`) is mounted.
///
/// This path is interpreted in the container's filesystem namespace, not on
/// the host. The path must be absolute (e.g. `/data`, `/var/log/app`).
#[serde(rename = "mountPath")]
pub mount_path: String,
/// Whether the container should see this mount as read-only.
///
/// If `None`, the mount defaults to read-write, subject to the underlying
/// volume's capabilities and permissions.
#[serde(rename = "readOnly", default)]
pub read_only: Option<bool>,
/// Optional sub-path within the volume to mount instead of the volume root.
///
/// When set, only this path inside the referenced volume is mounted at
/// `mount_path`. If omitted, the entire volume root is mounted.

Copilot uses AI. Check for mistakes.
#[serde(rename = "subPath", default)]
pub sub_path: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ContainerSpec {
pub name: String,
Expand All @@ -246,6 +315,22 @@ pub struct ContainerSpec {

#[serde(rename = "startupProbe", default)]
pub startup_probe: Option<Probe>,

// Handle the security
#[serde(rename = "securityContext", default)]
pub security_context: Option<SecurityContext>,

#[serde(default)]
pub env: Option<Vec<EnvVar>>,

#[serde(rename = "volumeMounts", default)]
pub volume_mounts: Option<Vec<VolumeMount>>,

#[serde(default)]
pub command: Option<Vec<String>>,

#[serde(rename = "workingDir", default)]
pub working_dir: Option<String>,
Comment on lines +322 to +333
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added ContainerSpec fields (env, volume_mounts, command, working_dir) are not implemented in the OCISpecGenerator. These fields are added to the API but don't affect container behavior, which could lead to user confusion. They should either be implemented or removed if planned for future work.

Suggested change
#[serde(default)]
pub env: Option<Vec<EnvVar>>,
#[serde(rename = "volumeMounts", default)]
pub volume_mounts: Option<Vec<VolumeMount>>,
#[serde(default)]
pub command: Option<Vec<String>>,
#[serde(rename = "workingDir", default)]
pub working_dir: Option<String>,

Copilot uses AI. Check for mistakes.
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
Expand Down
5 changes: 5 additions & 0 deletions project/libscheduler/tests/xline_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ fn create_test_pod(name: &str, cpu_limit: Option<&str>, memory_limit: Option<&st
liveness_probe: None,
readiness_probe: None,
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
}],
init_containers: vec![],
tolerations: vec![],
Expand Down
5 changes: 5 additions & 0 deletions project/rkl/src/commands/compose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ impl ComposeManager {
liveness_probe: None,
readiness_probe: None,
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
};

// handle the services volume name
Expand Down
30 changes: 21 additions & 9 deletions project/rkl/src/commands/container/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::oci;
use crate::{
commands::{
Exec, ExecContainer,
Expand All @@ -9,7 +10,7 @@ use crate::{
},
cri::cri_api::{ContainerConfig, CreateContainerResponse, Mount},
rootpath,
task::{add_cap_net_admin, add_cap_net_raw, get_cni},
task::get_cni,
};
use anyhow::{Ok, Result, anyhow};
use chrono::{DateTime, Local};
Expand Down Expand Up @@ -173,6 +174,11 @@ impl ContainerRunner {
liveness_probe: None,
readiness_probe: None,
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
},
config: None,
container_id: container_id.to_string(),
Expand Down Expand Up @@ -309,11 +315,7 @@ impl ContainerRunner {

// build the process path
let mut process = ProcessBuilder::default().cwd(&config.working_dir).build()?;
let mut capabilities = process.capabilities().clone().unwrap();
// add the CAP_NET_RAW
add_cap_net_raw(&mut capabilities);
add_cap_net_admin(&mut capabilities);

let capabilities = oci::new_linux_capabilities_with_defaults();
process.set_capabilities(Some(capabilities));
process.set_terminal(Some(false));
process.set_args(Some(config.args.clone()));
Expand All @@ -322,7 +324,7 @@ impl ContainerRunner {

spec.set_process(Some(process));

let mut mounts = convert_oci_mounts(&config.mounts)?;
let mut mounts = convert_cri_to_oci_mounts(&config.mounts)?;
let existing_mounts = spec.mounts().clone().unwrap_or_default();
mounts.extend(existing_mounts);
spec.set_mounts(Some(mounts));
Expand Down Expand Up @@ -394,7 +396,7 @@ impl ContainerRunner {
}

pub fn setup_container_network(&self) -> Result<JsonValue> {
// single container status
// If this container is not from compose, then setup the config file again
if self.determine_single_status() {
setup_network_conf()?;
}
Expand Down Expand Up @@ -499,7 +501,7 @@ impl ContainerRunner {
}
}

fn convert_oci_mounts(mounts: &Vec<Mount>) -> Result<Vec<OciMount>> {
fn convert_cri_to_oci_mounts(mounts: &Vec<Mount>) -> Result<Vec<OciMount>> {
let mut oci_mounts: Vec<OciMount> = vec![];
for mount in mounts {
let oci_mount = MountBuilder::default()
Expand Down Expand Up @@ -793,6 +795,11 @@ mod test {
liveness_probe: None,
readiness_probe: None,
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
};
let runner = ContainerRunner::from_spec(spec.clone(), None).unwrap();
assert_eq!(runner.container_id, "demo1");
Expand All @@ -819,6 +826,11 @@ mod test {
liveness_probe: None,
readiness_probe: None,
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
},
None,
)
Expand Down
5 changes: 5 additions & 0 deletions project/rkl/src/daemon/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,11 @@ mod tests {
..Probe::default()
}),
startup_probe: None,
security_context: None,
env: None,
volume_mounts: None,
command: None,
working_dir: None,
}],
init_containers: vec![],
tolerations: vec![],
Expand Down
1 change: 1 addition & 0 deletions project/rkl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod commands;
pub mod cri;
pub mod daemon;
pub mod network;
pub mod oci;
mod rootpath;
pub mod task;
//mod status_access;
Expand Down
1 change: 1 addition & 0 deletions project/rkl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod cri;
mod daemon;
mod dns;
mod network;
mod oci;
mod quic;
mod rootpath;
mod task;
Expand Down
Loading
Loading