mirror of
https://github.com/systemd/systemd
synced 2025-10-08 21:24:45 +02:00
Compare commits
2 Commits
162e5e4a77
...
04b3529cf1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
04b3529cf1 | ||
![]() |
aef605c631 |
@ -108,8 +108,9 @@
|
||||
follows the syntax and rules as described in the <ulink
|
||||
url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> page. The purpose of this
|
||||
file is to identify the extension and to allow the operating system to verify that the extension image
|
||||
matches the base OS. This is typically implemented by checking that the <varname>ID=</varname> options
|
||||
match, and either <varname>SYSEXT_LEVEL=</varname> exists and matches too, or if it is not present,
|
||||
matches the base OS. This is typically implemented by checking that the extension <varname>ID=</varname>
|
||||
option either matches the host <varname>ID=</varname> option or is included the host <varname>ID_LIKE=</varname>
|
||||
option, and either <varname>SYSEXT_LEVEL=</varname> exists and matches too, or if it is not present,
|
||||
<varname>VERSION_ID=</varname> exists and matches. This ensures ABI/API compatibility between the
|
||||
layers and prevents merging of an incompatible image in an overlay.</para>
|
||||
|
||||
|
@ -1516,7 +1516,7 @@ static int mount_image(
|
||||
const char *root_directory,
|
||||
const ImagePolicy *image_policy) {
|
||||
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_id_like = NULL, *host_os_release_version_id = NULL,
|
||||
*host_os_release_sysext_level = NULL, *host_os_release_confext_level = NULL,
|
||||
*extension_name = NULL;
|
||||
int r;
|
||||
@ -1531,6 +1531,7 @@ static int mount_image(
|
||||
r = parse_os_release(
|
||||
empty_to_root(root_directory),
|
||||
"ID", &host_os_release_id,
|
||||
"ID_LIKE", &host_os_release_id_like,
|
||||
"VERSION_ID", &host_os_release_version_id,
|
||||
image_class_info[IMAGE_SYSEXT].level_env, &host_os_release_sysext_level,
|
||||
image_class_info[IMAGE_CONFEXT].level_env, &host_os_release_confext_level,
|
||||
@ -1542,12 +1543,13 @@ static int mount_image(
|
||||
}
|
||||
|
||||
r = verity_dissect_and_mount(
|
||||
/* src_fd= */ -1,
|
||||
/* src_fd= */ -EBADF,
|
||||
mount_entry_source(m),
|
||||
mount_entry_path(m),
|
||||
m->image_options_const,
|
||||
image_policy,
|
||||
host_os_release_id,
|
||||
host_os_release_id_like,
|
||||
host_os_release_version_id,
|
||||
host_os_release_sysext_level,
|
||||
host_os_release_confext_level,
|
||||
@ -1558,9 +1560,10 @@ static int mount_image(
|
||||
return 0;
|
||||
if (r == -ESTALE && host_os_release_id)
|
||||
return log_error_errno(r, // FIXME: this should not be logged ad LOG_ERR, as it will result in duplicate logging.
|
||||
"Failed to mount image %s, extension-release metadata does not match the lower layer's: ID=%s%s%s%s%s%s%s",
|
||||
"Failed to mount image %s, extension-release metadata does not match the lower layer's: ID=%s ID_LIKE='%s'%s%s%s%s%s%s",
|
||||
mount_entry_source(m),
|
||||
host_os_release_id,
|
||||
strempty(host_os_release_id_like),
|
||||
host_os_release_version_id ? " VERSION_ID=" : "",
|
||||
strempty(host_os_release_version_id),
|
||||
host_os_release_sysext_level ? image_class_info[IMAGE_SYSEXT].level_env_print : "",
|
||||
@ -1735,8 +1738,9 @@ static int apply_one_mount(
|
||||
break;
|
||||
|
||||
case MOUNT_EXTENSION_DIRECTORY: {
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
|
||||
*host_os_release_level = NULL, *extension_name = NULL;
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_id_like = NULL,
|
||||
*host_os_release_version_id = NULL, *host_os_release_level = NULL,
|
||||
*extension_name = NULL;
|
||||
_cleanup_strv_free_ char **extension_release = NULL;
|
||||
ImageClass class = IMAGE_SYSEXT;
|
||||
|
||||
@ -1768,6 +1772,7 @@ static int apply_one_mount(
|
||||
r = parse_os_release(
|
||||
empty_to_root(root_directory),
|
||||
"ID", &host_os_release_id,
|
||||
"ID_LIKE", &host_os_release_id_like,
|
||||
"VERSION_ID", &host_os_release_version_id,
|
||||
image_class_info[class].level_env, &host_os_release_level,
|
||||
NULL);
|
||||
@ -1779,6 +1784,7 @@ static int apply_one_mount(
|
||||
r = extension_release_validate(
|
||||
extension_name,
|
||||
host_os_release_id,
|
||||
host_os_release_id_like,
|
||||
host_os_release_version_id,
|
||||
host_os_release_level,
|
||||
/* host_extension_scope = */ NULL, /* Leave empty, we need to accept both system and portable */
|
||||
|
@ -563,7 +563,7 @@ static int extract_image_and_extensions(
|
||||
char ***ret_valid_prefixes,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
|
||||
_cleanup_free_ char *id = NULL, *id_like = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
|
||||
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
|
||||
@ -664,6 +664,7 @@ static int extract_image_and_extensions(
|
||||
|
||||
r = parse_env_file_fd(os_release->fd, os_release->name,
|
||||
"ID", &id,
|
||||
"ID_LIKE", &id_like,
|
||||
"VERSION_ID", &version_id,
|
||||
"SYSEXT_LEVEL", &sysext_level,
|
||||
"CONFEXT_LEVEL", &confext_level,
|
||||
@ -710,9 +711,9 @@ static int extract_image_and_extensions(
|
||||
return r;
|
||||
|
||||
if (validate_extension) {
|
||||
r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release, IMAGE_SYSEXT);
|
||||
r = extension_release_validate(ext->path, id, id_like, version_id, sysext_level, "portable", extension_release, IMAGE_SYSEXT);
|
||||
if (r < 0)
|
||||
r = extension_release_validate(ext->path, id, version_id, confext_level, "portable", extension_release, IMAGE_CONFEXT);
|
||||
r = extension_release_validate(ext->path, id, id_like, version_id, confext_level, "portable", extension_release, IMAGE_CONFEXT);
|
||||
|
||||
if (r == 0)
|
||||
return sd_bus_error_set_errnof(error, ESTALE, "Image %s extension-release metadata does not match the root's", ext->path);
|
||||
|
@ -4030,6 +4030,7 @@ int verity_dissect_and_mount(
|
||||
const MountOptions *options,
|
||||
const ImagePolicy *image_policy,
|
||||
const char *required_host_os_release_id,
|
||||
const char *required_host_os_release_id_like,
|
||||
const char *required_host_os_release_version_id,
|
||||
const char *required_host_os_release_sysext_level,
|
||||
const char *required_host_os_release_confext_level,
|
||||
@ -4158,6 +4159,7 @@ int verity_dissect_and_mount(
|
||||
r = extension_release_validate(
|
||||
dissected_image->image_name,
|
||||
required_host_os_release_id,
|
||||
required_host_os_release_id_like,
|
||||
required_host_os_release_version_id,
|
||||
class == IMAGE_SYSEXT ? required_host_os_release_sysext_level : required_host_os_release_confext_level,
|
||||
required_sysext_scope,
|
||||
|
@ -226,7 +226,7 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
|
||||
|
||||
int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
|
||||
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_host_os_release_confext_level, const char *required_sysext_scope, VeritySettings *verity, DissectedImage **ret_image);
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_id_like, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_host_os_release_confext_level, const char *required_sysext_scope, VeritySettings *verity, DissectedImage **ret_image);
|
||||
|
||||
int dissect_fstype_ok(const char *fstype);
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
int extension_release_validate(
|
||||
const char *name,
|
||||
const char *host_os_release_id,
|
||||
const char *host_os_release_id_like,
|
||||
const char *host_os_release_version_id,
|
||||
const char *host_os_extension_release_level,
|
||||
const char *host_extension_scope,
|
||||
@ -21,6 +22,7 @@ int extension_release_validate(
|
||||
const char *extension_release_id = NULL, *extension_release_level = NULL, *extension_architecture = NULL;
|
||||
const char *extension_level = image_class == IMAGE_CONFEXT ? "CONFEXT_LEVEL" : "SYSEXT_LEVEL";
|
||||
const char *extension_scope = image_class == IMAGE_CONFEXT ? "CONFEXT_SCOPE" : "SYSEXT_SCOPE";
|
||||
_cleanup_strv_free_ char **id_like_l = NULL;
|
||||
|
||||
assert(name);
|
||||
assert(!isempty(host_os_release_id));
|
||||
@ -77,9 +79,19 @@ int extension_release_validate(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!streq(host_os_release_id, extension_release_id)) {
|
||||
log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.",
|
||||
name, extension_release_id, host_os_release_id);
|
||||
/* Match extension OS ID against host OS ID or ID_LIKE */
|
||||
if (host_os_release_id_like) {
|
||||
id_like_l = strv_split(host_os_release_id_like, WHITESPACE);
|
||||
if (!id_like_l)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!streq(host_os_release_id, extension_release_id) && !strv_contains(id_like_l, extension_release_id)) {
|
||||
log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'%s%s%s.",
|
||||
name, extension_release_id, host_os_release_id,
|
||||
host_os_release_id_like ? " (like '" : "",
|
||||
strempty(host_os_release_id_like),
|
||||
host_os_release_id_like ? "')" : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
int extension_release_validate(
|
||||
const char *name,
|
||||
const char *host_os_release_id,
|
||||
const char *host_os_release_id_like,
|
||||
const char *host_os_release_version_id,
|
||||
const char *host_os_extension_release_level,
|
||||
const char *host_extension_scope,
|
||||
|
@ -929,6 +929,7 @@ static int mount_in_namespace_legacy(
|
||||
options,
|
||||
image_policy,
|
||||
/* required_host_os_release_id= */ NULL,
|
||||
/* required_host_os_release_id_like= */ NULL,
|
||||
/* required_host_os_release_version_id= */ NULL,
|
||||
/* required_host_os_release_sysext_level= */ NULL,
|
||||
/* required_host_os_release_confext_level= */ NULL,
|
||||
@ -1140,6 +1141,7 @@ static int mount_in_namespace(
|
||||
options,
|
||||
image_policy,
|
||||
/* required_host_os_release_id= */ NULL,
|
||||
/* required_host_os_release_id_like= */ NULL,
|
||||
/* required_host_os_release_version_id= */ NULL,
|
||||
/* required_host_os_release_sysext_level= */ NULL,
|
||||
/* required_host_os_release_confext_level= */ NULL,
|
||||
|
@ -1638,7 +1638,9 @@ 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_api_level = NULL, *buf = NULL, *filename = NULL;
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_id_like = NULL,
|
||||
*host_os_release_version_id = NULL, *host_os_release_api_level = NULL,
|
||||
*buf = NULL, *filename = NULL;
|
||||
_cleanup_strv_free_ char **extensions = NULL, **extensions_v = NULL, **paths = NULL;
|
||||
size_t n_extensions = 0;
|
||||
unsigned n_ignored = 0;
|
||||
@ -1670,6 +1672,7 @@ static int merge_subprocess(
|
||||
r = parse_os_release(
|
||||
arg_root,
|
||||
"ID", &host_os_release_id,
|
||||
"ID_LIKE", &host_os_release_id_like,
|
||||
"VERSION_ID", &host_os_release_version_id,
|
||||
image_class_info[image_class].level_env, &host_os_release_api_level);
|
||||
if (r < 0)
|
||||
@ -1804,6 +1807,7 @@ static int merge_subprocess(
|
||||
r = extension_release_validate(
|
||||
img->name,
|
||||
host_os_release_id,
|
||||
host_os_release_id_like,
|
||||
host_os_release_version_id,
|
||||
host_os_release_api_level,
|
||||
in_initrd() ? "initrd" : "system",
|
||||
|
@ -85,6 +85,7 @@ prepare_root() {
|
||||
|
||||
{
|
||||
echo "ID=testtest"
|
||||
echo "ID_LIKE=\"foobar test_alike something-else\""
|
||||
echo "VERSION=1.2.3"
|
||||
} >"$root/usr/lib/os-release"
|
||||
|
||||
@ -120,6 +121,38 @@ prepare_extension_image() {
|
||||
prepend_trap "rm -rf ${ext_dir@Q}"
|
||||
}
|
||||
|
||||
prepare_extension_image_with_matching_id() {
|
||||
local root=${1:-}
|
||||
local hierarchy=${2:?}
|
||||
local ext_dir ext_release name
|
||||
|
||||
name="test-extension-matching-id"
|
||||
ext_dir="$root/var/lib/extensions/$name"
|
||||
ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
|
||||
mkdir -p "${ext_release%/*}"
|
||||
echo "ID=testtest" >"$ext_release"
|
||||
mkdir -p "$ext_dir/$hierarchy"
|
||||
touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
|
||||
|
||||
prepend_trap "rm -rf ${ext_dir@Q}"
|
||||
}
|
||||
|
||||
prepare_extension_image_with_matching_id_like() {
|
||||
local root=${1:-}
|
||||
local hierarchy=${2:?}
|
||||
local ext_dir ext_release name
|
||||
|
||||
name="test-extension-matching-id-like"
|
||||
ext_dir="$root/var/lib/extensions/$name"
|
||||
ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
|
||||
mkdir -p "${ext_release%/*}"
|
||||
echo "ID=test_alike" >"$ext_release"
|
||||
mkdir -p "$ext_dir/$hierarchy"
|
||||
touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
|
||||
|
||||
prepend_trap "rm -rf ${ext_dir@Q}"
|
||||
}
|
||||
|
||||
prepare_extension_mutable_dir() {
|
||||
local dir=${1:?}
|
||||
|
||||
@ -981,6 +1014,40 @@ for mutable_mode in no yes ephemeral; do
|
||||
done
|
||||
|
||||
|
||||
( init_trap
|
||||
: "Check if merging an extension with matching ID succeeds"
|
||||
fake_root=${roots_dir:+"$roots_dir/matching-id"}
|
||||
hierarchy=/opt
|
||||
|
||||
prepare_root "$fake_root" "$hierarchy"
|
||||
prepare_extension_image_with_matching_id "$fake_root" "$hierarchy"
|
||||
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
|
||||
|
||||
run_systemd_sysext "$fake_root" merge
|
||||
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
|
||||
run_systemd_sysext "$fake_root" unmerge
|
||||
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
)
|
||||
|
||||
|
||||
( init_trap
|
||||
: "Check if merging an extension that matches host ID_LIKE succeeds"
|
||||
fake_root=${roots_dir:+"$roots_dir/matching-id-like"}
|
||||
hierarchy=/opt
|
||||
|
||||
prepare_root "$fake_root" "$hierarchy"
|
||||
prepare_extension_image_with_matching_id_like "$fake_root" "$hierarchy"
|
||||
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
|
||||
|
||||
run_systemd_sysext "$fake_root" merge
|
||||
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
|
||||
run_systemd_sysext "$fake_root" unmerge
|
||||
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
)
|
||||
|
||||
|
||||
( init_trap
|
||||
: "Check if merging fails in case of invalid mutable directory permissions"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user