dissect: support mount options when going through mountfsd
RootImageOptions=/ExtensionImages=/MountImages= all support custom mount options, use the new mountfsd parameters to configure them if they are specified. This requires additioanl privileges via polkit due to security implications of mount options, so document an example policy that allows to use the nosuid mount option.
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE refsect1 PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
-->
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title/>
|
||||||
|
|
||||||
|
<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</title>
|
||||||
|
|
||||||
|
<para><filename index="false">/etc/polkit-1/rules.d/mountoptions.rules</filename>:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</refsect1>
|
||||||
@@ -226,6 +226,9 @@
|
|||||||
|
|
||||||
<xi:include href="system-only.xml" xpointer="singular"/>
|
<xi:include href="system-only.xml" xpointer="singular"/>
|
||||||
|
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="plural"/>
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="example"/>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@@ -524,6 +527,9 @@
|
|||||||
|
|
||||||
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/>
|
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/>
|
||||||
|
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="plural"/>
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="example"/>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@@ -591,6 +597,9 @@
|
|||||||
|
|
||||||
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/>
|
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/>
|
||||||
|
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="plural"/>
|
||||||
|
<xi:include href="system-or-user-ns-mountfsd-mount-options.xml" xpointer="example"/>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|||||||
@@ -2619,6 +2619,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
|||||||
r = mountfsd_mount_image(
|
r = mountfsd_mount_image(
|
||||||
p->root_image,
|
p->root_image,
|
||||||
userns_fd,
|
userns_fd,
|
||||||
|
p->root_image_options,
|
||||||
p->root_image_policy,
|
p->root_image_policy,
|
||||||
p->verity,
|
p->verity,
|
||||||
dissect_image_flags,
|
dissect_image_flags,
|
||||||
|
|||||||
@@ -2188,6 +2188,7 @@ static int run(int argc, char *argv[]) {
|
|||||||
r = mountfsd_mount_image(
|
r = mountfsd_mount_image(
|
||||||
arg_image,
|
arg_image,
|
||||||
userns_fd,
|
userns_fd,
|
||||||
|
/* options= */ NULL,
|
||||||
arg_image_policy,
|
arg_image_policy,
|
||||||
&arg_verity_settings,
|
&arg_verity_settings,
|
||||||
arg_flags,
|
arg_flags,
|
||||||
|
|||||||
@@ -6381,6 +6381,7 @@ static int run(int argc, char *argv[]) {
|
|||||||
r = mountfsd_mount_image(
|
r = mountfsd_mount_image(
|
||||||
arg_image,
|
arg_image,
|
||||||
userns_fd,
|
userns_fd,
|
||||||
|
/* options= */ NULL,
|
||||||
arg_image_policy,
|
arg_image_policy,
|
||||||
&arg_verity_settings,
|
&arg_verity_settings,
|
||||||
dissect_image_flags,
|
dissect_image_flags,
|
||||||
|
|||||||
@@ -4934,6 +4934,7 @@ int verity_dissect_and_mount(
|
|||||||
r = mountfsd_mount_image(
|
r = mountfsd_mount_image(
|
||||||
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
|
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
|
||||||
userns_fd,
|
userns_fd,
|
||||||
|
options,
|
||||||
image_policy,
|
image_policy,
|
||||||
verity,
|
verity,
|
||||||
dissect_image_flags,
|
dissect_image_flags,
|
||||||
@@ -5100,6 +5101,7 @@ static void mount_image_reply_parameters_done(MountImageReplyParameters *p) {
|
|||||||
int mountfsd_mount_image_fd(
|
int mountfsd_mount_image_fd(
|
||||||
int image_fd,
|
int image_fd,
|
||||||
int userns_fd,
|
int userns_fd,
|
||||||
|
const MountOptions *options,
|
||||||
const ImagePolicy *image_policy,
|
const ImagePolicy *image_policy,
|
||||||
const VeritySettings *verity,
|
const VeritySettings *verity,
|
||||||
DissectImageFlags flags,
|
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");
|
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;
|
sd_json_variant *reply = NULL;
|
||||||
r = varlink_callbo_and_log(
|
r = varlink_callbo_and_log(
|
||||||
vl,
|
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("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_BOOLEAN("growFileSystems", FLAGS_SET(flags, DISSECT_IMAGE_GROWFS)),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)),
|
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_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_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)),
|
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(
|
int mountfsd_mount_image(
|
||||||
const char *path,
|
const char *path,
|
||||||
int userns_fd,
|
int userns_fd,
|
||||||
|
const MountOptions *options,
|
||||||
const ImagePolicy *image_policy,
|
const ImagePolicy *image_policy,
|
||||||
const VeritySettings *verity,
|
const VeritySettings *verity,
|
||||||
DissectImageFlags flags,
|
DissectImageFlags flags,
|
||||||
@@ -5289,7 +5306,7 @@ int mountfsd_mount_image(
|
|||||||
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
||||||
|
|
||||||
_cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
|
_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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@@ -268,8 +268,8 @@ static inline const char* dissected_partition_fstype(const DissectedPartition *m
|
|||||||
|
|
||||||
int get_common_dissect_directory(char **ret);
|
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_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 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_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);
|
int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,43 @@ if [ "$VERITY_SIG_SUPPORTED" -eq 1 ]; then
|
|||||||
--property ExtensionImagePolicy=root=verity+signed+absent:usr=verity+signed+absent \
|
--property ExtensionImagePolicy=root=verity+signed+absent:usr=verity+signed+absent \
|
||||||
bash -c "test -e \"/dev/mapper/${MINIMAL_IMAGE_ROOTHASH}-verity\" && test -e \"/dev/mapper/$(</tmp/app0.roothash)-verity\"")
|
bash -c "test -e \"/dev/mapper/${MINIMAL_IMAGE_ROOTHASH}-verity\" && test -e \"/dev/mapper/$(</tmp/app0.roothash)-verity\"")
|
||||||
mv /tmp/app0.roothash.p7s.bak /tmp/app0.roothash.p7s
|
mv /tmp/app0.roothash.p7s.bak /tmp/app0.roothash.p7s
|
||||||
|
|
||||||
|
# Mount options should not be allowed without elevated privileges
|
||||||
|
(! systemd-run -M testuser@ --user --pipe --wait \
|
||||||
|
--property RootImage="$MINIMAL_IMAGE.gpt" \
|
||||||
|
--property RootImageOptions="root:ro,noatime,nosuid home:ro,dev nosuid,dev" \
|
||||||
|
--property RootImageOptions="home:ro,dev nosuid,dev,%%foo" \
|
||||||
|
true)
|
||||||
|
(! systemd-run -M testuser@ --user --pipe --wait \
|
||||||
|
--property RootImage="$MINIMAL_IMAGE.raw" \
|
||||||
|
--property ExtensionImages=/tmp/app0.raw \
|
||||||
|
--property MountImages=/tmp/app0.raw:/var/tmp:noatime,nosuid \
|
||||||
|
true)
|
||||||
|
|
||||||
|
mkdir -p /etc/polkit-1/rules.d
|
||||||
|
cat >/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/$(</tmp/app0.roothash)-verity\" && mount | grep -F /var/tmp | grep -q -F nosuid"
|
||||||
|
|
||||||
|
rm -f /etc/polkit-1/rules.d/mountoptions.rules
|
||||||
|
systemctl try-reload-or-restart polkit.service
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bare squashfs without any verity or signature also should be rejected, even if we ask to trust it
|
# Bare squashfs without any verity or signature also should be rejected, even if we ask to trust it
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ at_exit() {
|
|||||||
done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
|
done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
|
||||||
|
|
||||||
rm -rf "$IMAGE_DIR"
|
rm -rf "$IMAGE_DIR"
|
||||||
|
rm -rf /etc/polkit-1/rules.d/mountoptions.rules
|
||||||
|
|
||||||
rm -f /etc/polkit-1/rules.d/sysext-unpriv.rules
|
rm -f /etc/polkit-1/rules.d/sysext-unpriv.rules
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user