diff --git a/man/system-or-user-ns-mountfsd-mount-options.xml b/man/system-or-user-ns-mountfsd-mount-options.xml new file mode 100644 index 0000000000..f81adc64b3 --- /dev/null +++ b/man/system-or-user-ns-mountfsd-mount-options.xml @@ -0,0 +1,32 @@ + + + + + + + + + <para id="plural">When enabled for services running in per-user instances of the service manager + using mount options is disabled by default, due to the security implications. It is possible to use a + <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink> policy to allow + specific mount options, for example: + + <example id="example"> + <title>A polkit policy that allows mounting the root partition with nosuid + + /etc/polkit-1/rules.d/mountoptions.rules: + + +polkit.addRule(function(action, subject) { + if (action.id == "io.systemd.mount-file-system.mount-untrusted-image-privately" && + action.lookup("mount_options") == "root:nosuid") { + return polkit.Result.YES; + } +}); + + + + + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index be74cb52ef..27f9f6122d 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -226,6 +226,9 @@ + + + @@ -524,6 +527,9 @@ + + + @@ -591,6 +597,9 @@ + + + diff --git a/src/core/namespace.c b/src/core/namespace.c index 422c18d238..172e37d3b0 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2619,6 +2619,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) { r = mountfsd_mount_image( p->root_image, userns_fd, + p->root_image_options, p->root_image_policy, p->verity, dissect_image_flags, diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 8883dc6e26..65fb826663 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -2188,6 +2188,7 @@ static int run(int argc, char *argv[]) { r = mountfsd_mount_image( arg_image, userns_fd, + /* options= */ NULL, arg_image_policy, &arg_verity_settings, arg_flags, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index bb90cc1c94..80bbd27fb0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -6381,6 +6381,7 @@ static int run(int argc, char *argv[]) { r = mountfsd_mount_image( arg_image, userns_fd, + /* options= */ NULL, arg_image_policy, &arg_verity_settings, dissect_image_flags, diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 03edb6a72d..14120f2d57 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -4934,6 +4934,7 @@ int verity_dissect_and_mount( r = mountfsd_mount_image( src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, userns_fd, + options, image_policy, verity, dissect_image_flags, @@ -5100,6 +5101,7 @@ static void mount_image_reply_parameters_done(MountImageReplyParameters *p) { int mountfsd_mount_image_fd( int image_fd, int userns_fd, + const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, @@ -5171,6 +5173,19 @@ int mountfsd_mount_image_fd( return log_error_errno(r, "Failed to push verity data fd into varlink connection: %m"); } + _cleanup_(sd_json_variant_unrefp) sd_json_variant *mount_options = NULL; + for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { + const char *o = mount_options_from_designator(options, i); + if (!o) + continue; + + r = sd_json_variant_merge_objectbo( + &mount_options, + SD_JSON_BUILD_PAIR_STRING(partition_designator_to_string(i), o)); + if (r < 0) + return log_error_errno(r, "Failed to build mount options array: %m"); + } + sd_json_variant *reply = NULL; r = varlink_callbo_and_log( vl, @@ -5182,6 +5197,7 @@ int mountfsd_mount_image_fd( SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY)), SD_JSON_BUILD_PAIR_BOOLEAN("growFileSystems", FLAGS_SET(flags, DISSECT_IMAGE_GROWFS)), SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)), + JSON_BUILD_PAIR_VARIANT_NON_NULL("mountOptions", mount_options), SD_JSON_BUILD_PAIR_BOOLEAN("veritySharing", FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)), SD_JSON_BUILD_PAIR_CONDITION(verity_data_fd >= 0, "verityDataFileDescriptor", SD_JSON_BUILD_UNSIGNED(userns_fd >= 0 ? 2 : 1)), SD_JSON_BUILD_PAIR_CONDITION(verity && iovec_is_set(&verity->root_hash), "verityRootHash", JSON_BUILD_IOVEC_HEX(&verity->root_hash)), @@ -5274,6 +5290,7 @@ int mountfsd_mount_image_fd( int mountfsd_mount_image( const char *path, int userns_fd, + const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, @@ -5289,7 +5306,7 @@ int mountfsd_mount_image( return log_error_errno(errno, "Failed to open '%s': %m", path); _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL; - r = mountfsd_mount_image_fd(image_fd, userns_fd, image_policy, verity, flags, &di); + r = mountfsd_mount_image_fd(image_fd, userns_fd, options, image_policy, verity, flags, &di); if (r < 0) return r; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 16b42c2ed3..55c291c4ab 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -268,8 +268,8 @@ static inline const char* dissected_partition_fstype(const DissectedPartition *m int get_common_dissect_directory(char **ret); -int mountfsd_mount_image_fd(int image_fd, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); -int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); +int mountfsd_mount_image_fd(int image_fd, int userns_fd, const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); +int mountfsd_mount_image(const char *path, int userns_fd, const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret); int mountfsd_mount_directory_fd(int directory_fd, int userns_fd, DissectImageFlags flags, int *ret_mount_fd); int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd); diff --git a/test/units/TEST-50-DISSECT.mountfsd.sh b/test/units/TEST-50-DISSECT.mountfsd.sh index cf2a2284e9..94f802e780 100755 --- a/test/units/TEST-50-DISSECT.mountfsd.sh +++ b/test/units/TEST-50-DISSECT.mountfsd.sh @@ -91,6 +91,43 @@ if [ "$VERITY_SIG_SUPPORTED" -eq 1 ]; then --property ExtensionImagePolicy=root=verity+signed+absent:usr=verity+signed+absent \ bash -c "test -e \"/dev/mapper/${MINIMAL_IMAGE_ROOTHASH}-verity\" && test -e \"/dev/mapper/$(/etc/polkit-1/rules.d/mountoptions.rules <<'EOF' +polkit.addRule(function(action, subject) { + if (action.id == "io.systemd.mount-file-system.mount-untrusted-image-privately" && + action.lookup("mount_options") == "root:nosuid") { + return polkit.Result.YES; + } +}); +EOF + systemctl try-reload-or-restart polkit.service + + systemd-run -M testuser@ --user --pipe --wait \ + --property RootImage="$MINIMAL_IMAGE.gpt" \ + --property RootImageOptions="root:nosuid" \ + sh -c "test -e \"/dev/mapper/${MINIMAL_IMAGE_ROOTHASH}-verity\" && mount | grep -F squashfs | grep -q -F nosuid" + + systemd-run -M testuser@ --user --pipe --wait \ + --property RootImage="$MINIMAL_IMAGE.raw" \ + --property ExtensionImages=/tmp/app0.raw \ + --property MountImages=/tmp/app0.raw:/var/tmp:nosuid \ + sh -c "test -e \"/dev/mapper/${MINIMAL_IMAGE_ROOTHASH}-verity\" && test -e \"/dev/mapper/$(