Skip to content

Conversation

@gursewak1997
Copy link
Contributor

@gursewak1997 gursewak1997 commented Nov 14, 2025

Add check to prevent soft reboot when SELinux policies differ between booted and target deployments, since policy is not reloaded across soft reboots.

Fixes: #1760

@bootc-bot bootc-bot bot requested a review from jmarrero November 14, 2025 18:47
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds a check to prevent soft reboots when there's a change in SELinux policy between the booted and target deployments. This is a valuable safety measure. The implementation introduces a new function check_selinux_policy_for_soft_reboot and integrates it into the soft reboot logic for both staged updates and rollbacks.

My main feedback is on the logic within check_selinux_policy_for_soft_reboot, which I found to be too permissive. It incorrectly allows a soft reboot when one deployment has an SELinux policy and the other does not. This could lead to a system running in an insecure state. I've provided a critical-severity comment with a suggested code change to address this.

@gursewak1997 gursewak1997 force-pushed the fix-1760-detect-selinux-policy-deltas-soft-reboot branch 2 times, most recently from 7001036 to 770289f Compare November 14, 2025 18:58
Copy link
Collaborator

@cgwalters cgwalters left a comment

Choose a reason for hiding this comment

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

I know this raises the bar for landing this, but we really need to have tests when landing changes as a general rule.

We could definitely handle this in a better way but because right now of the two primary test frameworks (GHA and TF) neither offer an elegant way to have a registry by default (though we could of course) we end up just building images locally and booting those.

Simplest way to test this is to inject a local selinux policy module, there's various docs online for this.

let target_policy = crate::lsm::new_sepolicy_at(&target_fd)?;

// If either deployment doesn't have a policy, we can't compare
// In this case, we'll allow soft reboot (policy will be loaded on boot)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think that's true, we should not allow soft reboot in the degenerate case that selinux is enabled in one but not the other.

/// Check if SELinux policy differs between booted and target deployments.
/// Returns an error if SELinux is enabled and the policies differ.
#[context("Checking SELinux policy compatibility with soft reboot")]
fn check_selinux_policy_for_soft_reboot(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This helps, but I think what we really want is to change has_soft_reboot_capability.

But then that raises the next thing in that in theory this change should go in libostree which has a deployment_can_soft_reboot API which already does checks like this.

OTOH, I don't mind filling it in here either, we can just have a // TODO lower into ostree to start.

The other thing of course related to this is that when we get around to soft reboots + composefs, we'll need similar logic there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made some changes pertaining to this.

@gursewak1997 gursewak1997 force-pushed the fix-1760-detect-selinux-policy-deltas-soft-reboot branch 6 times, most recently from 3fdbb49 to dcbe65e Compare November 14, 2025 20:18
return true;
}

let Ok(booted_fd) = crate::utils::deployment_fd(sysroot, booted_deployment) else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't expect this operation to ever fail, I don't think we should "swallow errors" in a case like this.

let Ok(booted_fd) = crate::utils::deployment_fd(sysroot, booted_deployment) else {
return false; // Can't check, be conservative
};
let Ok(booted_policy) = crate::lsm::new_sepolicy_at(&booted_fd) else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This one...hmmm...it'd be quite surprising to me if we failed to init the selinux policy - and if we keep the code as is, failure to do so should at least use our log_err_or_else helper (which hmm I guess could nowadays e.g. at least always log to the systemd journal?).

I would say though we should just keep using the ? here too.

}
(Some(booted), Some(target)) => {
// Both have policies, checksums must match
// SAFETY: new_sepolicy_at filters out policies without checksums
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes though I think we could do

let booted_policy = booted_policy.map(|p| p.csum());
let target_policy = target_policy(|p| p.csum());

And then we just compare those two

# Inject a local SELinux policy change by modifying file_contexts
# This will change the policy checksum between deployments
RUN mkdir -p /opt/bootc-test-selinux-policy && \
echo '/opt/bootc-test-selinux-policy /opt/bootc-test-selinux-policy' >> /etc/selinux/targeted/contexts/files/file_contexts.subs_dist || true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the || true? In general I don't think we should ignore errors


bootc image copy-to-storage

# Create a derived container that doesn't change the policy
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're already covering the same-policy scenario in most other tests, I don't think we need to do this.

@gursewak1997 gursewak1997 force-pushed the fix-1760-detect-selinux-policy-deltas-soft-reboot branch 2 times, most recently from 6c48e6e to 006d882 Compare November 17, 2025 23:01
@gursewak1997
Copy link
Contributor Author

The test fails because modifying file_contexts.local doesn't change the compiled policy checksum that ostree uses. The checksum comes from the compiled policy binary, not source files. We need a different approach to change the policy checksum (e.g., installing a policy module or using different base images).
The implementation in status.rs is correct afaik

@cgwalters
Copy link
Collaborator

The test fails because modifying file_contexts.local doesn't change the compiled policy checksum that ostree uses. The checksum comes from the compiled policy binary, not source files. We need a different approach to change the policy checksum (e.g., installing a policy module or using different base images).

Right let's install a policy module then.

https://github.com/containers/composefs-rs/blob/0f636031a1ec81cdd9e7f674909ef6b75c2642cb/examples/uki/Containerfile#L49
is one example of this

@gursewak1997 gursewak1997 force-pushed the fix-1760-detect-selinux-policy-deltas-soft-reboot branch from 006d882 to dd6e419 Compare November 18, 2025 22:07
# Create a minimal SELinux policy module that will change the policy checksum
# We install it to ensure it's part of the deployment filesystem
RUN mkdir -p /tmp/bootc-test-policy && \\
Copy link
Collaborator

Choose a reason for hiding this comment

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

This one cries out to be a heredoc via RUN <<EORUN like we do in other places and we can drop all the && \\

# Ensure the policy module is in /usr/etc/selinux for the deployment
mkdir -p /usr/etc/selinux/targeted/modules/active/modules && \\
if [ -d /etc/selinux/targeted/modules/active/modules ]; then \\
cp -a /etc/selinux/targeted/modules/active/modules/* /usr/etc/selinux/targeted/modules/active/modules/ 2>/dev/null || true; \\
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think "error suppression and masking" via things like 2>/dev/null || true need justification.

But all of this stuff related to /usr/etc just looks wrong/unnecessary to me; I think the semodule -i should be sufficient.

BTW a bootc container lint --fatal-warnings at the end of this build should have errored out due to having both /etc and /usr/etc

assert (not ("/run/nextroot" | path exists)) "Soft reboot should not be prepared when policies differ"

# Reset and do a full reboot instead
ostree admin prepare-soft-reboot --reset
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this one should be unnecessary as we shouldn't have queued any soft reboot state in the first place

Add check to prevent soft reboot when SELinux policies differ
between booted and target deployments, since policy is not
reloaded across soft reboots.

Assisted-by: Cursor (Auto)
Signed-off-by: gursewak1997 <[email protected]>
@gursewak1997 gursewak1997 force-pushed the fix-1760-detect-selinux-policy-deltas-soft-reboot branch from dd6e419 to 4e4c5b9 Compare November 19, 2025 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Soft reboot: Also detect SELinux policy deltas

2 participants