Skip to content

Commit

Permalink
install: Add block to config, disable tpm2-luks unless opted-in
Browse files Browse the repository at this point in the history
This allows the container image builder more control over
`bootc install to-disk` in the installation config.  Per discussion in
containers#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 <[email protected]>
  • Loading branch information
cgwalters committed Apr 2, 2024
1 parent 2108921 commit a615e2b
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 15 deletions.
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
65 changes: 63 additions & 2 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 @@ -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());
}

0 comments on commit a615e2b

Please sign in to comment.