Skip to content

Commit 28b557c

Browse files
andrewdunndevcgwalters
authored andcommitted
initramfs: Inherit SELinux label on transient root tmpfs
When root.transient is enabled, the tmpfs used as the overlay upper layer has no SELinux context set, so its root directory gets the default tmpfs_t label. This propagates to the overlay mountpoint at /, causing services like systemd-networkd to refuse to start because they expect root_t. Fix this by reading the SELinux label from the composefs lower layer via fgetxattr(security.selinux) and setting it as rootcontext on the tmpfs before creating the overlay. This ensures the overlay root inherits the correct label from the base filesystem. The rootcontext mount option sets only the root inode's label, preserving per-file labeling from SELinux policy via type_transition rules. This is preferable to context= which would force a uniform label on all inodes. When SELinux is not enabled, fgetxattr returns an error and no context is set, preserving the existing behavior. Note: the ostree backend (otcore-prepare-root.c in ostreedev/ostree) has the same issue but uses /run as the tmpfs backing store rather than creating its own tmpfs. That fix would need to go through libcomposefs mount options or the ostree repo directly. Closes: #1992 AI-Assisted: yes AI-Tools: GitLab Duo, OpenCode Signed-off-by: Andrew Dunn <andrew@dunn.dev>
1 parent 841298f commit 28b557c

2 files changed

Lines changed: 25 additions & 5 deletions

File tree

crates/initramfs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ publish = false
77

88
[dependencies]
99
anyhow.workspace = true
10+
cap-std-ext.workspace = true
1011
clap = { workspace = true, features = ["std", "help", "usage", "derive"] }
1112
libc.workspace = true
1213
rustix.workspace = true

crates/initramfs/src/lib.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
//! Mount helpers for bootc-initramfs
22
33
use std::{
4-
ffi::OsString,
4+
ffi::{CString, OsString},
55
fmt::Debug,
66
io::ErrorKind,
77
os::fd::{AsFd, AsRawFd, OwnedFd},
88
path::{Path, PathBuf},
99
};
1010

1111
use anyhow::{Context, Result};
12+
use cap_std_ext::cap_std::fs::Dir;
13+
use cap_std_ext::dirext::CapStdExtDirExt;
1214
use clap::Parser;
1315
use rustix::{
1416
fs::{CWD, Mode, OFlags, major, minor, mkdirat, openat, stat, symlink},
@@ -19,6 +21,7 @@ use rustix::{
1921
},
2022
path,
2123
};
24+
2225
use serde::Deserialize;
2326

2427
use cfsctl::composefs;
@@ -202,9 +205,21 @@ fn bind_mount(fd: impl AsFd, path: &str) -> Result<OwnedFd> {
202205
Ok(res?)
203206
}
204207

205-
#[context("Mounting tmpfs")]
206-
fn mount_tmpfs() -> Result<OwnedFd> {
208+
/// Mount a tmpfs, inheriting the SELinux label from the base filesystem
209+
/// if provided. See <https://github.com/containers/bootc/issues/1992>.
210+
#[context("Mounting tmpfs for overlay")]
211+
fn mount_tmpfs_for_overlay(base: Option<impl AsFd>) -> Result<OwnedFd> {
207212
let tmpfs = FsHandle::open("tmpfs")?;
213+
214+
if let Some(base_fd) = base {
215+
let base_dir = Dir::reopen_dir(&base_fd.as_fd())?;
216+
if let Some(label) = base_dir.getxattr(".", "security.selinux")? {
217+
if let Ok(cstr) = CString::new(label) {
218+
fsconfig_set_string(tmpfs.as_fd(), "rootcontext", &cstr)?;
219+
}
220+
}
221+
}
222+
208223
fsconfig_create(tmpfs.as_fd())?;
209224
Ok(fsmount(
210225
tmpfs.as_fd(),
@@ -239,16 +254,20 @@ fn overlay_state(
239254
mount_at_wrapper(fs, base, ".").context("Moving mount")
240255
}
241256

242-
/// Mounts a transient overlayfs with passed in fd as the lowerdir
257+
/// Mounts a transient overlayfs with passed in fd as the lowerdir.
258+
///
259+
/// The tmpfs used for the overlay upper layer inherits the SELinux label
260+
/// from the base filesystem to prevent label mismatches (see #1992).
243261
#[context("Mounting transient overlayfs")]
244262
pub fn overlay_transient(
245263
base: impl AsFd,
246264
mode: Option<rustix::fs::Mode>,
247265
mount_attr_flags: Option<MountAttrFlags>,
248266
) -> Result<()> {
267+
let tmpfs = mount_tmpfs_for_overlay(Some(&base))?;
249268
overlay_state(
250269
base,
251-
prepare_mount(mount_tmpfs()?)?,
270+
prepare_mount(tmpfs)?,
252271
"transient",
253272
mode,
254273
mount_attr_flags,

0 commit comments

Comments
 (0)