Merge pull request #31000 from flatcar-hub/krnowak/mutable-overlays

systemd-sysext: Implement optional mutability for extensions
This commit is contained in:
Luca Boccassi
2024-02-26 16:17:11 +00:00
committed by GitHub
6 changed files with 1560 additions and 94 deletions

View File

@@ -69,8 +69,10 @@
<filename>/var/</filename> included in a system extension image will <emphasis>not</emphasis> appear in
the respective hierarchies after activation.</para>
<para>System extension images are strictly read-only, and the host <filename>/usr/</filename> and
<filename>/opt/</filename> hierarchies become read-only too while they are activated.</para>
<para>System extension images are strictly read-only by default. On mutable host file systems,
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies become read-only while extensions
are merged, unless mutability is enabled. Mutability may be enabled via the <option>--mutable=</option>
option; see "Mutability" below for more information.</para>
<para>System extensions are supposed to be purely additive, i.e. they are supposed to include only files
that do not exist in the underlying basic OS image. However, the underlying mechanism (overlayfs) also
@@ -158,6 +160,11 @@
same as sysext images. The merged hierarchy will be mounted with <literal>nosuid</literal> and
(if not disabled via <option>--noexec=false</option>) <literal>noexec</literal>.</para>
<para>Just like sysexts, confexts are strictly read-only by default. Merging confexts on mutable host
file systems will result in <filename>/etc/</filename> becoming read-only. As with sysexts, mutability
can be enabled via the <option>--mutable=</option> option. Refer to "Mutability" below for more
information.</para>
<para>Confexts are looked for in the directories <filename>/run/confexts/</filename>,
<filename>/var/lib/confexts/</filename>, <filename>/usr/lib/confexts/</filename> and
<filename>/usr/local/lib/confexts/</filename>. The first listed directory is not suitable for
@@ -205,6 +212,55 @@
to tie the most frequently configured options to runtime updateable flags that can be changed without a
system reboot. This will help reduce servicing times when there is a need for changing the OS configuration.</para></refsect1>
<refsect1>
<title>Mutability</title>
<para>By default, merging system extensions on mutable host file systems will render <filename>/usr/</filename>
and <filename>/opt/</filename> hierarchies read-only. Merging configuration extensions will have the same
effect on <filename>/etc/</filename>. Mutable mode allows writes to these locations when extensions are
merged.</para>
<para>The following modes are supported:
<orderedlist>
<listitem><para><option>disabled</option>: Force immutable mode even if write routing
directories exist below <filename>/var/lib/extensions.mutable/</filename>.
This is the default.</para></listitem>
<listitem><para><option>auto</option>: Automatic mode. Mutability is disabled by default
and only enabled if a corresponding write routing directory exists below
<filename>/var/lib/extensions.mutable/</filename>.</para></listitem>
<listitem><para><option>enabled</option>: Force mutable mode and automatically create write routing
directories below <filename>/var/lib/extensions.mutable/</filename> when required.</para></listitem>
<listitem><para><option>import</option>: Force immutable mode like <option>disabled</option> above, but
merge the contents of directories below <filename>/var/lib/extensions.mutable/</filename> into the host
file system.</para></listitem>
</orderedlist>
See "Options" below on specifying modes using the <option>--mutable=</option> command line option.</para>
<para>Mutable mode routes writes to subdirectories in <filename>/var/lib/extensions.mutable/</filename>.
<simplelist type="horiz">
<member>Writes to <filename>/usr/</filename> are directed to <filename>/var/lib/extensions.mutable/usr/</filename></member>,
<member>writes to <filename>/opt/</filename> are directed to <filename>/var/lib/extensions.mutable/opt/</filename>, and</member>
<member>writes to <filename>/etc/</filename> land in <filename>/var/lib/extensions.mutable/etc/</filename>.</member>
</simplelist></para>
<para>If <filename>usr/</filename>, <filename>opt/</filename>, or <filename>etc/</filename>
in <filename>/var/lib/extensions.mutable/</filename> are symlinks, then writes are directed to the
symlinks' targets.
Consequently, to retain mutability of a host file system, create symlinks
<simplelist type="horiz">
<member><filename>/var/lib/extensions.mutable/etc/</filename><filename>/etc/</filename></member>
<member><filename>/var/lib/extensions.mutable/usr/</filename><filename>/usr/</filename></member>
<member><filename>/var/lib/extensions.mutable/opt/</filename><filename>/opt/</filename></member>
</simplelist>
to route writes back to the original base directory hierarchy.</para>
<para> Alternatively, a temporary file system may be mounted to
<filename>/var/lib/extensions.mutable/</filename>, or symlinks in
<filename>/var/lib/extensions.mutable/</filename> may point to sub-directories on a temporary
file system (e.g. below <filename>/tmp/</filename>) to only allow ephemeral changes.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</refsect1>
<refsect1>
<title>Commands</title>
@@ -313,6 +369,45 @@
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--mutable=</option><replaceable>BOOL</replaceable>|<replaceable>auto</replaceable>|<replaceable>import</replaceable></term>
<listitem><para>Set mutable mode.</para>
<variablelist>
<varlistentry>
<term><option>no</option></term>
<listitem><para>force immutable mode even with write routing directories present.
This is the default.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>auto</option></term>
<listitem><para>enable mutable mode individually for <filename>/usr/</filename>,
<filename>/opt/</filename>, and <filename>/etc/</filename> if write routing sub-directories
or symlinks are present in <filename>/var/lib/extensions.mutable/</filename>; disable otherwise.
See "Mutability" above for more information on write routing.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>yes</option></term>
<listitem><para>force mutable mode. Write routing directories will be created in
<filename>/var/lib/extensions.mutable/</filename> if not present.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>import</option></term>
<listitem><para>immutable mode, but with contents of write routing directories in
<filename>/var/lib/extensions.mutable/</filename> also merged into the host file system.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
</variablelist>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--noexec=</option><replaceable>BOOL</replaceable></term>

