From a615e2b8c9bbc7fc224559c05f7af191409cfb11 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 26 Mar 2024 12:08:17 -0400 Subject: [PATCH] install: Add `block` to config, disable tpm2-luks unless opted-in This allows the container image builder more control over `bootc install to-disk` in the installation config. Per discussion in https://github.com/containers/bootc/issues/421 this one definitely requires integration by the base image, and not all of them will want it. (Or if the do want LUKS, they may want more control over it) The default value is `block: ["direct"]` which only enables the simple filesystem install. This change allows two different things: `block: []` With this, `bootc install to-disk` will just error out. It's a way to effectively disable it for those that want to use an external installer always. Another possibility is: `block: ["direct", "tpm2-luks"]` To explicitly re-enable the builtin tpm2-luks flow. Or, one could do just `block: ["tpm2-luks"]` to enforce encrypted installs. Signed-off-by: Colin Walters --- docs/src/bootc-install.md | 9 +--- docs/src/man-md/bootc-install-config.md | 3 ++ lib/src/install/baseline.rs | 12 +++-- lib/src/install/config.rs | 65 ++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/docs/src/bootc-install.md b/docs/src/bootc-install.md index b82c0eda..a7a6e57c 100644 --- a/docs/src/bootc-install.md +++ b/docs/src/bootc-install.md @@ -113,14 +113,7 @@ taking precedence. If for example you are building a derived container image fr you could create a `50-myos.toml` that sets `type = "btrfs"` which will override the prior setting. -Other available options, also under the `[install]` section: - -`kargs`: This allows setting kernel arguments which apply only at the time of `bootc install`. -This option is particularly useful when creating derived/layered images; for example, a cloud -image may want to have its default `console=` set, in contrast with a default base image. -The values in this field are space separated. - -`root-fs-type`: This value is the same as `install.filesystem.root.type`. +For other available options, see [bootc-install-config](man/bootc-install-config.md). ## Installing an "unconfigured" image diff --git a/docs/src/man-md/bootc-install-config.md b/docs/src/man-md/bootc-install-config.md index 351493a8..9fd4e515 100644 --- a/docs/src/man-md/bootc-install-config.md +++ b/docs/src/man-md/bootc-install-config.md @@ -20,6 +20,9 @@ This is the only defined toplevel table. The `install`` section supports two subfields: +- `block`: An array of supported `to-disk` backends enabled by this base container image; + if not specified, this will just be `direct`. The only other supported value is `tpm2-luks`. + The first value specified will be the default. To enable both, use `block = ["direct", "tpm2-luks"]`. - `filesystem`: See below. - `kargs`: An array of strings; this will be appended to the set of kernel arguments. diff --git a/lib/src/install/baseline.rs b/lib/src/install/baseline.rs index 2e0bd072..21e69a84 100644 --- a/lib/src/install/baseline.rs +++ b/lib/src/install/baseline.rs @@ -79,9 +79,8 @@ pub(crate) struct InstallBlockDeviceOpts { /// /// direct: Filesystem written directly to block device /// tpm2-luks: Bind unlock of filesystem to presence of the default tpm2 device. - #[clap(long, value_enum, default_value_t)] - #[serde(default)] - pub(crate) block_setup: BlockSetup, + #[clap(long, value_enum)] + pub(crate) block_setup: Option, /// Target root filesystem type. #[clap(long, value_enum)] @@ -297,7 +296,10 @@ pub(crate) fn install_create_rootfs( }; let base_rootdev = findpart(ROOTPN)?; - let (rootdev, root_blockdev_kargs) = match opts.block_setup { + let block_setup = state + .install_config + .get_block_setup(opts.block_setup.as_ref().copied())?; + let (rootdev, root_blockdev_kargs) = match block_setup { BlockSetup::Direct => (base_rootdev, None), BlockSetup::Tpm2Luks => { let uuid = uuid::Uuid::new_v4().to_string(); @@ -394,7 +396,7 @@ pub(crate) fn install_create_rootfs( mount::mount(espdev, &efifs_path)?; } - let luks_device = match opts.block_setup { + let luks_device = match block_setup { BlockSetup::Direct => None, BlockSetup::Tpm2Luks => Some(luks_name.to_string()), }; diff --git a/lib/src/install/config.rs b/lib/src/install/config.rs index 1e8826fd..4a326561 100644 --- a/lib/src/install/config.rs +++ b/lib/src/install/config.rs @@ -6,6 +6,8 @@ use anyhow::{Context, Result}; use fn_error_context::context; use serde::{Deserialize, Serialize}; +use super::baseline::BlockSetup; + /// The toplevel config entry for installation configs stored /// in bootc/install (e.g. /etc/bootc/install/05-custom.toml) #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -39,6 +41,8 @@ pub(crate) struct BasicFilesystems { pub(crate) struct InstallConfiguration { /// Root filesystem type pub(crate) root_fs_type: Option, + /// Enabled block storage configurations + pub(crate) block: Option>, pub(crate) filesystem: Option, /// Kernel arguments, applied at installation time #[serde(skip_serializing_if = "Option::is_none")] @@ -93,6 +97,7 @@ impl Mergeable for InstallConfiguration { /// Apply any values in other, overriding any existing values in `self`. fn merge(&mut self, other: Self) { merge_basic(&mut self.root_fs_type, other.root_fs_type); + merge_basic(&mut self.block, other.block); self.filesystem.merge(other.filesystem); if let Some(other_kargs) = other.kargs { self.kargs @@ -103,8 +108,8 @@ impl Mergeable for InstallConfiguration { } impl InstallConfiguration { - /// Some fields can be specified multiple ways. This synchronizes the values of the fields - /// to ensure they're the same. + /// Set defaults (e.g. `block`), and also handle fields that can be specified multiple ways + /// by synchronizing the values of the fields to ensure they're the same. /// /// - install.root-fs-type is synchronized with install.filesystems.root.type; if /// both are set, then the latter takes precedence @@ -117,6 +122,10 @@ impl InstallConfiguration { let root = fs.root.get_or_insert_with(Default::default); root.fstype = Some(*rootfs); } + + if self.block.is_none() { + self.block = Some(vec![BlockSetup::Direct]); + } } /// Convenience helper to access the root filesystem @@ -128,6 +137,18 @@ impl InstallConfiguration { pub(crate) fn filter_to_external(&mut self) { self.kargs.take(); } + + pub(crate) fn get_block_setup(&self, default: Option) -> Result { + let valid_block_setups = self.block.as_deref().unwrap_or_default(); + let default_block = valid_block_setups.iter().next().ok_or_else(|| { + anyhow::anyhow!("Empty block storage configuration in install configuration") + })?; + let block_setup = default.as_ref().unwrap_or(default_block); + if !valid_block_setups.contains(block_setup) { + anyhow::bail!("Block setup {block_setup:?} is not enabled in installation config"); + } + Ok(*block_setup) + } } #[context("Loading configuration")] @@ -257,3 +278,43 @@ type = "xfs" Filesystem::Ext4 ); } + +#[test] +fn test_parse_block() { + let c: InstallConfigurationToplevel = toml::from_str( + r##"[install.filesystem.root] +type = "xfs" +"##, + ) + .unwrap(); + let mut install = c.install.unwrap(); + // Verify the default (but note canonicalization mutates) + { + let mut install = install.clone(); + install.canonicalize(); + assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Direct); + } + let other = InstallConfigurationToplevel { + install: Some(InstallConfiguration { + block: Some(vec![]), + ..Default::default() + }), + }; + install.merge(other.install.unwrap()); + // Should be set, but zero length + assert_eq!(install.block.as_ref().unwrap().len(), 0); + assert!(install.get_block_setup(None).is_err()); + + let c: InstallConfigurationToplevel = toml::from_str( + r##"[install] +block = ["tpm2-luks"]"##, + ) + .unwrap(); + let mut install = c.install.unwrap(); + install.canonicalize(); + assert_eq!(install.block.as_ref().unwrap().len(), 1); + assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Tpm2Luks); + + // And verify passing a disallowed config is an error + assert!(install.get_block_setup(Some(BlockSetup::Direct)).is_err()); +}