@@ -288,9 +288,7 @@ pub(crate) struct SourceInfo {
288
288
pub ( crate ) struct State {
289
289
pub ( crate ) source : SourceInfo ,
290
290
/// Force SELinux off in target system
291
- pub ( crate ) override_disable_selinux : bool ,
292
- #[ allow( dead_code) ]
293
- pub ( crate ) setenforce_guard : Option < crate :: lsm:: SetEnforceGuard > ,
291
+ pub ( crate ) selinux_state : SELinuxFinalState ,
294
292
#[ allow( dead_code) ]
295
293
pub ( crate ) config_opts : InstallConfigOpts ,
296
294
pub ( crate ) target_imgref : ostree_container:: OstreeImageReference ,
@@ -303,7 +301,7 @@ impl State {
303
301
#[ context( "Loading SELinux policy" ) ]
304
302
pub ( crate ) fn load_policy ( & self ) -> Result < Option < ostree:: SePolicy > > {
305
303
use std:: os:: fd:: AsRawFd ;
306
- if !self . source . selinux || self . override_disable_selinux {
304
+ if !self . selinux_state . enabled ( ) {
307
305
return Ok ( None ) ;
308
306
}
309
307
// We always use the physical container root to bootstrap policy
@@ -334,6 +332,8 @@ struct InstallAleph {
334
332
timestamp : Option < chrono:: DateTime < Utc > > ,
335
333
/// The `uname -r` of the kernel doing the installation
336
334
kernel : String ,
335
+ /// The state of SELinux at install time
336
+ selinux : String ,
337
337
}
338
338
339
339
/// A mount specification is a subset of a line in `/etc/fstab`.
@@ -687,6 +687,7 @@ async fn initialize_ostree_root_from_self(
687
687
version : imgstate. version ( ) . as_ref ( ) . map ( |s| s. to_string ( ) ) ,
688
688
timestamp,
689
689
kernel : uname. release ( ) . to_str ( ) ?. to_string ( ) ,
690
+ selinux : state. selinux_state . to_aleph ( ) . to_string ( ) ,
690
691
} ;
691
692
692
693
Ok ( aleph)
@@ -777,27 +778,53 @@ impl RootSetup {
777
778
}
778
779
}
779
780
781
+ pub ( crate ) enum SELinuxFinalState {
782
+ /// Host and target both have SELinux, but user forced it off for target
783
+ ForceTargetDisabled ,
784
+ /// Host and target both have SELinux
785
+ Enabled ( Option < crate :: lsm:: SetEnforceGuard > ) ,
786
+ /// Host has SELinux disabled, target is enabled.
787
+ HostDisabled ,
788
+ /// Neither host or target have SELinux
789
+ Disabled ,
790
+ }
791
+
792
+ impl SELinuxFinalState {
793
+ /// Returns true if the target system will have SELinux enabled.
794
+ pub ( crate ) fn enabled ( & self ) -> bool {
795
+ match self {
796
+ SELinuxFinalState :: ForceTargetDisabled | SELinuxFinalState :: Disabled => false ,
797
+ SELinuxFinalState :: Enabled ( _) | SELinuxFinalState :: HostDisabled => true ,
798
+ }
799
+ }
800
+
801
+ /// Returns the canonical stringified version of self. This is only used
802
+ /// for debugging purposes.
803
+ pub ( crate ) fn to_aleph ( & self ) -> & ' static str {
804
+ match self {
805
+ SELinuxFinalState :: ForceTargetDisabled => "force-target-disabled" ,
806
+ SELinuxFinalState :: Enabled ( _) => "enabled" ,
807
+ SELinuxFinalState :: HostDisabled => "host-disabled" ,
808
+ SELinuxFinalState :: Disabled => "disabled" ,
809
+ }
810
+ }
811
+ }
812
+
780
813
/// If we detect that the target ostree commit has SELinux labels,
781
814
/// and we aren't passed an override to disable it, then ensure
782
815
/// the running process is labeled with install_t so it can
783
816
/// write arbitrary labels.
784
817
pub ( crate ) fn reexecute_self_for_selinux_if_needed (
785
818
srcdata : & SourceInfo ,
786
819
override_disable_selinux : bool ,
787
- ) -> Result < ( bool , Option < crate :: lsm:: SetEnforceGuard > ) > {
788
- let mut ret_did_override = false ;
820
+ ) -> Result < SELinuxFinalState > {
789
821
// If the target state has SELinux enabled, we need to check the host state.
790
- let mut g = None ;
791
- // We don't currently quite support installing SELinux enabled systems
792
- // from SELinux disabled hosts, but this environment variable can be set
793
- // to test it out anyways.
794
- let skip_check_envvar = "BOOTC_SKIP_SELINUX_HOST_CHECK" ;
795
822
if srcdata. selinux {
796
823
let host_selinux = crate :: lsm:: selinux_enabled ( ) ?;
797
824
tracing:: debug!( "Target has SELinux, host={host_selinux}" ) ;
798
- if override_disable_selinux {
799
- ret_did_override = true ;
800
- println ! ( "notice: Target has SELinux enabled, overriding to disable" )
825
+ let r = if override_disable_selinux {
826
+ println ! ( "notice: Target has SELinux enabled, overriding to disable" ) ;
827
+ SELinuxFinalState :: ForceTargetDisabled
801
828
} else if host_selinux {
802
829
// /sys/fs/selinuxfs is not normally mounted, so we do that now.
803
830
// Because SELinux enablement status is cached process-wide and was very likely
@@ -806,21 +833,20 @@ pub(crate) fn reexecute_self_for_selinux_if_needed(
806
833
// so let's just fall through to that.
807
834
setup_sys_mount ( "selinuxfs" , SELINUXFS ) ?;
808
835
// This will re-execute the current process (once).
809
- g = crate :: lsm:: selinux_ensure_install_or_setenforce ( ) ?;
810
- } else if std:: env:: var_os ( skip_check_envvar) . is_some ( ) {
811
- eprintln ! (
812
- "Host kernel does not have SELinux support, but target enables it by default; {} is set, continuing anyways" ,
813
- skip_check_envvar
814
- ) ;
836
+ let g = crate :: lsm:: selinux_ensure_install_or_setenforce ( ) ?;
837
+ SELinuxFinalState :: Enabled ( g)
815
838
} else {
816
- anyhow:: bail!(
817
- "Host kernel does not have SELinux support, but target enables it by default"
839
+ // This used to be a hard error, but is now a mild warning
840
+ crate :: utils:: medium_visibility_warning (
841
+ "Host kernel does not have SELinux support, but target enables it by default; this is less well tested. See https://github.com/containers/bootc/issues/419" ,
818
842
) ;
819
- }
843
+ SELinuxFinalState :: HostDisabled
844
+ } ;
845
+ Ok ( r)
820
846
} else {
821
847
tracing:: debug!( "Target does not enable SELinux" ) ;
848
+ Ok ( SELinuxFinalState :: Disabled )
822
849
}
823
- Ok ( ( ret_did_override, g) )
824
850
}
825
851
826
852
/// Trim, flush outstanding writes, and freeze/thaw the target mounted filesystem;
@@ -1079,8 +1105,7 @@ async fn prepare_install(
1079
1105
setup_sys_mount ( "efivarfs" , EFIVARFS ) ?;
1080
1106
1081
1107
// Now, deal with SELinux state.
1082
- let ( override_disable_selinux, setenforce_guard) =
1083
- reexecute_self_for_selinux_if_needed ( & source, config_opts. disable_selinux ) ?;
1108
+ let selinux_state = reexecute_self_for_selinux_if_needed ( & source, config_opts. disable_selinux ) ?;
1084
1109
1085
1110
println ! ( "Installing image: {:#}" , & target_imgref) ;
1086
1111
if let Some ( digest) = source. digest . as_deref ( ) {
@@ -1102,8 +1127,7 @@ async fn prepare_install(
1102
1127
// so we can pass it to worker threads too. Right now this just
1103
1128
// combines our command line options along with some bind mounts from the host.
1104
1129
let state = Arc :: new ( State {
1105
- override_disable_selinux,
1106
- setenforce_guard,
1130
+ selinux_state,
1107
1131
source,
1108
1132
config_opts,
1109
1133
target_imgref,
@@ -1115,7 +1139,7 @@ async fn prepare_install(
1115
1139
}
1116
1140
1117
1141
async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < ( ) > {
1118
- if state. override_disable_selinux {
1142
+ if matches ! ( state. selinux_state , SELinuxFinalState :: ForceTargetDisabled ) {
1119
1143
rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
1120
1144
}
1121
1145
@@ -1218,6 +1242,20 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
1218
1242
loopback_dev. close ( ) ?;
1219
1243
}
1220
1244
1245
+ // At this point, all other threads should be gone.
1246
+ if let Some ( state) = Arc :: into_inner ( state) {
1247
+ // If we had invoked `setenforce 0`, then let's re-enable it.
1248
+ match state. selinux_state {
1249
+ SELinuxFinalState :: Enabled ( Some ( guard) ) => {
1250
+ guard. consume ( ) ?;
1251
+ }
1252
+ _ => { }
1253
+ }
1254
+ } else {
1255
+ // This shouldn't happen...but we will make it not fatal right now
1256
+ tracing:: warn!( "Failed to consume state Arc" ) ;
1257
+ }
1258
+
1221
1259
installation_complete ( ) ;
1222
1260
1223
1261
Ok ( ( ) )
0 commit comments