View File

@@ -453,6 +453,16 @@ int bind_remount_one_with_mountinfo(
return 0;
}
int bind_remount_one(const char *path, unsigned long new_flags, unsigned long flags_mask) {
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
if (!proc_self_mountinfo)
return log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
return bind_remount_one_with_mountinfo(path, new_flags, flags_mask, proc_self_mountinfo);
}
static int mount_switch_root_pivot(int fd_newroot, const char *path) {
assert(fd_newroot >= 0);
assert(path);

View File

@@ -26,6 +26,7 @@ static inline int bind_remount_recursive(const char *prefix, unsigned long new_f
}
int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
int bind_remount_one(const char *path, unsigned long new_flags, unsigned long flags_mask);
int mount_switch_root_full(const char *path, unsigned long mount_propagation_flag, bool force_ms_move);
static inline int mount_switch_root(const char *path, unsigned long mount_propagation_flag) {

File diff suppressed because it is too large Load Diff

View File

@@ -213,6 +213,25 @@ TEST(bind_remount_one) {
_exit(EXIT_SUCCESS);
}
assert_se(wait_for_terminate_and_check("test-remount-one-with-mountinfo", pid, WAIT_LOG) == EXIT_SUCCESS);
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
/* child */
assert_se(detach_mount_namespace() >= 0);
assert_se(bind_remount_one("/run", MS_RDONLY, MS_RDONLY) >= 0);
assert_se(bind_remount_one("/run", MS_NOEXEC, MS_RDONLY|MS_NOEXEC) >= 0);
assert_se(bind_remount_one("/proc/idontexist", MS_RDONLY, MS_RDONLY) == -ENOENT);
assert_se(bind_remount_one("/proc/self", MS_RDONLY, MS_RDONLY) == -EINVAL);
assert_se(bind_remount_one("/", MS_RDONLY, MS_RDONLY) >= 0);
_exit(EXIT_SUCCESS);
}
assert_se(wait_for_terminate_and_check("test-remount-one", pid, WAIT_LOG) == EXIT_SUCCESS);
}

File diff suppressed because it is too large Load Diff