Always generate composefs blob, don't enable runtime by default
authorColin Walters <walters@verbum.org>
Thu, 9 Jan 2025 15:55:27 +0000 (10:55 -0500)
committerColin Walters <walters@verbum.org>
Fri, 10 Jan 2025 13:04:50 +0000 (08:04 -0500)
Followup to https://github.com/ostreedev/ostree/pull/3353/commits/9a0acd7249bb0c7f55c2bf56e5073902cd60038b

Basically our composefs enablement flag has long had a tension between
trying to do two things:

- Enable generating the composefs blob (at deployment time)
- Enable at runtime in prepare-root

And we've hit issues in "ratcheting" enabling composefs
across upgrades because of this.

This change builds on the previous one, and now it's really
simple to talk about:

- If composefs is enabled at build time, we *always*
  generate a composefs blob at deplyment time
- Configuring the prepare-root config now mostly
  only affects the runtime state.

There is one detail though: in order to handle the
verity requirement at deploy time, we do still parse
the config then.

But for the basic "is composefs enabled at all at runtime"
that is now fully keyed off the config, not the build time
or (worse) whether the deployment happened to have a composefs
blob.

For users who want composefs on, they need to do so in the base
image configuration.

Signed-off-by: Colin Walters <walters@verbum.org>
docs/composefs.md
man/ostree-prepare-root.xml
src/libostree/ostree-sysroot-deploy.c
src/libotcore/otcore-prepare-root.c
tests/test-admin-deploy-composefs.sh

index 513fdb2193497de48df481817108817bd5e59204..f575a73d29f12bb0ad3f3e4eaa2cd92b52aff09c 100644 (file)
@@ -21,22 +21,20 @@ At the current time, integration of composefs and ostree is experimental.
 
 ### Enabling composefs (unsigned)
 
-When building a disk image *or* to transition an existing system, run:
+If ostree is compiled with composefs support, then a composefs file
+corresponding to the deployment tree will be generated by default.
+
+The `ostree-prepare-root` binary will look for `ostree/prepare-root.conf` in `/etc` and
+`/usr/lib` in the initramfs. Using that configuration file you can enable composefs.
+This configuration will enable an "unsigned" mode, which does not require fsverity,
+but does make the system more resilient to accidental mutation.
 
 ```
-ostree config --repo=/ostree/repo set ex-integrity.composefs true
+[composefs]
+enabled = yes
 ```
 
-This will ensure that any future deployments (e.g. created by `ostree admin upgrade`)
-have a `.ostree.cfs` file in the deployment directory which is a mountable
-composefs metadata file, with a "backing store" directory that is
-shared with the current `/ostree/repo/objects`.
-
-### composefs configuration
-
-The `ostree-prepare-root` binary will look for `ostree/prepare-root.conf` in `/etc` and
-`/usr/lib` in the initramfs. Using that configuration file you can enable composefs,
-and specify an Ed25519 public key to validate the booted commit.
+You can also specify an Ed25519 public key to validate the booted commit.
 
 See the manpage for `ostree-prepare-root` for details of how to configure it.
 
index 4a84863e4562e1c113648e9efae34fe13cb6f903..c1f39a8a26d2dd3c04eb107a44b4c9d15978d2a3 100644 (file)
@@ -151,7 +151,12 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
                 the integrity of its backing OSTree object is validated by the digest stored in the image.
                 Additionally, if set to <literal>signed</literal>, boot will fail if the image cannot be
                 validated by a public key.
-                Setting this to <literal>maybe</literal> is currently equivalent to <literal>no</literal>.
+                Setting this to <literal>maybe</literal> will cause composefs to be used at runtime only
+                if the deployment has a composefs generated, which causes unpredicable and confusing semantics
+                and is not recommended. In practice with the <emphasis>current</emphasis> version of ostree,
+                in the case where composefs is enabled at build time for both the version that made the
+                deployment (often an older OS version), this will be equivalent to <literal>yes</literal>.
+                But in general one either wants composefs or not, so choose an explicit value for that.
                 </para></listitem>
             </varlistentry>
             <varlistentry>
index 3d382a6fcfffa8676174f4d2ccaaa1bb4505f572..2d8705d5c86c6a75383d041a02bcc4c85c3fc754 100644 (file)
@@ -669,33 +669,28 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy
   guint64 composefs_start_time = 0;
   guint64 composefs_end_time = 0;
 #ifdef HAVE_COMPOSEFS
