Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

install: Add block to config, disable tpm2-luks unless opted-in #445

Merged
merged 2 commits into from
Apr 3, 2024
Merged
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
9 changes: 1 addition & 8 deletions docs/src/bootc-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions docs/src/man-md/bootc-install-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
12 changes: 7 additions & 5 deletions lib/src/install/baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockSetup>,

/// Target root filesystem type.
#[clap(long, value_enum)]
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()),
};
Expand Down
74 changes: 66 additions & 8 deletions lib/src/install/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -39,6 +41,8 @@ pub(crate) struct BasicFilesystems {
pub(crate) struct InstallConfiguration {
/// Root filesystem type
pub(crate) root_fs_type: Option<super::baseline::Filesystem>,
/// Enabled block storage configurations
pub(crate) block: Option<Vec<BlockSetup>>,
pub(crate) filesystem: Option<BasicFilesystems>,
/// Kernel arguments, applied at installation time
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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<BlockSetup>) -> Result<BlockSetup> {
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")]
Expand Down Expand Up @@ -177,8 +198,7 @@ root-fs-type = "xfs"
let other = InstallConfigurationToplevel {
install: Some(InstallConfiguration {
root_fs_type: Some(Filesystem::Ext4),
filesystem: None,
kargs: None,
..Default::default()
}),
};
install.merge(other.install.unwrap());
Expand Down Expand Up @@ -206,14 +226,13 @@ kargs = ["console=ttyS0", "foo=bar"]
assert_eq!(install.root_fs_type.unwrap(), Filesystem::Ext4);
let other = InstallConfigurationToplevel {
install: Some(InstallConfiguration {
root_fs_type: None,
filesystem: None,
kargs: Some(
["console=tty0", "nosmt"]
.into_iter()
.map(ToOwned::to_owned)
.collect(),
),
..Default::default()
}),
};
install.merge(other.install.unwrap());
Expand Down Expand Up @@ -245,13 +264,12 @@ type = "xfs"
);
let other = InstallConfigurationToplevel {
install: Some(InstallConfiguration {
root_fs_type: None,
filesystem: Some(BasicFilesystems {
root: Some(RootFS {
fstype: Some(Filesystem::Ext4),
}),
}),
kargs: None,
..Default::default()
}),
};
install.merge(other.install.unwrap());
Expand All @@ -260,3 +278,43 @@ type = "xfs"
Filesystem::Ext4
);
}

#[test]
fn test_parse_block() {
cgwalters marked this conversation as resolved.
Show resolved Hide resolved
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);
cgwalters marked this conversation as resolved.
Show resolved Hide resolved

// And verify passing a disallowed config is an error
assert!(install.get_block_setup(Some(BlockSetup::Direct)).is_err());
}