diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 2163047896..aff43217e1 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2693,6 +2693,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b PrivateMounts = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b PrivateIPC = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectHome = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectSystem = '...';
@@ -2777,6 +2779,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s NetworkNamespacePath = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s IPCNamespacePath = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i KillSignal = ...;
@@ -3194,6 +3198,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -3278,6 +3284,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -3772,6 +3780,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -3856,6 +3866,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -4454,6 +4466,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b PrivateMounts = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b PrivateIPC = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectHome = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectSystem = '...';
@@ -4538,6 +4552,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s NetworkNamespacePath = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s IPCNamespacePath = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i KillSignal = ...;
@@ -4983,6 +4999,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -5067,6 +5085,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -5559,6 +5579,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -5643,6 +5665,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -6143,6 +6167,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b PrivateMounts = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b PrivateIPC = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectHome = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectSystem = '...';
@@ -6227,6 +6253,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s NetworkNamespacePath = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s IPCNamespacePath = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i KillSignal = ...;
@@ -6600,6 +6628,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -6684,6 +6714,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -7094,6 +7126,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -7178,6 +7212,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -7799,6 +7835,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b PrivateMounts = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b PrivateIPC = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectHome = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s ProtectSystem = '...';
@@ -7883,6 +7921,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s NetworkNamespacePath = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s IPCNamespacePath = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i KillSignal = ...;
@@ -8242,6 +8282,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
@@ -8326,6 +8368,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
@@ -8722,6 +8766,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
@@ -8806,6 +8852,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 30e64224c3..51f873f8cd 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1603,6 +1603,53 @@ BindReadOnlyPaths=/var/lib/systemd
+
+ PrivateIPC=
+
+ Takes a boolean argument. If true, sets up a new IPC namespace for the executed processes.
+ Each IPC namespace has its own set of System V IPC identifiers and its own POSIX message queue file system.
+ This is useful to avoid name clash of IPC identifiers. Defaults to false. It is possible to run two or
+ more units within the same private IPC namespace by using the JoinsNamespaceOf= directive,
+ see systemd.unit5 for
+ details.
+
+ Note that IPC namespacing does not have an effect on
+ AF_UNIX sockets, which are the most common
+ form of IPC used on Linux. Instead, AF_UNIX
+ sockets in the file system are subject to mount namespacing, and
+ those in the abstract namespace are subject to network namespacing.
+ IPC namespacing only has an effect on SysV IPC (which is mostly
+ legacy) as well as POSIX message queues (for which
+ AF_UNIX/SOCK_SEQPACKET
+ sockets are typically a better replacement). IPC namespacing also
+ has no effect on POSIX shared memory (which is subject to mount
+ namespacing) either. See
+ ipc_namespaces7 for
+ the details.
+
+ Note that the implementation of this setting might be impossible (for example if IPC namespaces are
+ not available), and the unit should be written in a way that does not solely rely on this setting for
+ security.
+
+
+
+
+
+ IPCNamespacePath=
+
+ Takes an absolute file system path refererring to a Linux IPC namespace
+ pseudo-file (i.e. a file like /proc/$PID/ns/ipc or a bind mount or symlink to
+ one). When set the invoked processes are added to the network namespace referenced by that path. The
+ path has to point to a valid namespace file at the moment the processes are forked off. If this
+ option is used PrivateIPC= has no effect. If this option is used together with
+ JoinsNamespaceOf= then it only has an effect if this unit is started before any of
+ the listed units that have PrivateIPC= or
+ IPCNamespacePath= configured, as otherwise the network namespace of those
+ units is reused.
+
+
+
+
PrivateUsers=
@@ -3585,7 +3632,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
226
EXIT_NAMESPACE
- Failed to set up mount namespacing. See ReadOnlyPaths= and related settings above.
+ Failed to set up mount, UTS, or IPC namespacing. See ReadOnlyPaths=, ProtectHostname=, PrivateIPC=, and related settings above.
227
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 6a9d4dc486..20e52c5664 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -799,14 +799,16 @@
For units that start processes (such as service units), lists one or more other units
whose network and/or temporary file namespace to join. This only applies to unit types which support
- the PrivateNetwork=, NetworkNamespacePath= and
+ the PrivateNetwork=, NetworkNamespacePath=,
+ PrivateIPC=, IPCNamespacePath=, and
PrivateTmp= directives (see
systemd.exec5 for
details). If a unit that has this setting set is started, its processes will see the same
- /tmp/, /var/tmp/ and network namespace as one listed unit
- that is started. If multiple listed units are already started, it is not defined which namespace is
- joined. Note that this setting only has an effect if
- PrivateNetwork=/NetworkNamespacePath= and/or
+ /tmp/, /var/tmp/, IPC namespace and network namespace as
+ one listed unit that is started. If multiple listed units are already started, it is not defined
+ which namespace is joined. Note that this setting only has an effect if
+ PrivateNetwork=/NetworkNamespacePath=,
+ PrivateIPC=/IPCNamespacePath= and/or
PrivateTmp= is enabled for both the unit that joins the namespace and the unit
whose namespace is joined.
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index a4817ca6de..f474a02b0e 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -1162,6 +1162,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateMounts", "b", bus_property_get_bool, offsetof(ExecContext, private_mounts), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PrivateIPC", "b", bus_property_get_bool, offsetof(ExecContext, private_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectHome", "s", property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectSystem", "s", property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1204,6 +1205,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("ProcSubset", "s", property_get_proc_subset, offsetof(ExecContext, proc_subset), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectHostname", "b", bus_property_get_bool, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
/* Obsolete/redundant properties: */
SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
@@ -1753,6 +1755,9 @@ int bus_exec_context_set_transient_property(
if (streq(name, "PrivateNetwork"))
return bus_set_transient_bool(u, name, &c->private_network, message, flags, error);
+ if (streq(name, "PrivateIPC"))
+ return bus_set_transient_bool(u, name, &c->private_ipc, message, flags, error);
+
if (streq(name, "PrivateUsers"))
return bus_set_transient_bool(u, name, &c->private_users, message, flags, error);
@@ -1873,6 +1878,9 @@ int bus_exec_context_set_transient_property(
if (streq(name, "NetworkNamespacePath"))
return bus_set_transient_path(u, name, &c->network_namespace_path, message, flags, error);
+ if (streq(name, "IPCNamespacePath"))
+ return bus_set_transient_path(u, name, &c->ipc_namespace_path, message, flags, error);
+
if (streq(name, "SupplementaryGroups")) {
_cleanup_strv_free_ char **l = NULL;
char **p;
diff --git a/src/core/execute.c b/src/core/execute.c
index 0a2d2e1158..684b5a233e 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -3476,8 +3476,10 @@ static int close_remaining_fds(
n_dont_close += n_fds;
}
- if (runtime)
+ if (runtime) {
append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket);
+ append_socket_pair(dont_close, &n_dont_close, runtime->ipcns_storage_socket);
+ }
if (dcreds) {
if (dcreds->user)
@@ -3925,6 +3927,14 @@ static int exec_child(
}
}
+ if (context->ipc_namespace_path && runtime && runtime->ipcns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(runtime->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC);
+ if (r < 0) {
+ *exit_status = EXIT_NAMESPACE;
+ return log_unit_error_errno(unit, r, "Failed to open IPC namespace path %s: %m", context->ipc_namespace_path);
+ }
+ }
+
r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
@@ -4211,6 +4221,25 @@ static int exec_child(
log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring.");
}
+ if ((context->private_ipc || context->ipc_namespace_path) && runtime && runtime->ipcns_storage_socket[0] >= 0) {
+
+ if (ns_type_supported(NAMESPACE_IPC)) {
+ r = setup_shareable_ns(runtime->ipcns_storage_socket, CLONE_NEWIPC);
+ if (r == -EPERM)
+ log_unit_warning_errno(unit, r,
+ "PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m");
+ else if (r < 0) {
+ *exit_status = EXIT_NAMESPACE;
+ return log_unit_error_errno(unit, r, "Failed to set up IPC namespacing: %m");
+ }
+ } else if (context->ipc_namespace_path) {
+ *exit_status = EXIT_NAMESPACE;
+ return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "IPCNamespacePath= is not supported, refusing.");
+ } else
+ log_unit_warning(unit, "PrivateIPC=yes is configured, but the kernel does not support IPC namespaces, ignoring.");
+ }
+
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
@@ -4314,7 +4343,7 @@ static int exec_child(
#endif
/* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are
- * more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd
+ * more aggressive this time since socket_fd and the netns and ipcns fds we don't need anymore. We do keep the exec_fd
* however if we have it as we want to keep it open until the final execve(). */
r = close_all_fds(keep_fds, n_keep_fds);
@@ -6057,6 +6086,7 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
rt->tmp_dir = mfree(rt->tmp_dir);
rt->var_tmp_dir = mfree(rt->var_tmp_dir);
safe_close_pair(rt->netns_storage_socket);
+ safe_close_pair(rt->ipcns_storage_socket);
return mfree(rt);
}
@@ -6081,6 +6111,7 @@ static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
*n = (ExecRuntime) {
.id = TAKE_PTR(id_copy),
.netns_storage_socket = { -1, -1 },
+ .ipcns_storage_socket = { -1, -1 },
};
*ret = n;
@@ -6093,6 +6124,7 @@ static int exec_runtime_add(
char **tmp_dir,
char **var_tmp_dir,
int netns_storage_socket[2],
+ int ipcns_storage_socket[2],
ExecRuntime **ret) {
_cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
@@ -6101,7 +6133,7 @@ static int exec_runtime_add(
assert(m);
assert(id);
- /* tmp_dir, var_tmp_dir, netns_storage_socket fds are donated on success */
+ /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
r = exec_runtime_allocate(&rt, id);
if (r < 0)
@@ -6120,6 +6152,11 @@ static int exec_runtime_add(
rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
}
+ if (ipcns_storage_socket) {
+ rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
+ rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
+ }
+
rt->manager = m;
if (ret)
@@ -6136,7 +6173,7 @@ static int exec_runtime_make(
ExecRuntime **ret) {
_cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
- _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 };
+ _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }, ipcns_storage_socket[2] = { -1, -1 };
int r;
assert(m);
@@ -6144,7 +6181,7 @@ static int exec_runtime_make(
assert(id);
/* It is not necessary to create ExecRuntime object. */
- if (!c->private_network && !c->private_tmp && !c->network_namespace_path) {
+ if (!c->private_network && !c->private_ipc && !c->private_tmp && !c->network_namespace_path) {
*ret = NULL;
return 0;
}
@@ -6163,7 +6200,12 @@ static int exec_runtime_make(
return -errno;
}
- r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ret);
+ if (c->private_ipc || c->ipc_namespace_path) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
+ return -errno;
+ }
+
+ r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
if (r < 0)
return r;
@@ -6254,6 +6296,26 @@ int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
fprintf(f, " netns-socket-1=%i", copy);
}
+ if (rt->ipcns_storage_socket[0] >= 0) {
+ int copy;
+
+ copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
+ if (copy < 0)
+ return copy;
+
+ fprintf(f, " ipcns-socket-0=%i", copy);
+ }
+
+ if (rt->ipcns_storage_socket[1] >= 0) {
+ int copy;
+
+ copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
+ if (copy < 0)
+ return copy;
+
+ fprintf(f, " ipcns-socket-1=%i", copy);
+ }
+
fputc('\n', f);
}
@@ -6335,6 +6397,28 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
safe_close(rt->netns_storage_socket[1]);
rt->netns_storage_socket[1] = fdset_remove(fds, fd);
+
+ } else if (streq(key, "ipcns-socket-0")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) {
+ log_unit_debug(u, "Failed to parse ipcns socket value: %s", value);
+ return 0;
+ }
+
+ safe_close(rt->ipcns_storage_socket[0]);
+ rt->ipcns_storage_socket[0] = fdset_remove(fds, fd);
+
+ } else if (streq(key, "ipcns-socket-1")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) {
+ log_unit_debug(u, "Failed to parse ipcns socket value: %s", value);
+ return 0;
+ }
+
+ safe_close(rt->ipcns_storage_socket[1]);
+ rt->ipcns_storage_socket[1] = fdset_remove(fds, fd);
} else
return 0;
@@ -6358,7 +6442,7 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
_cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
char *id = NULL;
- int r, fdpair[] = {-1, -1};
+ int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
const char *p, *v = value;
size_t n;
@@ -6401,13 +6485,13 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
n = strcspn(v, " ");
buf = strndupa(v, n);
- r = safe_atoi(buf, &fdpair[0]);
+ r = safe_atoi(buf, &netns_fdpair[0]);
if (r < 0)
return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-0=%s: %m", buf);
- if (!fdset_contains(fds, fdpair[0]))
+ if (!fdset_contains(fds, netns_fdpair[0]))
return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
- "exec-runtime specification netns-socket-0= refers to unknown fd %d: %m", fdpair[0]);
- fdpair[0] = fdset_remove(fds, fdpair[0]);
+ "exec-runtime specification netns-socket-0= refers to unknown fd %d: %m", netns_fdpair[0]);
+ netns_fdpair[0] = fdset_remove(fds, netns_fdpair[0]);
if (v[n] != ' ')
goto finalize;
p = v + n + 1;
@@ -6419,17 +6503,56 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
n = strcspn(v, " ");
buf = strndupa(v, n);
- r = safe_atoi(buf, &fdpair[1]);
+
+ r = safe_atoi(buf, &netns_fdpair[1]);
if (r < 0)
return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-1=%s: %m", buf);
- if (!fdset_contains(fds, fdpair[1]))
+ if (!fdset_contains(fds, netns_fdpair[1]))
return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
- "exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", fdpair[1]);
- fdpair[1] = fdset_remove(fds, fdpair[1]);
+ "exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", netns_fdpair[1]);
+ netns_fdpair[1] = fdset_remove(fds, netns_fdpair[1]);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
+ }
+
+ v = startswith(p, "ipcns-socket-0=");
+ if (v) {
+ char *buf;
+
+ n = strcspn(v, " ");
+ buf = strndupa(v, n);
+
+ r = safe_atoi(buf, &ipcns_fdpair[0]);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to parse exec-runtime specification ipcns-socket-0=%s: %m", buf);
+ if (!fdset_contains(fds, ipcns_fdpair[0]))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+ "exec-runtime specification ipcns-socket-0= refers to unknown fd %d: %m", ipcns_fdpair[0]);
+ ipcns_fdpair[0] = fdset_remove(fds, ipcns_fdpair[0]);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
+ }
+
+ v = startswith(p, "ipcns-socket-1=");
+ if (v) {
+ char *buf;
+
+ n = strcspn(v, " ");
+ buf = strndupa(v, n);
+
+ r = safe_atoi(buf, &ipcns_fdpair[1]);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to parse exec-runtime specification ipcns-socket-1=%s: %m", buf);
+ if (!fdset_contains(fds, ipcns_fdpair[1]))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+ "exec-runtime specification ipcns-socket-1= refers to unknown fd %d: %m", ipcns_fdpair[1]);
+ ipcns_fdpair[1] = fdset_remove(fds, ipcns_fdpair[1]);
}
finalize:
- r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, fdpair, NULL);
+ r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
if (r < 0)
return log_debug_errno(r, "Failed to add exec-runtime: %m");
return 0;
diff --git a/src/core/execute.h b/src/core/execute.h
index 20e1799b46..4c7a5b874f 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -117,6 +117,9 @@ struct ExecRuntime {
/* An AF_UNIX socket pair, that contains a datagram containing a file descriptor referring to the network
* namespace. */
int netns_storage_socket[2];
+
+ /* Like netns_storage_socket, but the file descriptor is referring to the IPC namespace. */
+ int ipcns_storage_socket[2];
};
typedef enum ExecDirectoryType {
@@ -280,6 +283,7 @@ struct ExecContext {
bool private_devices;
bool private_users;
bool private_mounts;
+ bool private_ipc;
bool protect_kernel_tunables;
bool protect_kernel_modules;
bool protect_kernel_logs;
@@ -314,6 +318,7 @@ struct ExecContext {
Set *address_families;
char *network_namespace_path;
+ char *ipc_namespace_path;
ExecDirectory directories[_EXEC_DIRECTORY_TYPE_MAX];
ExecPreserveMode runtime_directory_preserve_mode;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 6a11ef0d9d..21bbcffe41 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -133,10 +133,12 @@ $1.ProtectKernelLogs, config_parse_bool,
$1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock)
$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
$1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path)
+$1.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.ipc_namespace_path)
$1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts)
+$1.PrivateIPC, config_parse_bool, 0, offsetof($1, exec_context.private_ipc)
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context.protect_system)
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context.protect_home)
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context.mount_flags)
diff --git a/src/core/socket.c b/src/core/socket.c
index 4bc77f3018..6255e704ce 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1552,6 +1552,14 @@ static int socket_address_listen_in_cgroup(
return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path);
}
+ if (s->exec_context.ipc_namespace_path &&
+ s->exec_runtime &&
+ s->exec_runtime->ipcns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path, CLONE_NEWIPC);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to open IPC namespace path %s: %m", s->exec_context.ipc_namespace_path);
+ }
+
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index a58495dbf8..9ed5378791 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -882,6 +882,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"ProtectProc",
"ProcSubset",
"NetworkNamespacePath",
+ "IPCNamespacePath",
"LogNamespace"))
return bus_append_string(m, field, eq);
@@ -894,6 +895,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"PrivateNetwork",
"PrivateUsers",
"PrivateMounts",
+ "PrivateIPC",
"NoNewPrivileges",
"SyslogLevelPrefix",
"MemoryDenyWriteExecute",
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 7f03db5fa7..4dd18be65c 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -2,8 +2,8 @@
#include
#include
-#include
#include
+#include
#include "alloc-util.h"
#include "fd-util.h"
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index bdc4515d62..55204463c8 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -111,6 +111,7 @@ IOWriteIOPSMax=
IPAccounting=
IPAddressAllow=
IPAddressDeny=
+IPCNamespacePath=
IPTOS=
IPTTL=
IgnoreOnIsolate=
@@ -857,6 +858,7 @@ PivotRoot=
Port=
PowerKeyIgnoreInhibited=
Private=
+PrivateIPC=
PrivateDevices=
PrivateNetwork=
PrivateTmp=