-  if (composefs_enabled != OT_TRISTATE_NO)
-    {
-      composefs_start_time = g_get_monotonic_time ();
-      // TODO: Clean up our mess around composefs/fsverity...we have duplication
-      // between the repo config and the sysroot config, *and* we need to better
-      // handle skew between repo config and repo state (e.g. "post-copy" should
-      // support transitioning verity on and off in general).
-      // For now we configure things such that the fsverity digest is only added
-      // if present on disk in the unsigned case, and in the signed case unconditionally
-      // require it.
-      g_auto (GVariantBuilder) cfs_checkout_opts_builder
-          = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
-      guint32 composefs_requested = 1;
-      if (composefs_config->require_verity)
-        composefs_requested = 2;
-      g_variant_builder_add (&cfs_checkout_opts_builder, "{sv}", "verity",
-                             g_variant_new_uint32 (composefs_requested));
-      g_debug ("composefs requested: %u", composefs_requested);
-      g_autoptr (GVariant) cfs_checkout_opts
-          = g_variant_ref_sink (g_variant_builder_end (&cfs_checkout_opts_builder));
-      if (!ostree_repo_checkout_composefs (repo, cfs_checkout_opts, ret_deployment_dfd,
-                                           OSTREE_COMPOSEFS_NAME, csum, cancellable, error))
-        return FALSE;
-      composefs_end_time = g_get_monotonic_time ();
-    }
-  else
-    g_debug ("not using composefs");
+  composefs_start_time = g_get_monotonic_time ();
+  // TODO: Clean up our mess around composefs/fsverity...we have duplication
+  // between the repo config and the sysroot config, *and* we need to better
+  // handle skew between repo config and repo state (e.g. "post-copy" should
+  // support transitioning verity on and off in general).
+  // For now we configure things such that the fsverity digest is only added
+  // if present on disk in the unsigned case, and in the signed case unconditionally
+  // require it.
+  g_auto (GVariantBuilder) cfs_checkout_opts_builder
+      = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
+  guint32 composefs_requested = 1;
+  if (composefs_config->require_verity)
+    composefs_requested = 2;
+  g_variant_builder_add (&cfs_checkout_opts_builder, "{sv}", "verity",
+                         g_variant_new_uint32 (composefs_requested));
+  g_debug ("composefs requested: %u", composefs_requested);
+  g_autoptr (GVariant) cfs_checkout_opts
+      = g_variant_ref_sink (g_variant_builder_end (&cfs_checkout_opts_builder));
+  if (!ostree_repo_checkout_composefs (repo, cfs_checkout_opts, ret_deployment_dfd,
+                                       OSTREE_COMPOSEFS_NAME, csum, cancellable, error))
+    return FALSE;
+  composefs_end_time = g_get_monotonic_time ();
 #else
   if (composefs_enabled == OT_TRISTATE_YES)
     return glnx_throw (error, "composefs: enabled at runtime, but support is not compiled in");
index 90b9905487b07ce47d3ff9143d1fb7e434184561..18bdf43e5663928fc615b75e172dc14d1ad2c95a 100644 (file)
@@ -188,8 +188,8 @@ otcore_load_composefs_config (const char *cmdline, GKeyFile *config, gboolean lo
       ret->is_signed = false;
     }
   else if (!ot_keyfile_get_tristate_with_default (config, OTCORE_PREPARE_ROOT_COMPOSEFS_KEY,
-                                                  OTCORE_PREPARE_ROOT_ENABLED_KEY,
-                                                  OT_TRISTATE_MAYBE, &ret->enabled, error))
+                                                  OTCORE_PREPARE_ROOT_ENABLED_KEY, OT_TRISTATE_NO,
+                                                  &ret->enabled, error))
     return NULL;
 
   // Look for a key - we default to the initramfs binding path.
index ff20005d70bf2b058e5c9981b3d0ee15748cece8..950e8da0a98fce1968f518d79d126734afdc2146 100755 (executable)
@@ -38,10 +38,10 @@ cd -
 ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string version=1.composefs -b testos/buildmain/x86_64-runtime osdata
 ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmain/x86_64-runtime
 
+# We generate the blob now, even if it's explicitly runtime disabled
 ${CMD_PREFIX} ostree admin deploy --os=testos --karg=root=LABEL=foo --karg=testkarg=1 testos:testos/buildmain/x86_64-runtime
-if test -f sysroot/ostree/deploy/testos/deploy/*.0/.ostree.cfs; then
-    fatal "found composefs unexpectedly"
-fi
+cfs_count=$(ls sysroot/ostree/deploy/testos/deploy/*.0/.ostree.cfs | wc -l)
+assert_streq "${cfs_count}" "1"
 
 # check explicit enablement
 cd osdata
@@ -55,7 +55,8 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-str
 ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmain/x86_64-runtime
 
 ${CMD_PREFIX} ostree admin deploy --os=testos --karg=root=LABEL=foo --karg=testkarg=1 testos:testos/buildmain/x86_64-runtime
-ls sysroot/ostree/deploy/testos/deploy/*.0/.ostree.cfs
+cfs_count=$(ls sysroot/ostree/deploy/testos/deploy/*.0/.ostree.cfs | wc -l)
+assert_streq "${cfs_count}" "2"
 
 tap_ok composefs