Skip to content

Commit be35678

Browse files
committed
install: Verify target image fetch by default
Now that we've dropped the `--net=none` by default, let's avoid two major footguns by verifying the target image specification by default. - Forgetting to use `--target-no-signature-verification` (most people are going to need this in demos right now) - When the target OS requires an authenticated pull, but one didn't embed the pull secret in the target OS Signed-off-by: Colin Walters <[email protected]>
1 parent 5d46e24 commit be35678

File tree

4 files changed

+60
-2
lines changed

4 files changed

+60
-2
lines changed

docs/install.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ The `--pid=host --security-opt label=type:unconfined_t` today
5959
make it more convenient for bootc to perform some privileged
6060
operations; in the future these requirement may be dropped.
6161

62+
### "day 2" updates, security and fetch configuration
63+
64+
Note that by default the `bootc install` path will find the pull specification used
65+
for the `podman run` invocation and use it to set up "day 2" OS updates that `bootc update`
66+
will use.
67+
68+
For example, if you invoke `podman run --privileged ... quay.io/examplecorp/exampleos:latest bootc install ...`
69+
then the installed operating system will fetch updates from `quay.io/examplecorp/exampleos:latest`.
70+
This can be overridden via `--target_imgref`; this is handy in cases like performing
71+
installation in a manufacturing environment from a mirrored registry.
72+
73+
By default, the installation process will verify that the container (representing the target OS)
74+
can fetch its own updates. A common cause of failure here is not changing the security settings
75+
in `/etc/containers/policy.json` in the target OS to verify signatures.
76+
77+
If you are pushing an unsigned image, you must specify `bootc install --target-no-signature-verification`.
78+
79+
Additionally note that to perform an install from an authenticated registry, you must also embed
80+
the pull secret into the image to pass this check. If you are fetching
81+
6282
### Operating system install configuration required
6383

6484
The container image must define its default install configuration. For example,

lib/src/install.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ pub(crate) struct InstallTargetOpts {
7373
/// Enable verification via an ostree remote
7474
#[clap(long)]
7575
pub(crate) target_ostree_remote: Option<String>,
76+
77+
/// By default, the accessiblity of the target image will be verified (just the manifest will be fetched).
78+
/// Specifying this option suppresses the check; use this when you know the issues it might find
79+
/// are addressed.
80+
///
81+
/// Two main reasons this might fail:
82+
///
83+
/// - Forgetting `--target-no-signature-verification` if needed
84+
/// - Using a registry which requires authentication, but not embedding the pull secret in the image.
85+
#[clap(long)]
86+
#[serde(default)]
87+
pub(crate) skip_fetch_check: bool,
7688
}
7789

7890
#[derive(clap::Args, Debug, Clone, Serialize, Deserialize)]
@@ -765,6 +777,28 @@ pub(crate) fn propagate_tmp_mounts_to_host() -> Result<()> {
765777
Ok(())
766778
}
767779

780+
/// Verify that we can load the manifest of the target image
781+
#[context("Verifying fetch")]
782+
async fn verify_target_fetch(imgref: &ostree_container::OstreeImageReference) -> Result<()> {
783+
let tmpdir = tempfile::tempdir()?;
784+
let tmprepo = &ostree::Repo::new_for_path(tmpdir.path());
785+
tmprepo
786+
.create(ostree::RepoMode::Bare, ostree::gio::Cancellable::NONE)
787+
.context("Init tmp repo")?;
788+
789+
tracing::trace!("Verifying fetch for {imgref}");
790+
let mut imp =
791+
ostree_container::store::ImageImporter::new(&tmprepo, imgref, Default::default()).await?;
792+
use ostree_container::store::PrepareResult;
793+
let prep = match imp.prepare().await? {
794+
// SAFETY: It's impossible that the image was already fetched into this newly created temporary repository
795+
PrepareResult::AlreadyPresent(_) => unreachable!(),
796+
PrepareResult::Ready(r) => r,
797+
};
798+
tracing::debug!("Fetched manifest with digest {}", prep.manifest_digest);
799+
Ok(())
800+
}
801+
768802
/// Preparation for an install; validates and prepares some (thereafter immutable) global state.
769803
async fn prepare_install(
770804
config_opts: InstallConfigOpts,
@@ -816,6 +850,10 @@ async fn prepare_install(
816850
};
817851
tracing::debug!("Target image reference: {target_imgref}");
818852

853+
if !target_opts.skip_fetch_check {
854+
verify_target_fetch(&target_imgref).await?;
855+
}
856+
819857
ensure_var()?;
820858
propagate_tmp_mounts_to_host()?;
821859

lib/src/privtests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ fn test_install_filesystem(image: &str, blockdev: &Utf8Path) -> Result<()> {
152152
let mountpoint: &Utf8Path = mountpoint_dir.path().try_into().unwrap();
153153

154154
// And run the install
155-
cmd!(sh, "podman run --rm --privileged --pid=host --env=RUST_LOG -v /usr/bin/bootc:/usr/bin/bootc -v {mountpoint}:/target-root {image} bootc install-to-filesystem /target-root").run()?;
155+
cmd!(sh, "podman run --rm --privileged --pid=host --env=RUST_LOG -v /usr/bin/bootc:/usr/bin/bootc -v {mountpoint}:/target-root {image} bootc install-to-filesystem --target-no-signature-verification /target-root").run()?;
156156

157157
cmd!(sh, "umount -R {mountpoint}").run()?;
158158

tests/kolainst/install

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ cd $(mktemp -d)
1919

2020
case "${AUTOPKGTEST_REBOOT_MARK:-}" in
2121
"")
22-
podman run --rm -ti --privileged --pid=host -v /usr/bin/bootc:/usr/bin/bootc ${IMAGE} bootc install --karg=foo=bar ${DEV}
22+
podman run --rm -ti --privileged --pid=host -v /usr/bin/bootc:/usr/bin/bootc ${IMAGE} bootc install --target-no-signature-verification --karg=foo=bar ${DEV}
2323
# In theory we could e.g. wipe the bootloader setup on the primary disk, then reboot;
2424
# but for now let's just sanity test that the install command executes.
2525
lsblk ${DEV}

0 commit comments

Comments
 (0)