core: use socket cookie for naming per-connection service instances + set $SO_COOKIE env var to it (#37469)

This commit is contained in:
Luca Boccassi
2025-05-15 11:11:21 +01:00
committed by GitHub
9 changed files with 77 additions and 29 deletions
-3
View File
@@ -125,9 +125,6 @@ Deprecations and removals:
Features:
* in pid1, name Accept=yes service instances after the socket cookie. Also, set
$SO_COOKIE env var or so for executed services.
* loginctl: show argv[] of "leader" process in tabular list-sessions output
* loginctl: show "service identifier" in tabular list-sessions output, to make
+12 -1
View File
@@ -4409,7 +4409,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
in case of an unnamed socket. <varname>$REMOTE_PORT</varname> is not set for <constant>AF_UNIX</constant>
sockets.</para>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SO_COOKIE</varname></term>
<listitem><para>If this is a unit started via per-connection socket activation (i.e. via a socket
unit with <varname>Accept=yes</varname>), this environment variable contains the Linux socket
cookie, formatted as decimal integer. The socket cookie can otherwise be acquired via <citerefentry
project='man-pages'><refentrytitle>getsockopt</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
+9 -4
View File
@@ -425,14 +425,19 @@
above for a more detailed discussion of the naming rules of triggered services.</para>
<para>For IPv4 and IPv6 connections, the <varname>$REMOTE_ADDR</varname> environment variable will
contain the remote IP address, and <varname>$REMOTE_PORT</varname> will contain the remote port. This
is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
protocol.</para>
contain the remote IP address, and <varname>$REMOTE_PORT</varname> will contain the remote port
number. These two variables correspond to those defined by the CGI interface for web services (see
<ulink url="https://datatracker.ietf.org/doc/html/rfc3875">RFC 3875</ulink>).</para>
<para>For <constant>AF_UNIX</constant> socket connections, the <varname>$REMOTE_ADDR</varname>
environment variable will contain either the remote socket's file system path starting with a slash
(<literal>/</literal>) or its address in the abstract namespace starting with an at symbol
(<literal>@</literal>). If the socket is unnamed, <varname>$REMOTE_ADDR</varname> will not be set.</para>
(<literal>@</literal>). If the socket is unnamed, <varname>$REMOTE_ADDR</varname> will not be
set.</para>
<para>The <varname>$SO_COOKIE</varname> environment variable is set to the Linux socket cookie,
formatted as decimal integer. The socket cookie can otherwise be acquired via <citerefentry
project='man-pages'><refentrytitle>getsockopt</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<para>It is recommended to set <varname>CollectMode=inactive-or-failed</varname> for service
instances activated via <varname>Accept=yes</varname>, to ensure that failed connection services are
+15
View File
@@ -1959,3 +1959,18 @@ finalize:
return 0;
}
int socket_get_cookie(int fd, uint64_t *ret) {
assert(fd >= 0);
uint64_t cookie = 0;
socklen_t cookie_len = sizeof(cookie);
if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0)
return -errno;
assert(cookie_len == sizeof(cookie));
if (ret)
*ret = cookie;
return 0;
}
+2
View File
@@ -354,3 +354,5 @@ int socket_address_equal_unix(const char *a, const char *b);
int vsock_get_local_cid(unsigned *ret);
int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups);
int socket_get_cookie(int fd, uint64_t *ret);
+9 -1
View File
@@ -1749,7 +1749,7 @@ static int service_spawn_internal(
if (r < 0)
return r;
our_env = new0(char*, 15);
our_env = new0(char*, 16);
if (!our_env)
return -ENOMEM;
@@ -1821,6 +1821,14 @@ static int service_spawn_internal(
our_env[n_env++] = t;
}
}
uint64_t cookie;
if (socket_get_cookie(s->socket_fd, &cookie) >= 0) {
char *t;
if (asprintf(&t, "SO_COOKIE=%" PRIu64, cookie) < 0)
return -ENOMEM;
our_env[n_env++] = t;
}
}
Service *env_source = NULL;
+25 -11
View File
@@ -830,7 +830,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
cgroup_context_dump(UNIT(s), f, prefix);
}
static int instance_from_socket(int fd, unsigned nr, char **ret) {
static int instance_from_socket(
int fd,
unsigned nr,
char **ret) {
union sockaddr_union local, remote;
socklen_t l;
int r;
@@ -846,6 +850,11 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
if (getpeername(fd, &remote.sa, &l) < 0)
return -errno;
uint64_t cookie;
r = socket_get_cookie(fd, &cookie);
if (r < 0)
return r;
char *s;
switch (local.sa.sa_family) {
@@ -856,8 +865,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
b = be32toh(remote.in.sin_addr.s_addr);
if (asprintf(&s,
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
"%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
cookie,
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
be16toh(local.in.sin_port),
b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
@@ -879,8 +889,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
*b = remote.in6.sin6_addr.s6_addr+12;
if (asprintf(&s,
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
"%u-%" PRIu64 "-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
cookie,
a[0], a[1], a[2], a[3],
be16toh(local.in6.sin6_port),
b[0], b[1], b[2], b[3],
@@ -888,8 +899,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
return -ENOMEM;
} else {
if (asprintf(&s,
"%u-%s:%u-%s:%u",
"%u-%" PRIu64 "-%s:%u-%s:%u",
nr,
cookie,
IN6_ADDR_TO_STRING(&local.in6.sin6_addr),
be16toh(local.in6.sin6_port),
IN6_ADDR_TO_STRING(&remote.in6.sin6_addr),
@@ -909,19 +921,20 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
uint64_t pidfd_id;
if (pidfd >= 0 && pidfd_get_inode_id(pidfd, &pidfd_id) >= 0)
r = asprintf(&s, "%u-" PID_FMT "_%" PRIu64 "-" UID_FMT,
nr, ucred.pid, pidfd_id, ucred.uid);
r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "_%" PRIu64 "-" UID_FMT,
nr, cookie, ucred.pid, pidfd_id, ucred.uid);
else
r = asprintf(&s, "%u-" PID_FMT "-" UID_FMT,
nr, ucred.pid, ucred.uid);
r = asprintf(&s, "%u-%" PRIu64 "-" PID_FMT "-" UID_FMT,
nr, cookie, ucred.pid, ucred.uid);
if (r < 0)
return -ENOMEM;
} else if (r == -ENODATA) {
/* This handles the case where somebody is connecting from another pid/uid namespace
* (e.g. from outside of our container). */
if (asprintf(&s,
"%u-unknown",
nr) < 0)
"%u-%" PRIu64 "-unknown",
nr,
cookie) < 0)
return -ENOMEM;
} else
return r;
@@ -931,8 +944,9 @@ static int instance_from_socket(int fd, unsigned nr, char **ret) {
case AF_VSOCK:
if (asprintf(&s,
"%u-%u:%u-%u:%u",
"%u-%" PRIu64 "-%u:%u-%u:%u",
nr,
cookie,
local.vm.svm_cid, local.vm.svm_port,
remote.vm.svm_cid, remote.vm.svm_port) < 0)
return -ENOMEM;
+1 -3
View File
@@ -717,9 +717,7 @@ TEST(sock_diag_unix) {
ASSERT_OK_ERRNO(fstat(unix_fd, &st));
uint64_t cookie;
socklen_t cookie_len = sizeof(cookie);
ASSERT_OK_ERRNO(getsockopt(unix_fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len));
ASSERT_EQ(cookie_len, sizeof(cookie));
ASSERT_OK(socket_get_cookie(unix_fd, &cookie));
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
ASSERT_OK(sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN));
+4 -6
View File
@@ -504,12 +504,10 @@ int af_unix_get_qlen(int fd, uint32_t *ret) {
if (r < 0)
return r;
uint64_t cookie = 0;
socklen_t cookie_len = sizeof(cookie);
if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0)
return -errno;
assert(cookie_len == sizeof(cookie));
uint64_t cookie;
r = socket_get_cookie(fd, &cookie);
if (r < 0)
return r;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
r = sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN);