Skip to content
Open
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
33 changes: 32 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,31 @@ FROM scratch as base
COPY --from=target-base /target-rootfs/ /
# SKIP_CONFIGS=1 skips LBIs, test kargs, and install configs (for FCOS testing)
ARG SKIP_CONFIGS
ARG boot_type
ARG seal_state
# Use tmpfs for /run and /tmp with bind mounts inside to avoid leaking mount stubs into the image
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=src,src=/src/hack,target=/run/hack \
--mount=type=bind,from=src,src=/src/hack,target=/run/hack <<-EOF
set -ex

cd /run/hack/ && SKIP_CONFIGS="${SKIP_CONFIGS}" ./provision-derived.sh

pkgs_to_install=()
if [[ "${seal_state}" == "sealed" ]]; then
pkgs_to_install+=(sbsigntools)
fi

# Install systemd-ukify and systemd-boot for UKIs
# This also installs systemd-boot for the grub UKI case which is not ideal...
if [[ "${boot_type}" == "uki" ]]; then
pkgs_to_install+=(systemd-ukify)
fi

if [[ ${#pkgs_to_install[@]} -gt 0 ]]; then
dnf install -y "${pkgs_to_install[@]}"
fi
Comment thread
Johan-Liebert1 marked this conversation as resolved.
EOF

# Note we don't do any customization here yet
# Mark this as a test image
LABEL bootc.testimage="1"
Expand Down Expand Up @@ -165,14 +186,24 @@ RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp
FROM base as base-penultimate
ARG variant
ARG bootloader
ARG boot_type

# Switch to a signed systemd-boot, if configured
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
--mount=type=bind,from=sdboot-signed,src=/,target=/run/sdboot-signed <<EORUN
set -xeuo pipefail

if [[ "${bootloader}" == "systemd" ]]; then
/run/packaging/switch-to-sdboot /run/sdboot-signed
fi

if [[ "${boot_type}" == "uki" ]]; then
cp /run/packaging/seal-uki /usr/bin/seal-uki
cp /run/packaging/finalize-uki /usr/bin/finalize-uki
cp /run/packaging/initialize-sealing-tools /usr/bin/initialize-sealing-tools
fi

EORUN
# Configure the rootfs
ARG rootfs=""
Expand Down
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ test-composefs bootloader filesystem boot_type seal_state *ARGS:
--seal-state={{seal_state}} \
--boot-type={{boot_type}} \
{{ARGS}} \
$(if [ "{{boot_type}}" = "uki" ]; then echo "readonly image-upgrade-reboot"; else echo "integration"; fi)
$(if [ "{{boot_type}}" = "uki" ] && [ "{{seal_state}}" = "sealed" ]; then echo "readonly image-upgrade-reboot"; else echo "integration"; fi)

# Run upgrade test: boot VM from published base image (with tmt deps added),
# upgrade to locally-built image, reboot, then run readonly tests to verify.
Expand Down
2 changes: 1 addition & 1 deletion contrib/packaging/install-rpm-and-setup
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ RPM_DIR="${1:-/tmp}"

# Install the RPM package
# Use rpm -Uvh with --oldpackage to allow replacing with dev version
rpm -Uvh --oldpackage --nosignature "${RPM_DIR}"/*.rpm
rpm -Uvh --replacepkgs --oldpackage --nosignature "${RPM_DIR}"/*.rpm
# Note: we don't need to clean up the source directory since it's a bind mount

# Regenerate initramfs if we have initramfs-setup
Expand Down
2 changes: 1 addition & 1 deletion contrib/packaging/switch-to-sdboot
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if rpm -q bootupd &>/dev/null; then
fi

# First install the unsigned systemd-boot RPM to get the package in place
rpm -Uvh "${src}"/*.rpm
rpm -Uvh --replacepkgs "${src}"/*.rpm

# Now find where it installed the binary and override with our signed version
sdboot=$(ls /usr/lib/systemd/boot/efi/systemd-boot*.efi)
Expand Down
13 changes: 11 additions & 2 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ async fn boot_entry_from_composefs_deployment(
storage: &Storage,
origin: tini::Ini,
verity: &str,
missing_verity_allowed: bool,
) -> Result<BootEntry> {
let image = match origin.get::<String>("origin", ORIGIN_CONTAINER) {
Some(img_name_from_config) => {
Expand Down Expand Up @@ -502,6 +503,7 @@ async fn boot_entry_from_composefs_deployment(
boot_type,
bootloader: get_bootloader()?,
boot_digest,
missing_verity_allowed,
}),
soft_reboot_capable: false,
};
Expand Down Expand Up @@ -784,8 +786,15 @@ async fn composefs_deployment_status_from(
let ini = tini::Ini::from_string(&config)
.with_context(|| format!("Failed to parse file {verity_digest}.origin as ini"))?;

let mut boot_entry =
boot_entry_from_composefs_deployment(storage, ini, &verity_digest).await?;
let mut boot_entry = boot_entry_from_composefs_deployment(
storage,
ini,
&verity_digest,
// We will either have verity enforced or not (possible but we don't allow it)
// There won't be two deployments with one enforcing verity and one not
cmdline.allow_missing_fsverity,
)
.await?;

// SAFETY: boot_entry.composefs will always be present
let boot_type_from_origin = boot_entry.composefs.as_ref().unwrap().boot_type;
Expand Down
13 changes: 12 additions & 1 deletion crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,10 @@ pub(crate) enum ContainerOpts {
#[clap(long)]
allow_missing_verity: bool,

/// Write a dumpfile to this path
#[clap(long)]
write_dumpfile_to: Option<Utf8PathBuf>,

/// Additional arguments to pass to ukify (after `--`).
#[clap(last = true)]
args: Vec<OsString>,
Expand Down Expand Up @@ -1791,8 +1795,15 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
rootfs,
kargs,
allow_missing_verity,
write_dumpfile_to,
args,
} => crate::ukify::build_ukify(&rootfs, &kargs, &args, allow_missing_verity),
} => crate::ukify::build_ukify(
&rootfs,
&kargs,
&args,
allow_missing_verity,
write_dumpfile_to.as_deref(),
),
ContainerOpts::Export {
format,
target,
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ pub struct BootEntryComposefs {
/// The sha256sum of vmlinuz + initrd
/// Only `Some` for Type1 boot entries
pub boot_digest: Option<String>,
/// Whether fs-verity validation is optional
pub missing_verity_allowed: bool,
}

/// A bootable entry
Expand Down
23 changes: 23 additions & 0 deletions crates/lib/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,27 @@ fn write_download_only(
Ok(())
}

fn write_fsverity_enforcement(
mut out: impl Write,
entry: &crate::spec::BootEntry,
prefix_len: usize,
) -> Result<()> {
if let Some(cfs) = &entry.composefs {
write_row_name(&mut out, "FsVerity", prefix_len)?;
writeln!(
out,
"{}",
if cfs.missing_verity_allowed {
"Not Enforced"
} else {
"Enforced"
}
)?;
};

Ok(())
}

/// Render cached update information, showing what update is available.
///
/// This is populated by a previous `bootc upgrade --check` that found
Expand Down Expand Up @@ -734,6 +755,8 @@ fn human_render_slot(
// Show soft-reboot capability
write_soft_reboot(&mut out, entry, prefix_len)?;

write_fsverity_enforcement(&mut out, entry, prefix_len)?;

// Show download-only lock status
write_download_only(&mut out, slot, entry, prefix_len)?;
}
Expand Down
7 changes: 4 additions & 3 deletions crates/lib/src/ukify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub(crate) fn build_ukify(
extra_kargs: &[String],
args: &[OsString],
allow_missing_fsverity: bool,
write_dumpfile_to: Option<&Utf8Path>,
) -> Result<()> {
// Warn if --karg is used (temporary workaround)
if !extra_kargs.is_empty() {
Expand Down Expand Up @@ -78,7 +79,7 @@ pub(crate) fn build_ukify(
}

// Compute the composefs digest
let composefs_digest = compute_composefs_digest(rootfs, None)?;
let composefs_digest = compute_composefs_digest(rootfs, write_dumpfile_to)?;

// Get kernel arguments from kargs.d
let mut cmdline = crate::bootc_kargs::get_kargs_in_root(&root, std::env::consts::ARCH)?;
Expand Down Expand Up @@ -131,7 +132,7 @@ mod tests {
let tempdir = tempfile::tempdir().unwrap();
let path = Utf8Path::from_path(tempdir.path()).unwrap();

let result = build_ukify(path, &[], &[], false);
let result = build_ukify(path, &[], &[], false, None);
assert!(result.is_err());
let err = format!("{:#}", result.unwrap_err());
assert!(
Expand All @@ -149,7 +150,7 @@ mod tests {
fs::create_dir_all(tempdir.path().join("boot/EFI/Linux")).unwrap();
fs::write(tempdir.path().join("boot/EFI/Linux/test.efi"), b"fake uki").unwrap();

let result = build_ukify(path, &[], &[], false);
let result = build_ukify(path, &[], &[], false, None);
assert!(result.is_err());
let err = format!("{:#}", result.unwrap_err());
assert!(
Expand Down
51 changes: 51 additions & 0 deletions crates/xtask/src/tmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const FIELD_SUMMARY: &str = "summary";
const FIELD_ADJUST: &str = "adjust";

const FIELD_FIXME_SKIP_IF_COMPOSEFS: &str = "fixme_skip_if_composefs";
const FIELD_FIXME_SKIP_IF_UKI: &str = "fixme_skip_if_uki";

// bcvk options
const BCVK_OPT_BIND_STORAGE_RO: &str = "--bind-storage-ro";
Expand Down Expand Up @@ -246,6 +247,7 @@ fn verify_ssh_connectivity(sh: &Shell, port: u16, key_path: &Utf8Path) -> Result
struct PlanMetadata {
try_bind_storage: bool,
skip_if_composefs: bool,
skip_if_uki: bool,
}

/// Parse integration.fmf to extract extra-try_bind_storage for all plans
Expand Down Expand Up @@ -286,6 +288,7 @@ fn parse_plan_metadata(
.and_modify(|m| m.try_bind_storage = b)
.or_insert(PlanMetadata {
try_bind_storage: b,
skip_if_uki: false,
skip_if_composefs: false,
});
}
Expand All @@ -301,6 +304,23 @@ fn parse_plan_metadata(
.and_modify(|m| m.skip_if_composefs = b)
.or_insert(PlanMetadata {
skip_if_composefs: b,
skip_if_uki: false,
try_bind_storage: false,
});
}
}

if let Some(skip_if_uki) = plan_data.get(&serde_yaml::Value::String(format!(
"extra-{}",
FIELD_FIXME_SKIP_IF_UKI
))) {
if let Some(b) = skip_if_uki.as_bool() {
plan_metadata
.entry(plan_name.to_string())
.and_modify(|m| m.skip_if_uki = b)
.or_insert(PlanMetadata {
skip_if_uki: b,
skip_if_composefs: false,
try_bind_storage: false,
});
}
Expand Down Expand Up @@ -407,6 +427,16 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> {
});
}

if matches!(args.boot_type, crate::BootType::Uki) {
plans.retain(|plan| {
!plan_metadata
.iter()
.find(|(key, _)| plan.ends_with(key.as_str()))
.map(|(_, v)| v.skip_if_uki)
.unwrap_or(false)
});
}

if plans.len() < original_plans_count {
println!(
"Filtered from {} to {} plan(s) based on arguments: {:?}",
Expand Down Expand Up @@ -910,6 +940,8 @@ struct TestDef {
try_bind_storage: bool,
/// Whether to skip this test for composefs backend
skip_if_composefs: bool,
/// Whether to skip this test for images with UKI
skip_if_uki: bool,
/// TMT fmf attributes to pass through (summary, duration, adjust, etc.)
tmt: serde_yaml::Value,
}
Expand Down Expand Up @@ -1011,12 +1043,24 @@ pub(crate) fn update_integration() -> Result<()> {
.and_then(|v| v.as_bool())
.unwrap_or(false);

let skip_if_uki = metadata
.extra
.as_mapping()
.and_then(|m| {
m.get(&serde_yaml::Value::String(
FIELD_FIXME_SKIP_IF_UKI.to_string(),
))
})
.and_then(|v| v.as_bool())
.unwrap_or(false);

tests.push(TestDef {
number: metadata.number,
name: display_name,
test_command,
try_bind_storage,
skip_if_composefs,
skip_if_uki,
tmt: metadata.tmt,
});
}
Expand Down Expand Up @@ -1154,6 +1198,13 @@ pub(crate) fn update_integration() -> Result<()> {
);
}

if test.skip_if_uki {
plan_value.insert(
serde_yaml::Value::String(format!("extra-{}", FIELD_FIXME_SKIP_IF_UKI)),
serde_yaml::Value::Bool(true),
);
}

plans_mapping.insert(
serde_yaml::Value::String(plan_key),
serde_yaml::Value::Mapping(plan_value),
Expand Down
7 changes: 6 additions & 1 deletion docs/src/host-v1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@
"description": "Whether we boot using systemd or grub",
"$ref": "#/$defs/Bootloader"
},
"missingVerityAllowed": {
"description": "Whether fs-verity validation is optional",
"type": "boolean"
},
"verity": {
"description": "The erofs verity",
"type": "string"
Expand All @@ -151,7 +155,8 @@
"required": [
"verity",
"bootType",
"bootloader"
"bootloader",
"missingVerityAllowed"
]
},
"BootEntryOstree": {
Expand Down
4 changes: 4 additions & 0 deletions docs/src/man/bootc-container-ukify.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Any additional arguments after `--` are passed through to ukify unchanged.

Make fs-verity validation optional in case the filesystem doesn't support it

**--write-dumpfile-to**=*WRITE_DUMPFILE_TO*

Write a dumpfile to this path

<!-- END GENERATED OPTIONS -->

# EXAMPLES
Expand Down
8 changes: 8 additions & 0 deletions tmt/plans/integration.fmf
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ execute:
how: fmf
test:
- /tmt/tests/tests/test-29-soft-reboot-selinux-policy
extra-fixme_skip_if_uki: true

/plan-30-install-unified-flag:
summary: Test bootc install with experimental unified storage flag
Expand Down Expand Up @@ -246,4 +247,11 @@ execute:
test:
- /tmt/tests/tests/test-40-install-karg-delete
extra-fixme_skip_if_composefs: true

/plan-41-composefs-gc-uki:
summary: Test composefs garbage collection for UKI
discover:
how: fmf
test:
- /tmt/tests/tests/test-41-composefs-gc-uki
# END GENERATED PLANS
Loading
Loading