Skip to content
Draft
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
2 changes: 1 addition & 1 deletion src/cloud-resources/src/crd/materialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ pub mod v1 {
}
}

fn parse_image_ref(image_ref: &str) -> Option<Version> {
pub fn parse_image_ref(image_ref: &str) -> Option<Version> {
image_ref
.rsplit_once(':')
.and_then(|(_repo, tag)| tag.strip_prefix('v'))
Expand Down
69 changes: 63 additions & 6 deletions src/orchestratord/src/controller/balancer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use mz_cloud_resources::crd::{
ManagedResource,
balancer::v1alpha1::{Balancer, Routing},
generated::cert_manager::certificates::{Certificate, CertificatePrivateKeyAlgorithm},
materialize::parse_image_ref,
};
use mz_orchestrator_kubernetes::KubernetesImagePullPolicy;
use mz_ore::{cli::KeyValueArg, instrument};
Expand Down Expand Up @@ -160,6 +161,34 @@ impl Context {
)
}

fn pod_uid_gid(image_ref: &str) -> i64 {
// Distroless images (v26.18+) run as the `nonroot` user (uid/gid 65534).
// Older Ubuntu-based images use the `materialize` user (uid/gid 999).
// Note: balancerd transitioned to distroless earlier than
// environmentd/clusterd (which use V26_32_0 in generation.rs).
// Verified against release history: balancerd's ci/Dockerfile switched
// to distroless-prod-base in v26.18.0 (prod-base in v26.17.x).
static V26_18_0: std::sync::LazyLock<semver::Version> =
std::sync::LazyLock::new(|| semver::Version {
major: 26,
minor: 18,
patch: 0,
pre: semver::Prerelease::new("dev.0").expect("dev.0 is valid prerelease"),
build: semver::BuildMetadata::new("").expect("empty string is valid buildmetadata"),
});
let is_distroless = match parse_image_ref(image_ref) {
Some(v) => v.cmp_precedence(&V26_18_0).is_ge(),
None => {
tracing::warn!(
image_ref,
"failed to parse balancerd image ref; assuming distroless"
);
true
}
};
if is_distroless { 65534 } else { 999 }
}

fn create_deployment_object(&self, balancer: &Balancer) -> anyhow::Result<Deployment> {
let security_context = if self.config.enable_security_context {
// Since we want to adhere to the most restrictive security context, all
Expand Down Expand Up @@ -392,12 +421,15 @@ impl Context {
),
affinity: self.config.balancerd_affinity.clone(),
tolerations: self.config.balancerd_tolerations.clone(),
security_context: Some(PodSecurityContext {
fs_group: Some(999),
run_as_user: Some(999),
run_as_group: Some(999),
..Default::default()
}),
security_context: {
let uid_gid = Self::pod_uid_gid(&balancer.spec.balancerd_image_ref);
Some(PodSecurityContext {
fs_group: Some(uid_gid),
run_as_user: Some(uid_gid),
run_as_group: Some(uid_gid),
..Default::default()
})
},
scheduler_name: self.config.scheduler_name.clone(),
volumes: Some(volumes),
..Default::default()
Expand Down Expand Up @@ -601,3 +633,28 @@ impl k8s_controller::Context for Context {
Ok(None)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[mz_ore::test]
fn test_pod_uid_gid() {
// Boundary: v26.18.0 is the first distroless balancerd version
assert_eq!(Context::pod_uid_gid("materialize/balancerd:v26.17.0"), 999);
assert_eq!(
Context::pod_uid_gid("materialize/balancerd:v26.18.0"),
65534
);
// Pre-release below threshold gets Ubuntu
assert_eq!(
Context::pod_uid_gid("materialize/balancerd:v26.18.0-dev"),
999
);
// Unparseable refs assume distroless
assert_eq!(
Context::pod_uid_gid("materialize/balancerd@sha256:abc"),
65534
);
}
}
36 changes: 31 additions & 5 deletions src/orchestratord/src/controller/materialize/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ static PER_ROUTE_GROUP_ROLES_VERSION: LazyLock<Version> = LazyLock::new(|| Versi
build: BuildMetadata::new("").expect("empty string is valid buildmetadata"),
});

/// Minimum version for distroless environmentd/clusterd images (nonroot
/// uid/gid 65534). Balancerd transitioned earlier at V26_18_0 (see
/// balancer.rs).
static V26_32_0: LazyLock<Version> = LazyLock::new(|| Version {
major: 26,
minor: 32,
patch: 0,
pre: Prerelease::new("dev.0").expect("dev.0 is valid prerelease"),
build: BuildMetadata::new("").expect("empty string is valid buildmetadata"),
});

/// Describes the status of a deployment.
///
/// This is a simplified representation of `DeploymentState`, suitable for
Expand Down Expand Up @@ -887,8 +898,23 @@ fn create_environmentd_statefulset_object(
ephemeral_volume_class
));
}
// The `materialize` user used by clusterd always has gid 999.
args.push("--orchestrator-kubernetes-service-fs-group=999".to_string());
// Distroless images (v26.28+) run as the `nonroot` user (uid/gid 65534).
// Older Ubuntu-based images use the `materialize` user (uid/gid 999).
// This value is used for both the environmentd pod security context and
// the --orchestrator-kubernetes-service-fs-group arg (which controls
// clusterd pod security contexts). Both transition at the same version.
// Note: Kubernetes fsGroup re-chowns volume contents on mount, so
// existing PVCs with UID 999 files will be migrated automatically
// (may add startup latency for large volumes).
let service_fs_group: i64 = if mz.meets_minimum_version(&V26_32_0) {
65534
} else {
999
};
args.push(format!(
"--orchestrator-kubernetes-service-fs-group={}",
service_fs_group
));

// Add system_param configmap
// This feature was enabled in 0.163 but did not have testing until after 0.164.
Expand Down Expand Up @@ -1241,9 +1267,9 @@ fn create_environmentd_statefulset_object(
service_account_name: Some(mz.service_account_name()),
volumes: Some(volumes),
security_context: Some(PodSecurityContext {
fs_group: Some(999),
run_as_user: Some(999),
run_as_group: Some(999),
fs_group: Some(service_fs_group),
run_as_user: Some(service_fs_group),
run_as_group: Some(service_fs_group),
..Default::default()
}),
tolerations,
Expand Down
Loading