diff --git a/docs/src/man/bootc-container-lint.md b/docs/src/man/bootc-container-lint.md index ece07d50..7607d365 100644 --- a/docs/src/man/bootc-container-lint.md +++ b/docs/src/man/bootc-container-lint.md @@ -5,7 +5,8 @@ checks as part of a container build # SYNOPSIS -**bootc container lint** \[**\--rootfs**\] \[**-h**\|**\--help**\] +**bootc container lint** \[**\--rootfs**\] \[**\--fatal-warnings**\] +\[**\--list**\] \[**-h**\|**\--help**\] # DESCRIPTION @@ -21,6 +22,17 @@ part of a build process; it will error if any problems are detected. : Operate on the provided rootfs +**\--fatal-warnings** + +: Make warnings fatal + +**\--list** + +: Instead of executing the lints, just print all available lints. At + the current time, this will output in YAML format because its + reasonably human friendly. However, there is no commitment to + maintaining this exact format; do not parse it via code or scripts + **-h**, **\--help** : Print help (see a summary with -h) diff --git a/docs/src/man/bootc-install-to-disk.md b/docs/src/man/bootc-install-to-disk.md index ddd5ad05..865968d5 100644 --- a/docs/src/man/bootc-install-to-disk.md +++ b/docs/src/man/bootc-install-to-disk.md @@ -7,8 +7,8 @@ bootc-install-to-disk - Install to the target block device **bootc install to-disk** \[**\--wipe**\] \[**\--block-setup**\] \[**\--filesystem**\] \[**\--root-size**\] \[**\--source-imgref**\] \[**\--target-transport**\] \[**\--target-imgref**\] -\[**\--enforce-container-sigpolicy**\] \[**\--target-ostree-remote**\] -\[**\--skip-fetch-check**\] \[**\--disable-selinux**\] \[**\--karg**\] +\[**\--enforce-container-sigpolicy**\] \[**\--skip-fetch-check**\] +\[**\--disable-selinux**\] \[**\--karg**\] \[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] \[**\--bound-images**\] \[**\--stateroot**\] \[**\--via-loopback**\] \[**-h**\|**\--help**\] \<*DEVICE*\> @@ -83,10 +83,6 @@ more complex such as RAID, LVM, LUKS etc. Enabling this option enforces that \`/etc/containers/policy.json\` includes a default policy which requires signatures -**\--target-ostree-remote**=*TARGET_OSTREE_REMOTE* - -: Enable verification via an ostree remote - **\--skip-fetch-check** : By default, the accessiblity of the target image will be verified diff --git a/docs/src/man/bootc-install-to-existing-root.md b/docs/src/man/bootc-install-to-existing-root.md index ee560710..bd70d65b 100644 --- a/docs/src/man/bootc-install-to-existing-root.md +++ b/docs/src/man/bootc-install-to-existing-root.md @@ -7,8 +7,7 @@ bootc-install-to-existing-root - Install to the host root filesystem **bootc install to-existing-root** \[**\--replace**\] \[**\--source-imgref**\] \[**\--target-transport**\] \[**\--target-imgref**\] \[**\--enforce-container-sigpolicy**\] -\[**\--target-ostree-remote**\] \[**\--skip-fetch-check**\] -\[**\--disable-selinux**\] \[**\--karg**\] +\[**\--skip-fetch-check**\] \[**\--disable-selinux**\] \[**\--karg**\] \[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] \[**\--bound-images**\] \[**\--stateroot**\] \[**\--acknowledge-destructive**\] \[**-h**\|**\--help**\] @@ -69,10 +68,6 @@ cleaned up if desired when rebooted into the new root. Enabling this option enforces that \`/etc/containers/policy.json\` includes a default policy which requires signatures -**\--target-ostree-remote**=*TARGET_OSTREE_REMOTE* - -: Enable verification via an ostree remote - **\--skip-fetch-check** : By default, the accessiblity of the target image will be verified diff --git a/docs/src/man/bootc-install-to-filesystem.md b/docs/src/man/bootc-install-to-filesystem.md index 59b90046..69732179 100644 --- a/docs/src/man/bootc-install-to-filesystem.md +++ b/docs/src/man/bootc-install-to-filesystem.md @@ -10,8 +10,7 @@ filesystem structure \[**\--acknowledge-destructive**\] \[**\--skip-finalize**\] \[**\--source-imgref**\] \[**\--target-transport**\] \[**\--target-imgref**\] \[**\--enforce-container-sigpolicy**\] -\[**\--target-ostree-remote**\] \[**\--skip-fetch-check**\] -\[**\--disable-selinux**\] \[**\--karg**\] +\[**\--skip-fetch-check**\] \[**\--disable-selinux**\] \[**\--karg**\] \[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] \[**\--bound-images**\] \[**\--stateroot**\] \[**-h**\|**\--help**\] \<*ROOT_PATH*\> @@ -98,10 +97,6 @@ is currently expected to be empty by default. Enabling this option enforces that \`/etc/containers/policy.json\` includes a default policy which requires signatures -**\--target-ostree-remote**=*TARGET_OSTREE_REMOTE* - -: Enable verification via an ostree remote - **\--skip-fetch-check** : By default, the accessiblity of the target image will be verified diff --git a/docs/src/man/bootc-switch.md b/docs/src/man/bootc-switch.md index cd93c470..7be8478b 100644 --- a/docs/src/man/bootc-switch.md +++ b/docs/src/man/bootc-switch.md @@ -5,8 +5,8 @@ bootc-switch - Target a new container image reference to boot # SYNOPSIS **bootc switch** \[**\--quiet**\] \[**\--apply**\] \[**\--transport**\] -\[**\--enforce-container-sigpolicy**\] \[**\--ostree-remote**\] -\[**\--retain**\] \[**-h**\|**\--help**\] \<*TARGET*\> +\[**\--enforce-container-sigpolicy**\] \[**\--retain**\] +\[**-h**\|**\--help**\] \<*TARGET*\> # DESCRIPTION @@ -51,10 +51,6 @@ updates via container image tags; for example, Enabling this option enforces that \`/etc/containers/policy.json\` includes a default policy which requires signatures. -**\--ostree-remote**=*OSTREE_REMOTE* - -: Enable verification via an ostree remote - **\--retain** : Retain reference to currently booted image diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 69364966..a8eb8798 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -27,6 +27,7 @@ cap-std-ext = { workspace = true, features = ["fs_utf8"] } hex = { workspace = true } fn-error-context = { workspace = true } indicatif = { workspace = true } +indoc = { workspace = true } libc = { workspace = true } liboverdrop = "0.1.0" libsystemd = "0.7" @@ -50,7 +51,6 @@ comfy-table = "7.1.1" thiserror = "2.0.11" [dev-dependencies] -indoc = { workspace = true } similar-asserts = { workspace = true } static_assertions = { workspace = true } diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 70b6d91b..d693be6d 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -249,6 +249,13 @@ pub(crate) enum ContainerOpts { /// Make warnings fatal. #[clap(long)] fatal_warnings: bool, + + /// Instead of executing the lints, just print all available lints. + /// At the current time, this will output in YAML format because it's + /// reasonably human friendly. However, there is no commitment to + /// maintaining this exact format; do not parse it via code or scripts. + #[clap(long)] + list: bool, }, } @@ -1018,7 +1025,11 @@ async fn run_from_opt(opt: Opt) -> Result<()> { ContainerOpts::Lint { rootfs, fatal_warnings, + list, } => { + if list { + return lints::lint_list(std::io::stdout().lock()); + } if !ostree_ext::container_utils::is_ostree_container()? { anyhow::bail!( "Not in a ostree container, this command only verifies ostree containers." diff --git a/lib/src/lints.rs b/lib/src/lints.rs index 3b681080..8bc58dcd 100644 --- a/lib/src/lints.rs +++ b/lib/src/lints.rs @@ -13,6 +13,8 @@ use cap_std_ext::cap_std; use cap_std_ext::cap_std::fs::MetadataExt; use cap_std_ext::dirext::CapStdExtDirExt as _; use fn_error_context::context; +use indoc::indoc; +use serde::Serialize; /// Reference to embedded default baseimage content that should exist. const BASEIMAGE_REF: &str = "usr/share/doc/bootc/baseimage/base"; @@ -51,7 +53,8 @@ impl LintError { type LintFn = fn(&Dir) -> LintResult; /// The classification of a lint type. -#[derive(Debug)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "kebab-case")] enum LintType { /// If this fails, it is known to be fatal - the system will not install or /// is effectively guaranteed to fail at runtime. @@ -60,10 +63,15 @@ enum LintType { Warning, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "kebab-case")] struct Lint { name: &'static str, + #[serde(rename = "type")] ty: LintType, + #[serde(skip)] f: LintFn, + description: &'static str, } const LINTS: &[Lint] = &[ @@ -71,40 +79,73 @@ const LINTS: &[Lint] = &[ name: "var-run", ty: LintType::Fatal, f: check_var_run, + description: "Check for /var/run being a physical directory; this is always a bug.", }, Lint { name: "kernel", ty: LintType::Fatal, f: check_kernel, + description: indoc! { r#" + Check for multiple kernels, i.e. multiple directories of the form /usr/lib/modules/$kver. + Only one kernel is supported in an image. + "# }, }, Lint { name: "bootc-kargs", ty: LintType::Fatal, f: check_parse_kargs, + description: "Verify syntax of /usr/lib/bootc/kargs.d.", }, Lint { name: "etc-usretc", ty: LintType::Fatal, f: check_usretc, + description: indoc! { r#" + Verify that only one of /etc or /usr/etc exist. You should only have /etc + in a container image. It will cause undefined behavior to have both /etc + and /usr/etc. + "#}, }, Lint { // This one can be lifted in the future, see https://github.com/containers/bootc/issues/975 name: "utf8", ty: LintType::Fatal, f: check_utf8, + description: indoc! { r#" + Check for non-UTF8 filenames. Currently, the ostree backend of bootc only supports + UTF-8 filenames. Non-UTF8 filenames will cause a fatal error. + "#}, }, Lint { name: "baseimage-root", ty: LintType::Fatal, f: check_baseimage_root, + description: indoc! { r#" + Check that expected files are present in the root of the filesystem; such + as /sysroot and a composefs configuration for ostree. More in + . + "#}, }, Lint { name: "var-log", ty: LintType::Warning, f: check_varlog, + description: indoc! { r#" + Check for non-empty regular files in `/var/log`. It is often undesired + to ship log files in container images. Log files in general are usually + per-machine state in `/var`. Additionally, log files often include + timestamps, causing unreproducible container images, and may contain + sensitive build system information. + "#}, }, ]; +pub(crate) fn lint_list(output: impl std::io::Write) -> Result<()> { + // Dump in yaml format by default, it's readable enough + serde_yaml::to_writer(output, LINTS)?; + Ok(()) +} + /// check for the existence of the /var/run directory /// if it exists we need to check that it links to /run if not error /// if it does not exist error. @@ -522,4 +563,12 @@ mod tests { check_baseimage_root(&td).unwrap().unwrap(); Ok(()) } + + #[test] + fn test_list() { + let mut r = Vec::new(); + lint_list(&mut r).unwrap(); + let lints: Vec = serde_yaml::from_slice(&r).unwrap(); + assert_eq!(lints.len(), LINTS.len()); + } }