mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-31 07:19:06 +00:00
genpolicy: Integrate /etc/passwd from OCI container when setting GIDs
The GID used for the running process in an OCI container is a function of 1. The securityContext.runAsGroup specified in a pod yaml, 2. The UID:GID mapping in /etc/passwd, if present in the container image layers, 3. Zero, even if the userstr specifies a GID. Make our policy engine align with this behavior by: 1. At the registry level, always obtain the GID from the /etc/passwd file if present. Ignore GIDs specified in the userstr encoded in the OCI container. 2. After an update to UID due to securityContexts, perform one final check against the /etc/passwd file if present. The GID used for the running process is the mapping in this file from UID->GID. 3. Override everything above with the GID of the securityContext configuration if provided Signed-off-by: Cameron Baird <cameronbaird@microsoft.com>
This commit is contained in:
parent
c13d7796ee
commit
eb2c7f4150
@ -34,6 +34,13 @@
|
||||
"io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)"
|
||||
},
|
||||
"Process": {
|
||||
"NoNewPrivileges": true,
|
||||
"User": {
|
||||
"UID": 65535,
|
||||
"GID": 65535,
|
||||
"AdditionalGids": [],
|
||||
"Username": ""
|
||||
},
|
||||
"Args": [
|
||||
"/pause"
|
||||
]
|
||||
|
@ -148,10 +148,11 @@ impl yaml::K8sResource for CronJob {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.jobTemplate.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -148,8 +148,12 @@ impl yaml::K8sResource for DaemonSet {
|
||||
.or_else(|| Some(String::new()))
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -146,8 +146,12 @@ impl yaml::K8sResource for Deployment {
|
||||
.or_else(|| Some(String::new()))
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -111,8 +111,12 @@ impl yaml::K8sResource for Job {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -911,8 +911,8 @@ impl yaml::K8sResource for Pod {
|
||||
.or_else(|| Some(String::new()))
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(process, &self.spec.securityContext, must_check_passwd);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<Sysctl> {
|
||||
@ -970,6 +970,19 @@ impl Container {
|
||||
if let Some(context) = &self.securityContext {
|
||||
if let Some(uid) = context.runAsUser {
|
||||
process.User.UID = uid.try_into().unwrap();
|
||||
// Changing the UID can break the GID mapping
|
||||
// if a /etc/passwd file is present.
|
||||
// The proper GID is determined, in order of preference:
|
||||
// 1. the securityContext runAsGroup field (applied last in code)
|
||||
// 2. lacking an explicit runAsGroup, /etc/passwd (get_gid_from_passwd_uid)
|
||||
// 3. fall back to pod-level GID if there is one (unwrap_or)
|
||||
//
|
||||
// This behavior comes from the containerd runtime implementation:
|
||||
// WithUser https://github.com/containerd/containerd/blob/main/pkg/oci/spec_opts.go#L592
|
||||
process.User.GID = self
|
||||
.registry
|
||||
.get_gid_from_passwd_uid(process.User.UID)
|
||||
.unwrap_or(process.User.GID);
|
||||
}
|
||||
|
||||
if let Some(gid) = context.runAsGroup {
|
||||
|
@ -713,7 +713,17 @@ impl AgentPolicy {
|
||||
substitute_args_env_variables(&mut process.Args, &process.Env);
|
||||
|
||||
c_settings.get_process_fields(&mut process);
|
||||
resource.get_process_fields(&mut process);
|
||||
let mut must_check_passwd = false;
|
||||
resource.get_process_fields(&mut process, &mut must_check_passwd);
|
||||
|
||||
// The actual GID of the process run by the CRI
|
||||
// Depends on the contents of /etc/passwd in the container
|
||||
if must_check_passwd {
|
||||
process.User.GID = yaml_container
|
||||
.registry
|
||||
.get_gid_from_passwd_uid(process.User.UID)
|
||||
.unwrap_or(0);
|
||||
}
|
||||
yaml_container.get_process_fields(&mut process);
|
||||
|
||||
process
|
||||
|
@ -356,6 +356,12 @@ impl Container {
|
||||
|
||||
debug!("Parsing gid from user[1] = {:?}", user[1]);
|
||||
process.User.GID = self.parse_group_string(user[1]);
|
||||
|
||||
debug!(
|
||||
"Overriding OCI container GID with UID:GID mapping from /etc/passwd"
|
||||
);
|
||||
process.User.GID =
|
||||
self.get_gid_from_passwd_uid(process.User.UID).unwrap_or(0);
|
||||
}
|
||||
} else {
|
||||
debug!("Parsing uid from image_user = {}", image_user);
|
||||
|
@ -109,8 +109,12 @@ impl yaml::K8sResource for ReplicaSet {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -111,8 +111,12 @@ impl yaml::K8sResource for ReplicationController {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -193,8 +193,12 @@ impl yaml::K8sResource for StatefulSet {
|
||||
.or_else(|| Some(String::new()))
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess) {
|
||||
yaml::get_process_fields(process, &self.spec.template.spec.securityContext);
|
||||
fn get_process_fields(&self, process: &mut policy::KataProcess, must_check_passwd: &mut bool) {
|
||||
yaml::get_process_fields(
|
||||
process,
|
||||
&self.spec.template.spec.securityContext,
|
||||
must_check_passwd,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_sysctls(&self) -> Vec<pod::Sysctl> {
|
||||
|
@ -96,7 +96,11 @@ pub trait K8sResource {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_process_fields(&self, _process: &mut policy::KataProcess) {
|
||||
fn get_process_fields(
|
||||
&self,
|
||||
_process: &mut policy::KataProcess,
|
||||
_must_check_passwd: &mut bool,
|
||||
) {
|
||||
// No need to implement support for securityContext or similar fields
|
||||
// for some of the K8s resource types.
|
||||
}
|
||||
@ -386,14 +390,32 @@ fn handle_unused_field(path: &str, silent_unsupported_fields: bool) {
|
||||
pub fn get_process_fields(
|
||||
process: &mut policy::KataProcess,
|
||||
security_context: &Option<pod::PodSecurityContext>,
|
||||
must_check_passwd: &mut bool,
|
||||
) {
|
||||
if let Some(context) = security_context {
|
||||
if let Some(uid) = context.runAsUser {
|
||||
process.User.UID = uid.try_into().unwrap();
|
||||
// Changing the UID can break the GID mapping
|
||||
// if a /etc/passwd file is present.
|
||||
// The proper GID is determined, in order of preference:
|
||||
// 1. the securityContext runAsGroup field (applied last in code)
|
||||
// 2. lacking an explicit runAsGroup, /etc/passwd
|
||||
// (parsed in policy::get_container_process())
|
||||
// 3. lacking an /etc/passwd, 0 (unwrap_or)
|
||||
//
|
||||
// This behavior comes from the containerd runtime implementation:
|
||||
// WithUser https://github.com/containerd/containerd/blob/main/pkg/oci/spec_opts.go#L592
|
||||
//
|
||||
// We can't parse the /etc/passwd file here because
|
||||
// we are in the resource context. Defer execution to outside
|
||||
// the resource context, in policy::get_container_process()
|
||||
// IFF the UID is changed by the resource securityContext but not the GID.
|
||||
*must_check_passwd = true;
|
||||
}
|
||||
|
||||
if let Some(gid) = context.runAsGroup {
|
||||
process.User.GID = gid.try_into().unwrap();
|
||||
*must_check_passwd = false;
|
||||
}
|
||||
|
||||
if let Some(allow) = context.allowPrivilegeEscalation {
|
||||
|
Loading…
Reference in New Issue
Block a user