From 22b8e4f953ed183a368ec97ce7f74337b1574045 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 14 Jul 2023 14:31:58 -0400 Subject: [PATCH 1/3] prepare-root: Introduce `ostree/prepare-root.conf` Using the repository configuration for configuration of this program was always a bit hacky. But actually with composefs, we really must validate the target root *before* we parse anything in it. Let's add a config file for `ostree-prepare-root` that can live in the initramfs, which will already have been verified. In the future we'll also add configuration for composefs here. We expect OS builders to drop this in `/usr/lib/ostree/prepare-root.conf`, but system local configuration can live in `/etc`. --- man/ostree-prepare-root.xml | 17 ++++++++ src/boot/dracut/module-setup.sh | 5 +++ src/switchroot/ostree-prepare-root.c | 62 ++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/man/ostree-prepare-root.xml b/man/ostree-prepare-root.xml index 8b42113a74..8726ccf18a 100644 --- a/man/ostree-prepare-root.xml +++ b/man/ostree-prepare-root.xml @@ -100,6 +100,23 @@ License along with this library. If not, see . + + Configuration + + + The /usr/lib/ostree/prepare-root.conf (or /etc/ostree/prepare-root.conf) config file is parsed by ostree-prepare-root. This file must + be present in the initramfs. The default dracut module will copy it from the real root if present. + + + + + sysroot.readonly + A boolean value; the default is false. If this is set to true, then the /sysroot mount point is mounted read-only. + + + + + systemd diff --git a/src/boot/dracut/module-setup.sh b/src/boot/dracut/module-setup.sh index bedf584ec3..987e7697c7 100755 --- a/src/boot/dracut/module-setup.sh +++ b/src/boot/dracut/module-setup.sh @@ -33,6 +33,11 @@ depends() { install() { dracut_install /usr/lib/ostree/ostree-prepare-root + for r in /usr/lib /etc; do + if test -f "$r/ostree/prepare-root.conf"; then + inst_simple "$r/ostree/prepare-root.conf" + fi + done inst_simple "${systemdsystemunitdir}/ostree-prepare-root.service" mkdir -p "${initdir}${systemdsystemconfdir}/initrd-root-fs.target.wants" ln_r "${systemdsystemunitdir}/ostree-prepare-root.service" \ diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index 5f35fc4345..dfac548134 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -73,8 +73,16 @@ #include #include +#include "ot-keyfile-utils.h" #include "otcore.h" +// The path to the config file for this binary +const char *config_roots[] = { "/usr/lib", "/etc" }; +#define PREPARE_ROOT_CONFIG_PATH "ostree/prepare-root.conf" + +#define SYSROOT_KEY "sysroot" +#define READONLY_KEY "readonly" + // The kernel argument we support to configure composefs. #define OT_COMPOSEFS_KARG "ot-composefs" @@ -91,6 +99,35 @@ #include "ostree-mount-util.h" +// Load our config file; if it doesn't exist, we return an empty configuration. +// NULL will be returned if we caught an error. +static GKeyFile * +load_config (GError **error) +{ + g_autoptr (GKeyFile) ret = g_key_file_new (); + + for (guint i = 0; i < G_N_ELEMENTS (config_roots); i++) + { + glnx_autofd int fd = -1; + g_autofree char *path = g_build_filename (config_roots[i], PREPARE_ROOT_CONFIG_PATH, NULL); + if (!ot_openat_ignore_enoent (AT_FDCWD, path, &fd, error)) + return NULL; + /* If the config file doesn't exist, that's OK */ + if (fd == -1) + continue; + + g_print ("Loading %s\n", path); + + g_autofree char *buf = glnx_fd_readall_utf8 (fd, NULL, NULL, error); + if (!buf) + return NULL; + if (!g_key_file_load_from_data (ret, buf, -1, 0, error)) + return NULL; + } + + return g_steal_pointer (&ret); +} + static bool sysroot_is_configured_ro (const char *sysroot) { @@ -103,7 +140,7 @@ sysroot_is_configured_ro (const char *sysroot) return false; } - return g_key_file_get_boolean (repo_config, "sysroot", "readonly", NULL); + return g_key_file_get_boolean (repo_config, SYSROOT_KEY, READONLY_KEY, NULL); } static inline char * @@ -302,6 +339,15 @@ main (int argc, char *argv[]) err (EXIT_FAILURE, "usage: ostree-prepare-root SYSROOT"); root_arg = argv[1]; + g_autoptr (GKeyFile) config = load_config (&error); + if (!config) + errx (EXIT_FAILURE, "Failed to parse config: %s", error->message); + + gboolean sysroot_readonly = FALSE; + if (!ot_keyfile_get_boolean_with_default (config, SYSROOT_KEY, READONLY_KEY, FALSE, + &sysroot_readonly, &error)) + errx (EXIT_FAILURE, "Failed to parse sysroot.readonly value: %s", error->message); + /* This is the final target where we should prepare the rootfs. The usual * case with systemd in the initramfs is that root_mountpoint = "/sysroot". * In the fastboot embedded case we're pid1 and will setup / ourself, and @@ -317,10 +363,18 @@ main (int argc, char *argv[]) if (mkdirat (AT_FDCWD, OTCORE_RUN_OSTREE_PRIVATE, 0) < 0) err (EXIT_FAILURE, "Failed to create %s", OTCORE_RUN_OSTREE_PRIVATE); - /* Query the repository configuration - this is an operating system builder - * choice. More info: https://github.com/ostreedev/ostree/pull/1767 + /* Fall back to querying the repository configuration in the target disk. + * This is an operating system builder choice. More info: + * https://github.com/ostreedev/ostree/pull/1767 */ - const bool sysroot_readonly = sysroot_is_configured_ro (root_arg); + if (!sysroot_readonly) + { + sysroot_readonly = sysroot_is_configured_ro (root_arg); + // Encourage porting to the new config file + if (sysroot_readonly) + g_print ("Found legacy sysroot.readonly flag, not configured in %s\n", + PREPARE_ROOT_CONFIG_PATH); + } const bool sysroot_currently_writable = !path_is_on_readonly_fs (root_arg); g_print ("sysroot.readonly configuration value: %d (fs writable: %d)\n", (int)sysroot_readonly, (int)sysroot_currently_writable); From 83d37d6d3cecf746bd63c0c97bf9154b889e67ba Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 23 Jul 2023 14:33:51 -0400 Subject: [PATCH 2/3] prepare-root: Default sysroot.readonly=true if composefs Not because it's logically required or anything, but because it's just a good idea. --- src/switchroot/ostree-prepare-root.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index dfac548134..c0f9d75378 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -344,8 +344,18 @@ main (int argc, char *argv[]) errx (EXIT_FAILURE, "Failed to parse config: %s", error->message); gboolean sysroot_readonly = FALSE; - if (!ot_keyfile_get_boolean_with_default (config, SYSROOT_KEY, READONLY_KEY, FALSE, - &sysroot_readonly, &error)) + + // We always parse the composefs config, because we want to detect and error + // out if it's enabled, but not supported at compile time. + g_autoptr (ComposefsConfig) composefs_config = load_composefs_config (&error); + if (!composefs_config) + errx (EXIT_FAILURE, "%s", error->message); + + // If composefs is enabled, that also implies sysroot.readonly=true because it's + // the new default we want to use (not because it's actually required) + const bool sysroot_readonly_default = composefs_config->enabled == OT_TRISTATE_YES; + if (!ot_keyfile_get_boolean_with_default (config, SYSROOT_KEY, READONLY_KEY, + sysroot_readonly_default, &sysroot_readonly, &error)) errx (EXIT_FAILURE, "Failed to parse sysroot.readonly value: %s", error->message); /* This is the final target where we should prepare the rootfs. The usual @@ -398,11 +408,6 @@ main (int argc, char *argv[]) GVariantBuilder metadata_builder; g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}")); - // We always parse the composefs config, because we want to detect and error - // out if it's enabled, but not supported at compile time. - g_autoptr (ComposefsConfig) composefs_config = load_composefs_config (&error); - if (!composefs_config) - errx (EXIT_FAILURE, "%s", error->message); // Tracks if we did successfully enable it at runtime bool using_composefs = false; From 34656260154ad9affcd4d60c568d06c82b7149a6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 23 Jul 2023 14:39:04 -0400 Subject: [PATCH 3/3] prepare-root: Don't parse target root when composefs enabled We shouldn't load anything from the target root filesystem *before* verifying its integrity if composefs is enabled. In effect, we want to force composefs users to migrate to `/usr/lib/ostree/prepare-root.conf` which lives in the initramfs. (But because we enable sysroot.readonly=true if composefs is enabled too, they don't actually need to) --- src/switchroot/ostree-prepare-root.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index c0f9d75378..bf5de00a9c 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -376,8 +376,10 @@ main (int argc, char *argv[]) /* Fall back to querying the repository configuration in the target disk. * This is an operating system builder choice. More info: * https://github.com/ostreedev/ostree/pull/1767 + * However, we only do this if composefs is not enabled, because we don't + * want to parse the target root filesystem before verifying its integrity. */ - if (!sysroot_readonly) + if (!sysroot_readonly && composefs_config->enabled != OT_TRISTATE_YES) { sysroot_readonly = sysroot_is_configured_ro (root_arg); // Encourage porting to the new config file