Skip to content

Commit a615e2b

Browse files
committed
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 #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]>
1 parent 2108921 commit a615e2b

File tree

4 files changed

+74
-15
lines changed

4 files changed

+74
-15
lines changed

docs/src/bootc-install.md

+1-8
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,7 @@ taking precedence. If for example you are building a derived container image fr
113113
you could create a `50-myos.toml` that sets `type = "btrfs"` which will override the
114114
prior setting.
115115

116-
Other available options, also under the `[install]` section:
117-
118-
`kargs`: This allows setting kernel arguments which apply only at the time of `bootc install`.
119-
This option is particularly useful when creating derived/layered images; for example, a cloud
120-
image may want to have its default `console=` set, in contrast with a default base image.
121-
The values in this field are space separated.
122-
123-
`root-fs-type`: This value is the same as `install.filesystem.root.type`.
116+
For other available options, see [bootc-install-config](man/bootc-install-config.md).
124117

125118
## Installing an "unconfigured" image
126119

docs/src/man-md/bootc-install-config.md

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ This is the only defined toplevel table.
2020

2121
The `install`` section supports two subfields:
2222

23+
- `block`: An array of supported `to-disk` backends enabled by this base container image;
24+
if not specified, this will just be `direct`. The only other supported value is `tpm2-luks`.
25+
The first value specified will be the default. To enable both, use `block = ["direct", "tpm2-luks"]`.
2326
- `filesystem`: See below.
2427
- `kargs`: An array of strings; this will be appended to the set of kernel arguments.
2528

lib/src/install/baseline.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,8 @@ pub(crate) struct InstallBlockDeviceOpts {
7979
///
8080
/// direct: Filesystem written directly to block device
8181
/// tpm2-luks: Bind unlock of filesystem to presence of the default tpm2 device.
82-
#[clap(long, value_enum, default_value_t)]
83-
#[serde(default)]
84-
pub(crate) block_setup: BlockSetup,
82+
#[clap(long, value_enum)]
83+
pub(crate) block_setup: Option<BlockSetup>,
8584

8685
/// Target root filesystem type.
8786
#[clap(long, value_enum)]
@@ -297,7 +296,10 @@ pub(crate) fn install_create_rootfs(
297296
};
298297

299298
let base_rootdev = findpart(ROOTPN)?;
300-
let (rootdev, root_blockdev_kargs) = match opts.block_setup {
299+
let block_setup = state
300+
.install_config
301+
.get_block_setup(opts.block_setup.as_ref().copied())?;
302+
let (rootdev, root_blockdev_kargs) = match block_setup {
301303
BlockSetup::Direct => (base_rootdev, None),
302304
BlockSetup::Tpm2Luks => {
303305
let uuid = uuid::Uuid::new_v4().to_string();
@@ -394,7 +396,7 @@ pub(crate) fn install_create_rootfs(
394396
mount::mount(espdev, &efifs_path)?;
395397
}
396398

397-
let luks_device = match opts.block_setup {
399+
let luks_device = match block_setup {
398400
BlockSetup::Direct => None,
399401
BlockSetup::Tpm2Luks => Some(luks_name.to_string()),
400402
};

lib/src/install/config.rs

+63-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use anyhow::{Context, Result};
66
use fn_error_context::context;
77
use serde::{Deserialize, Serialize};
88

9+
use super::baseline::BlockSetup;
10+
911
/// The toplevel config entry for installation configs stored
1012
/// in bootc/install (e.g. /etc/bootc/install/05-custom.toml)
1113
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -39,6 +41,8 @@ pub(crate) struct BasicFilesystems {
3941
pub(crate) struct InstallConfiguration {
4042
/// Root filesystem type
4143
pub(crate) root_fs_type: Option<super::baseline::Filesystem>,
44+
/// Enabled block storage configurations
45+
pub(crate) block: Option<Vec<BlockSetup>>,
4246
pub(crate) filesystem: Option<BasicFilesystems>,
4347
/// Kernel arguments, applied at installation time
4448
#[serde(skip_serializing_if = "Option::is_none")]
@@ -93,6 +97,7 @@ impl Mergeable for InstallConfiguration {
9397
/// Apply any values in other, overriding any existing values in `self`.
9498
fn merge(&mut self, other: Self) {
9599
merge_basic(&mut self.root_fs_type, other.root_fs_type);
100+
merge_basic(&mut self.block, other.block);
96101
self.filesystem.merge(other.filesystem);
97102
if let Some(other_kargs) = other.kargs {
98103
self.kargs
@@ -103,8 +108,8 @@ impl Mergeable for InstallConfiguration {
103108
}
104109

105110
impl InstallConfiguration {
106-
/// Some fields can be specified multiple ways. This synchronizes the values of the fields
107-
/// to ensure they're the same.
111+
/// Set defaults (e.g. `block`), and also handle fields that can be specified multiple ways
112+
/// by synchronizing the values of the fields to ensure they're the same.
108113
///
109114
/// - install.root-fs-type is synchronized with install.filesystems.root.type; if
110115
/// both are set, then the latter takes precedence
@@ -117,6 +122,10 @@ impl InstallConfiguration {
117122
let root = fs.root.get_or_insert_with(Default::default);
118123
root.fstype = Some(*rootfs);
119124
}
125+
126+
if self.block.is_none() {
127+
self.block = Some(vec![BlockSetup::Direct]);
128+
}
120129
}
121130

122131
/// Convenience helper to access the root filesystem
@@ -128,6 +137,18 @@ impl InstallConfiguration {
128137
pub(crate) fn filter_to_external(&mut self) {
129138
self.kargs.take();
130139
}
140+
141+
pub(crate) fn get_block_setup(&self, default: Option<BlockSetup>) -> Result<BlockSetup> {
142+
let valid_block_setups = self.block.as_deref().unwrap_or_default();
143+
let default_block = valid_block_setups.iter().next().ok_or_else(|| {
144+
anyhow::anyhow!("Empty block storage configuration in install configuration")
145+
})?;
146+
let block_setup = default.as_ref().unwrap_or(default_block);
147+
if !valid_block_setups.contains(block_setup) {
148+
anyhow::bail!("Block setup {block_setup:?} is not enabled in installation config");
149+
}
150+
Ok(*block_setup)
151+
}
131152
}
132153

133154
#[context("Loading configuration")]
@@ -257,3 +278,43 @@ type = "xfs"
257278
Filesystem::Ext4
258279
);
259280
}
281+
282+
#[test]
283+
fn test_parse_block() {
284+
let c: InstallConfigurationToplevel = toml::from_str(
285+
r##"[install.filesystem.root]
286+
type = "xfs"
287+
"##,
288+
)
289+
.unwrap();
290+
let mut install = c.install.unwrap();
291+
// Verify the default (but note canonicalization mutates)
292+
{
293+
let mut install = install.clone();
294+
install.canonicalize();
295+
assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Direct);
296+
}
297+
let other = InstallConfigurationToplevel {
298+
install: Some(InstallConfiguration {
299+
block: Some(vec![]),
300+
..Default::default()
301+
}),
302+
};
303+
install.merge(other.install.unwrap());
304+
// Should be set, but zero length
305+
assert_eq!(install.block.as_ref().unwrap().len(), 0);
306+
assert!(install.get_block_setup(None).is_err());
307+
308+
let c: InstallConfigurationToplevel = toml::from_str(
309+
r##"[install]
310+
block = ["tpm2-luks"]"##,
311+
)
312+
.unwrap();
313+
let mut install = c.install.unwrap();
314+
install.canonicalize();
315+
assert_eq!(install.block.as_ref().unwrap().len(), 1);
316+
assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Tpm2Luks);
317+
318+
// And verify passing a disallowed config is an error
319+
assert!(install.get_block_setup(Some(BlockSetup::Direct)).is_err());
320+
}

0 commit comments

Comments
 (0)