]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
socket: pass socket FDs to all ExecXYZ= commands but ExecStartPre=
authorJakub Sitnicki <jakub@cloudflare.com>
Thu, 15 Feb 2024 17:02:50 +0000 (18:02 +0100)
committerMike Yuan <me@yhndnzj.com>
Tue, 26 Mar 2024 17:41:26 +0000 (01:41 +0800)
Today listen file descriptors created by socket unit don't get passed to
commands in Exec{Start,Stop}{Pre,Post}= socket options.

This prevents ExecXYZ= commands from accessing the created socket FDs to do
any kind of system setup which involves the socket but is not covered by
existing socket unit options.

One concrete example is to insert a socket FD into a BPF map capable of
holding socket references, such as BPF sockmap/sockhash [1] or
reuseport_sockarray [2]. Or, similarly, send the file descriptor with
SCM_RIGHTS to another process, which has access to a BPF map for storing
sockets.

To unblock this use case, pass ListenXYZ= file descriptors to ExecXYZ=
commands as listen FDs [4]. As an exception, ExecStartPre= command does not
inherit any file descriptors because it gets invoked before the listen FDs
are created.

This new behavior can potentially break existing configurations. Commands
invoked from ExecXYZ= might not expect to inherit file descriptors through
sd_listen_fds protocol.

To prevent breakage, add a new socket unit parameter,
PassFileDescriptorsToExec=, to control whether ExecXYZ= programs inherit
listen FDs.

[1] https://docs.kernel.org/bpf/map_sockmap.html
[2] https://lore.kernel.org/r/20180808075917.3009181-1-kafai@fb.com
[3] https://man.archlinux.org/man/socket.7#SO_INCOMING_CPU
[4] https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html

man/org.freedesktop.systemd1.xml
man/systemd.socket.xml
src/core/dbus-socket.c
src/core/load-fragment-gperf.gperf.in
src/core/socket.c
src/core/socket.h
src/shared/bus-unit-util.c
test/fuzz/fuzz-unit-file/directives-all.service

index 3a37a636b3c7a955032f4f84322214b8003114b6..4e43b4ba20dc70b9c2a485766b9548b98a6179c1 100644 (file)
@@ -4821,6 +4821,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PassCredentials = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PassFileDescriptorsToExec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PassSecurity = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PassPacketInfo = ...;
@@ -5488,6 +5490,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property PassCredentials is not documented!-->
 
+    <!--property PassFileDescriptorsToExec is not documented!-->
+
     <!--property PassSecurity is not documented!-->
 
     <!--property PassPacketInfo is not documented!-->
@@ -6100,6 +6104,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="PassCredentials"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PassFileDescriptorsToExec"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="PassSecurity"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="PassPacketInfo"/>
@@ -12061,8 +12067,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
       <para><varname>EffectiveMemoryHigh</varname>,
       <varname>EffectiveMemoryMax</varname>,
-      <varname>EffectiveTasksMax</varname>, and
-      <varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
+      <varname>EffectiveTasksMax</varname>,
+      <varname>MemoryZSwapWriteback</varname>, and
+      <varname>PassFileDescriptorsToExec</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Mount Unit Objects</title>
index c7166e4f643bd5b082d673bd8b02771f88fa34f5..50871f7a749efb66a521e68000ca479cd10cfa8c 100644 (file)
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>PassFileDescriptorsToExec=</varname></term>
+
+        <listitem><para>Takes a boolean argument. Defaults to off. If enabled, file descriptors created by
+        the socket unit are passed to <varname>ExecStartPost=</varname>, <varname>ExecStopPre=</varname>, and
+        <varname>ExecStopPost=</varname> commands from the socket unit. The passed file descriptors can be
+        accessed with
+        <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> as
+        if the commands were invoked from the associated service units.  Note that
+        <varname>ExecStartPre=</varname> command cannot access socket file descriptors.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
     </variablelist>
 
     <xi:include href="systemd.service.xml" xpointer="shared-unit-options" />
