Accept dynamic primary group in workload audit
This commit is contained in:
parent
9f00e7ab84
commit
865190611f
2 changed files with 67 additions and 13 deletions
|
|
@ -135,6 +135,13 @@ deterministically lowers the constrained launch contract through the configured
|
|||
workload driver; repository-owned raw unit or container-runtime text is not an
|
||||
input.
|
||||
|
||||
For systemd `DynamicUser` workloads, systemd's reported primary user and group
|
||||
names are allocated runtime identities. When a binding declares no fixed state
|
||||
group, Idunn validates that the process primary UID and GID match instead of
|
||||
requiring an empty `Group` property. A declared state group remains an exact
|
||||
name and numeric GID contract because it grants the process its bounded write
|
||||
lease.
|
||||
|
||||
## Operator binding
|
||||
|
||||
An operator binding is static root-admitted configuration, not observed state.
|
||||
|
|
|
|||
|
|
@ -2407,6 +2407,18 @@ pub struct SystemdTransientWorkloadDriver {
|
|||
pub credential_root: PathBuf,
|
||||
}
|
||||
|
||||
fn systemd_group_matches_launch_contract(
|
||||
expected_group: Option<(&str, u32)>,
|
||||
observed_group: &str,
|
||||
process_uid: u32,
|
||||
process_gid: u32,
|
||||
) -> bool {
|
||||
match expected_group {
|
||||
Some((group, group_id)) => observed_group == group && process_gid == group_id,
|
||||
None => process_uid == process_gid,
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SystemdTransientWorkloadDriver {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
@ -3342,13 +3354,15 @@ impl SystemdTransientWorkloadDriver {
|
|||
//
|
||||
// `systemd_user` is deliberately absent: systemd reports the identity it
|
||||
// allocated for a DynamicUser unit, so it is never empty for a running
|
||||
// workload. See the same note in `observe_unit`.
|
||||
// workload. The same applies to `Group` when no fixed state group is
|
||||
// bound. In that case the numeric primary GID must match the allocated
|
||||
// UID; a fixed state group still requires both its exact name and GID.
|
||||
let expected_load_credential = if regular_credential_names.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"[unprintable]"
|
||||
};
|
||||
let launch_contract: [(bool, &str); 28] = [
|
||||
let launch_contract: [(bool, &str); 27] = [
|
||||
(
|
||||
observation.unit_description == expected_description,
|
||||
"unit description",
|
||||
|
|
@ -3358,10 +3372,12 @@ impl SystemdTransientWorkloadDriver {
|
|||
(observation.kill_mode == "mixed", "kill mode"),
|
||||
(observation.dynamic_user, "dynamic user"),
|
||||
(
|
||||
match expected_group {
|
||||
Some(group) => observation.systemd_group == group,
|
||||
None => observation.systemd_group.is_empty(),
|
||||
},
|
||||
systemd_group_matches_launch_contract(
|
||||
expected_group.zip(expected_group_id),
|
||||
&observation.systemd_group,
|
||||
observation.process_uids[0],
|
||||
observation.process_gids[0],
|
||||
),
|
||||
"systemd group",
|
||||
),
|
||||
(
|
||||
|
|
@ -3430,13 +3446,6 @@ impl SystemdTransientWorkloadDriver {
|
|||
observed_credential_names == regular_credential_names,
|
||||
"credential names",
|
||||
),
|
||||
(
|
||||
match expected_group_id {
|
||||
Some(group_id) => observation.process_gids[0] == group_id,
|
||||
None => observation.process_gids[0] == observation.process_uids[0],
|
||||
},
|
||||
"process group id",
|
||||
),
|
||||
];
|
||||
for (holds, clause) in launch_contract {
|
||||
ensure!(
|
||||
|
|
@ -8199,6 +8208,44 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_primary_group_is_checked_by_numeric_identity() {
|
||||
assert!(systemd_group_matches_launch_contract(
|
||||
None,
|
||||
"_du12345",
|
||||
61_000,
|
||||
61_000
|
||||
));
|
||||
assert!(!systemd_group_matches_launch_contract(
|
||||
None,
|
||||
"_du12345",
|
||||
61_000,
|
||||
61_001
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_state_group_keeps_its_name_and_gid_contract() {
|
||||
assert!(systemd_group_matches_launch_contract(
|
||||
Some(("streampixels-service-state", 61_234)),
|
||||
"streampixels-service-state",
|
||||
61_000,
|
||||
61_234
|
||||
));
|
||||
assert!(!systemd_group_matches_launch_contract(
|
||||
Some(("streampixels-service-state", 61_234)),
|
||||
"_du12345",
|
||||
61_000,
|
||||
61_234
|
||||
));
|
||||
assert!(!systemd_group_matches_launch_contract(
|
||||
Some(("streampixels-service-state", 61_234)),
|
||||
"streampixels-service-state",
|
||||
61_000,
|
||||
61_235
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn the_published_projection_is_readable_by_a_workload() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue