<term><option>--force</option></term>
<listitem><para>When merging system extensions into <filename>/usr/</filename> and
- <filename>/opt/</filename>, ignore version incompatibilities, i.e. force merging regardless of
- whether the version information included in the extension images matches the host or
- not.</para></listitem>
+ <filename>/opt/</filename> for sysext and <filename>/etc/</filename> for confext,
+ ignore version incompatibilities, i.e. force merging regardless of
+ whether the version information included in the images matches the host or not.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--image-policy=<replaceable>policy</replaceable></option></term>
+
+ <listitem><para>Takes an image policy string as argument, as per
+ <citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
+ policy is enforced when operating on system extension disk images. If not specified defaults to
+ <literal>root=verity+signed+encrypted+unprotected+absent:usr=verity+signed+encrypted+unprotected+absent</literal>,
+ i.e. only the root and <filename>/usr/</filename> file systems in the image are used. When run in the
+ initrd and operating on a system extension image stored in the <filename>/.extra/sysext/</filename>
+ directory a slightly stricter policy is used by default:
+ <literal>root=signed+absent:usr=signed+absent</literal>, see above for details.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
if (MANAGER_IS_SYSTEM(u->manager)) {
propagate_dir = path_join("/run/systemd/propagate/", u->id);
- if (!propagate_dir) {
- r = -ENOMEM;
- goto finalize;
- }
+ if (!propagate_dir)
+ return -ENOMEM;
incoming_dir = strdup("/run/systemd/incoming");
- if (!incoming_dir) {
- r = -ENOMEM;
- goto finalize;
- }
+ if (!incoming_dir)
+ return -ENOMEM;
extension_dir = strdup("/run/systemd/unit-extensions");
- if (!extension_dir) {
- r = -ENOMEM;
- goto finalize;
- }
+ if (!extension_dir)
+ return -ENOMEM;
} else
- if (asprintf(&extension_dir, "/run/user/" UID_FMT "/systemd/unit-extensions", geteuid()) < 0) {
- r = -ENOMEM;
- goto finalize;
- }
+ if (asprintf(&extension_dir, "/run/user/" UID_FMT "/systemd/unit-extensions", geteuid()) < 0)
+ return -ENOMEM;
- r = setup_namespace(root_dir, root_image, context->root_image_options,
- &ns_info, read_write_paths,
- needs_sandboxing ? context->read_only_paths : NULL,
- needs_sandboxing ? context->inaccessible_paths : NULL,
- needs_sandboxing ? context->exec_paths : NULL,
- needs_sandboxing ? context->no_exec_paths : NULL,
- empty_directories,
- symlinks,
- bind_mounts,
- n_bind_mounts,
- context->temporary_filesystems,
- context->n_temporary_filesystems,
- context->mount_images,
- context->n_mount_images,
- tmp_dir,
- var_tmp_dir,
- creds_path,
- context->log_namespace,
- context->mount_propagation_flag,
- context->root_hash, context->root_hash_size, context->root_hash_path,
- context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
- context->root_verity,
- context->extension_images,
- context->n_extension_images,
- context->extension_directories,
- propagate_dir,
- incoming_dir,
- extension_dir,
- root_dir || root_image ? params->notify_socket : NULL,
- error_path);
+ r = setup_namespace(
+ root_dir,
+ root_image,
+ context->root_image_options,
+ context->root_image_policy ?: &image_policy_service,
+ &ns_info,
+ read_write_paths,
+ needs_sandboxing ? context->read_only_paths : NULL,
+ needs_sandboxing ? context->inaccessible_paths : NULL,
+ needs_sandboxing ? context->exec_paths : NULL,
+ needs_sandboxing ? context->no_exec_paths : NULL,
+ empty_directories,
+ symlinks,
+ bind_mounts,
+ n_bind_mounts,
+ context->temporary_filesystems,
+ context->n_temporary_filesystems,
+ context->mount_images,
+ context->n_mount_images,
+ context->mount_image_policy ?: &image_policy_service,
+ tmp_dir,
+ var_tmp_dir,
+ creds_path,
+ context->log_namespace,
+ context->mount_propagation_flag,
+ context->root_hash, context->root_hash_size, context->root_hash_path,
+ context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
+ context->root_verity,
+ context->extension_images,
+ context->n_extension_images,
+ context->extension_image_policy ?: &image_policy_sysext,
+ context->extension_directories,
+ propagate_dir,
+ incoming_dir,
+ extension_dir,
+ root_dir || root_image ? params->notify_socket : NULL,
+ error_path);
/* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
* that with a special, recognizable error ENOANO. In this case, silently proceed, but only if exclusively
* because extension images are supposed to extend /usr/, so you get into recursive races, especially
* with directory-based extensions, as the kernel's OverlayFS explicitly checks for this and errors
* out with -ELOOP if it finds that a lowerdir= is a child of another lowerdir=. */
- [IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
- "/run/extensions\0" /* and here too */
- "/var/lib/extensions\0", /* the main place for images */
+ [IMAGE_SYSEXT] = "/etc/extensions\0" /* only place symlinks here */
+ "/run/extensions\0" /* and here too */
+ "/var/lib/extensions\0", /* the main place for images */
+
+ [IMAGE_CONFEXT] = "/run/confexts\0" /* only place symlinks here */
+ "/var/lib/confexts\0" /* the main place for images */
+ "/usr/local/lib/confexts\0"
+ "/usr/lib/confexts\0",
};
+ /* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext in extension
+ * search dir) */
+ static const char* const image_search_path_initrd[_IMAGE_CLASS_MAX] = {
+ /* (entries that aren't listed here will get the same search path as for the non initrd-case) */
+
+ [IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
+ "/run/extensions\0" /* and here too */
+ "/var/lib/extensions\0" /* the main place for images */
+ "/usr/local/lib/extensions\0"
+ "/usr/lib/extensions\0"
+ "/.extra/sysext\0" /* put sysext picked up by systemd-stub last, since not trusted */
+ };
+
static Image *image_free(Image *i) {
assert(i);
#include "sd-id128.h"
#include "hashmap.h"
+ #include "image-policy.h"
#include "lock-util.h"
#include "macro.h"
+#include "os-util.h"
#include "path-util.h"
#include "string-util.h"
#include "time-util.h"
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_force = false;
+ static ImagePolicy *arg_image_policy = NULL;
+/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
+static ImageClass arg_image_class = IMAGE_SYSEXT;
+
STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+ STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
+/* Helper struct for naming simplicity and reusability */
+static const struct {
+ const char *dot_directory_name;
+ const char *directory_name;
+ const char *short_identifier;
+ const char *short_identifier_plural;
+ const char *level_env;
+ const char *scope_env;
+ const char *name_env;
+} image_class_info[_IMAGE_CLASS_MAX] = {
+ [IMAGE_SYSEXT] = {
+ .dot_directory_name = ".systemd-sysext",
+ .directory_name = "systemd-sysext",
+ .short_identifier = "sysext",
+ .short_identifier_plural = "extensions",
+ .level_env = "SYSEXT_LEVEL",
+ .scope_env = "SYSEXT_SCOPE",
+ .name_env = "SYSTEMD_SYSEXT_HIERARCHIES",
+ },
+ [IMAGE_CONFEXT] = {
+ .dot_directory_name = ".systemd-confext",
+ .directory_name = "systemd-confext",
+ .short_identifier = "confext",
+ .short_identifier_plural = "confexts",
+ .level_env = "CONFEXT_LEVEL",
+ .scope_env = "CONFEXT_SCOPE",
+ .name_env = "SYSTEMD_CONFEXT_HIERARCHIES",
+ }
+};
+
static int is_our_mount_point(const char *p) {
_cleanup_free_ char *buf = NULL, *f = NULL;
struct stat st;
return strverscmp_improved(*a, *b);
}
+ static const ImagePolicy *pick_image_policy(const Image *img) {
+ assert(img);
+ assert(img->path);
+
+ /* Explicitly specified policy always wins */
+ if (arg_image_policy)
+ return arg_image_policy;
+
+ /* If located in /.extra/sysext/ in the initrd, then it was placed there by systemd-stub, and was
+ * picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
+ * other directories we assume the appropriate level of trust was already established already. */
+
+ if (in_initrd() && path_startswith(img->path, "/.extra/sysext/"))
+ return &image_policy_sysext_strict;
+
+ return &image_policy_sysext;
+ }
+
static int merge_subprocess(Hashmap *images, const char *workspace) {
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL,
- *buf = NULL;
+ *host_os_release_confext_level = NULL, *buf = NULL;
_cleanup_strv_free_ char **extensions = NULL, **paths = NULL;
size_t n_extensions = 0;
unsigned n_ignored = 0;
if (!images)
return log_oom();
- r = image_discover(IMAGE_EXTENSION, arg_root, images);
+ r = image_discover(arg_image_class, arg_root, images);
if (r < 0)
- return log_error_errno(r, "Failed to discover extension images: %m");
+ return log_error_errno(r, "Failed to discover images: %m");
HASHMAP_FOREACH(img, images) {
- r = image_read_metadata(img);
+ r = image_read_metadata(img, &image_policy_sysext);
if (r < 0)
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
}