index e77e9e5ccd27b5392b7c6707036283cc43f1679a..03c5b4ad2ae5edc188c88854469a0a0ab70d8bd8 100644 (file)
@@ -86,6 +86,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
         SD_BUS_PROPERTY("Transparent", "b", bus_property_get_bool, offsetof(Socket, transparent), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("PassFileDescriptorsToExec", "b", bus_property_get_bool, offsetof(Socket, pass_fds_to_exec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Timestamping", "s", property_get_timestamping, offsetof(Socket, timestamping), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -190,6 +191,9 @@ static int bus_socket_set_transient_property(
         if (streq(name, "PassCredentials"))
                 return bus_set_transient_bool(u, name, &s->pass_cred, message, flags, error);
 
+        if (streq(name, "PassFileDescriptorsToExec"))
+                return bus_set_transient_bool(u, name, &s->pass_fds_to_exec, message, flags, error);
+
         if (streq(name, "PassSecurity"))
                 return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error);
 
index c5ea99726a4d55587dc976baa4d5da28db1da14d..27aa27b55a9fbcad057e6c306095e64e936e76e4 100644 (file)
@@ -500,6 +500,7 @@ Socket.FreeBind,                         config_parse_bool,
 Socket.Transparent,                      config_parse_bool,                           0,                                  offsetof(Socket, transparent)
 Socket.Broadcast,                        config_parse_bool,                           0,                                  offsetof(Socket, broadcast)
 Socket.PassCredentials,                  config_parse_bool,                           0,                                  offsetof(Socket, pass_cred)
+Socket.PassFileDescriptorsToExec,        config_parse_bool,                           0,                                  offsetof(Socket, pass_fds_to_exec)
 Socket.PassSecurity,                     config_parse_bool,                           0,                                  offsetof(Socket, pass_sec)
 Socket.PassPacketInfo,                   config_parse_bool,                           0,                                  offsetof(Socket, pass_pktinfo)
 Socket.Timestamping,                     config_parse_socket_timestamping,            0,                                  offsetof(Socket, timestamping)
index 45656cbda77943db36676f5dd20bf403406d82ad..5dbbd5a1d255926942e022cb39eb0b0921a120ca 100644 (file)
@@ -590,6 +590,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sTransparent: %s\n"
                 "%sBroadcast: %s\n"
                 "%sPassCredentials: %s\n"
+                "%sPassFileDescriptorsToExec: %s\n"
                 "%sPassSecurity: %s\n"
                 "%sPassPacketInfo: %s\n"
                 "%sTCPCongestion: %s\n"
@@ -610,6 +611,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, yes_no(s->transparent),
                 prefix, yes_no(s->broadcast),
                 prefix, yes_no(s->pass_cred),
+                prefix, yes_no(s->pass_fds_to_exec),
                 prefix, yes_no(s->pass_sec),
                 prefix, yes_no(s->pass_pktinfo),
                 prefix, strna(s->tcp_congestion),
@@ -1921,6 +1923,26 @@ static int socket_spawn(Socket *s, ExecCommand *c, PidRef *ret_pid) {
         if (r < 0)
                 return r;
 
+        /* Note that ExecStartPre= command doesn't inherit any FDs. It runs before we open listen FDs. */
+        if (s->pass_fds_to_exec) {
+                _cleanup_strv_free_ char **fd_names = NULL;
+                _cleanup_free_ int *fds = NULL;
+                int n_fds;
+
+                n_fds = socket_collect_fds(s, &fds);
+                if (n_fds < 0)
+                        return n_fds;
+
+                r = strv_extend_n(&fd_names, socket_fdname(s), n_fds);
+                if (r < 0)
+                        return r;
+
+                exec_params.flags |= EXEC_PASS_FDS;
+                exec_params.fds = TAKE_PTR(fds);
+                exec_params.fd_names = TAKE_PTR(fd_names);
+                exec_params.n_socket_fds = n_fds;
+        }
+
         r = exec_spawn(UNIT(s),
                        c,
                        &s->exec_context,
index 973a697f86154ed10aec7a6b1562d9a536674cc0..5e3929c5fa78370b4430bd4442e4b37caba77c0d 100644 (file)
@@ -129,6 +129,7 @@ struct Socket {
         bool transparent;
         bool broadcast;
         bool pass_cred;
+        bool pass_fds_to_exec;
         bool pass_sec;
         bool pass_pktinfo;
         SocketTimestamping timestamping;
index 2fcfb1d3b96c9affc9f93dd4176c58f0e8713be5..19cebb0cfe1c935d7d115cf1184f04189d7d264f 100644 (file)
@@ -2450,6 +2450,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
                               "Transparent",
                               "Broadcast",
                               "PassCredentials",
+                              "PassFileDescriptorsToExec",
                               "PassSecurity",
                               "PassPacketInfo",
                               "ReusePort",
index b05b0a49731675aee3385c65075d13c09aec365b..670e589babe7a22665e090071f4d280dfa0845ad 100644 (file)
@@ -185,6 +185,7 @@ PAMName=
 PIDFile=
 PartOf=
 PassCredentials=
+PassFileDescriptorsToExec=
 PassSecurity=
 PassPacketInfo=
 PathChanged=