image-policy: add support for restricting to specific filesystems

This commit is contained in:
Luca Boccassi
2025-12-19 09:17:41 +00:00
parent bc3f700a4a
commit d0cb79b8a9
5 changed files with 110 additions and 9 deletions
+50 -5
View File
@@ -26,11 +26,11 @@
<para>In systemd, whenever a disk image (DDI) implementing the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">UAPI.2
Discoverable Partitions Specification</ulink> is activated, a policy may be specified controlling which
partitions to mount and what kind of cryptographic protection to require. Such a disk image dissection
policy is a string that contains per-partition-type rules, separated by colons
(<literal>:</literal>). The individual rules consist of a partition identifier, an equal sign
(<literal>=</literal>), and one or more flags which may be set per partition. If multiple flags are
specified per partition they are separated by a plus sign (<literal>+</literal>).</para>
partitions to mount and what kind of cryptographic protection to require, and which type of filesystem
is allowed. Such a disk image dissection policy is a string that contains per-partition-type rules,
separated by colons (<literal>:</literal>). The individual rules consist of a partition identifier, an
equal sign (<literal>=</literal>), and one or more flags which may be set per partition. If multiple
flags are specified per partition they are separated by a plus sign (<literal>+</literal>).</para>
<para>The partition identifiers currently defined are: <option>root</option>, <option>usr</option>,
<option>home</option>, <option>srv</option>, <option>esp</option>, <option>xbootldr</option>,
@@ -71,6 +71,46 @@
image.</para></listitem>
</itemizedlist>
<para>The following filesystem policy flags are defined that dictate which filesystem types are allowed
for which partition:</para>
<table>
<title>
Filesystems types supported by the policy
</title>
<tgroup cols='1'>
<thead>
<row>
<entry>Filesystem flag</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>btrfs</literal></entry>
</row>
<row>
<entry><literal>erofs</literal></entry>
</row>
<row>
<entry><literal>ext4</literal></entry>
</row>
<row>
<entry><literal>f2fs</literal></entry>
</row>
<row>
<entry><literal>squashfs</literal></entry>
</row>
<row>
<entry><literal>vfat</literal></entry>
</row>
<row>
<entry><literal>xfs</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>By setting a combination of the flags above, alternatives can be declared. For example the
combination <literal>unused+absent</literal> means: the partition may exist (in which case it shall not
be used) or may be absent. The combination of
@@ -175,6 +215,11 @@
does not have to be, and ignores swap partitions, and uses all other partitions if they are available, possibly with encryption.</para>
<programlisting>root=unprotected+encrypted:swap=absent+unused:=unprotected+encrypted+absent</programlisting>
<para>The following image policy string dictates a single root partition that can only be erofs or squashfs,
and ignores swap partitions, and uses all other partitions if they are available, possibly with encryption.</para>
<programlisting>root=erofs+squashfs:swap=absent+unused:=unprotected+encrypted+absent</programlisting>
</refsect1>
<refsect1>
+10 -2
View File
@@ -9,7 +9,7 @@
#include "string-util.h"
static int table_add_designator_line(Table *table, PartitionDesignator d, PartitionPolicyFlags f) {
_cleanup_free_ char *q = NULL;
_cleanup_free_ char *q = NULL, *t = NULL;
const char *color;
int r;
@@ -19,6 +19,9 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
if (partition_policy_flags_to_string(f & _PARTITION_POLICY_USE_MASK, /* simplify= */ true, &q) < 0)
return log_oom();
if (partition_policy_flags_to_string(f & _PARTITION_POLICY_FSTYPE_MASK, /* simplify= */ true, &t) < 0)
return log_oom();
color = (f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE ? ansi_grey() :
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
@@ -72,6 +75,11 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
if (r < 0)
return table_log_add_error(r);
r = table_add_many(table,
TABLE_STRING, isempty(t) ? "-" : t);
if (r < 0)
return table_log_add_error(r);
return 0;
}
@@ -128,7 +136,7 @@ int verb_image_policy(int argc, char *argv[], void *userdata) {
ansi_highlight(), as_string_simplified, ansi_normal(),
ansi_grey(), as_string, ansi_normal());
table = table_new("partition", "mode", "read-only", "growfs");
table = table_new("partition", "mode", "read-only", "growfs", "fstype");
if (!table)
return log_oom();
+33 -2
View File
@@ -207,6 +207,20 @@ static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
return PARTITION_POLICY_GROWFS_ON;
if (streq(s, "growfs-off"))
return PARTITION_POLICY_GROWFS_OFF;
if (streq(s, "btrfs"))
return PARTITION_POLICY_BTRFS;
if (streq(s, "erofs"))
return PARTITION_POLICY_EROFS;
if (streq(s, "ext4"))
return PARTITION_POLICY_EXT4;
if (streq(s, "f2fs"))
return PARTITION_POLICY_F2FS;
if (streq(s, "squashfs"))
return PARTITION_POLICY_SQUASHFS;
if (streq(s, "vfat"))
return PARTITION_POLICY_VFAT;
if (streq(s, "xfs"))
return PARTITION_POLICY_XFS;
return _PARTITION_POLICY_FLAGS_INVALID;
}
@@ -389,7 +403,7 @@ int image_policy_from_string(const char *s, bool graceful, ImagePolicy **ret) {
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret) {
_cleanup_free_ char *buf = NULL;
const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK + 1) + 1]; /* one string per known flag at most */
const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK + _PARTITION_POLICY_FSTYPE_MASK + 1) + 1]; /* one string per known flag at most */
size_t m = 0;
assert(ret);
@@ -449,6 +463,23 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
l[m++] = "growfs-on";
}
/* These flags must translate to the literal fstype name of each filesystem, as accepted by
* `mount -t`. */
if (flags & PARTITION_POLICY_BTRFS)
l[m++] = "btrfs";
if (flags & PARTITION_POLICY_EROFS)
l[m++] = "erofs";
if (flags & PARTITION_POLICY_EXT4)
l[m++] = "ext4";
if (flags & PARTITION_POLICY_F2FS)
l[m++] = "f2fs";
if (flags & PARTITION_POLICY_SQUASHFS)
l[m++] = "squashfs";
if (flags & PARTITION_POLICY_VFAT)
l[m++] = "vfat";
if (flags & PARTITION_POLICY_XFS)
l[m++] = "xfs";
if (m == 0)
buf = strdup("-");
else {
@@ -461,7 +492,7 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
return -ENOMEM;
*ret = TAKE_PTR(buf);
return 0;
return (int) m;
}
static bool partition_policy_flags_extended_equal(PartitionPolicyFlags a, PartitionPolicyFlags b) {
+13
View File
@@ -36,6 +36,19 @@ typedef enum PartitionPolicyFlags {
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
/* Policies can impose filesystem type requirements on images. These flags translate to the literal
* fstype name of each filesystem. */
PARTITION_POLICY_BTRFS = 1 << 11,
PARTITION_POLICY_EROFS = 1 << 12,
PARTITION_POLICY_EXT4 = 1 << 13,
PARTITION_POLICY_F2FS = 1 << 14,
PARTITION_POLICY_SQUASHFS = 1 << 15,
PARTITION_POLICY_VFAT = 1 << 16,
PARTITION_POLICY_XFS = 1 << 17,
_PARTITION_POLICY_FSTYPE_MASK = PARTITION_POLICY_BTRFS|PARTITION_POLICY_EROFS|PARTITION_POLICY_EXT4|
PARTITION_POLICY_F2FS|PARTITION_POLICY_SQUASHFS|
PARTITION_POLICY_VFAT|PARTITION_POLICY_XFS,
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
} PartitionPolicyFlags;
+4
View File
@@ -92,6 +92,10 @@ TEST_RET(test_image_policy_to_string) {
test_policy_string("swap=open:root=signed+read-only-on+growfs-off:=absent");
test_policy_string("=-");
test_policy_string("=");
test_policy_string("root=ext4+squashfs+verity");
test_policy_string("usr=encrypted+erofs+read-only-off");
test_policy_string("home=unprotected+btrfs");
test_policy_string("=vfat+erofs");
test_policy_equiv("", image_policy_equiv_ignore);
test_policy_equiv("-", image_policy_equiv_ignore);