core/service: support credentials refreshing on reload (#40093)

Closes https://github.com/systemd/systemd/issues/21099
Closes https://github.com/systemd/systemd/issues/35227
This commit is contained in:
Daan De Meyer
2026-02-13 10:21:44 +01:00
committed by GitHub
28 changed files with 1120 additions and 181 deletions
+65
View File
@@ -262,6 +262,71 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
return 0;
}
static int namespace_enter_one_idempotent(int nsfd, NamespaceType type) {
int r;
/* Join a namespace, but only if we're not part of it already. This is important if we don't necessarily
* own the namespace in question, as kernel would unconditionally return EPERM otherwise. */
assert(nsfd >= 0);
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
r = is_our_namespace(nsfd, type);
if (r < 0)
return r;
if (r > 0)
return 0;
if (setns(nsfd, namespace_info[type].clone_flag) < 0)
return -errno;
return 1;
}
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
int r;
/* Similar to namespace_enter(), but operates on a set of namespaces that are potentially owned
* by the userns ("delegated"), in which case we'll need to gain CAP_SYS_ADMIN by joining
* the userns first, and the rest later. */
assert(userns_fd >= 0);
/* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */
block_dlopen();
if (setns(userns_fd, CLONE_NEWUSER) < 0)
return -errno;
if (pidns_fd >= 0) {
r = namespace_enter_one_idempotent(pidns_fd, NAMESPACE_PID);
if (r < 0)
return r;
}
if (mntns_fd >= 0) {
r = namespace_enter_one_idempotent(mntns_fd, NAMESPACE_MOUNT);
if (r < 0)
return r;
}
if (netns_fd >= 0) {
r = namespace_enter_one_idempotent(netns_fd, NAMESPACE_NET);
if (r < 0)
return r;
}
if (root_fd >= 0) {
if (fchdir(root_fd) < 0)
return -errno;
if (chroot(".") < 0)
return -errno;
}
return maybe_setgroups(/* size = */ 0, NULL);
}
int fd_is_namespace(int fd, NamespaceType type) {
int r;
+1
View File
@@ -47,6 +47,7 @@ int namespace_open(
int *ret_root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
int fd_is_namespace(int fd, NamespaceType type);
int is_our_namespace(int fd, NamespaceType type);
+5 -1
View File
@@ -1778,6 +1778,7 @@ int namespace_fork_full(
int netns_fd,
int userns_fd,
int root_fd,
bool delegated,
PidRef *ret) {
_cleanup_(pidref_done_sigkill_wait) PidRef pidref_outer = PIDREF_NULL;
@@ -1823,7 +1824,10 @@ int namespace_fork_full(
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
if (delegated)
r = namespace_enter_delegated(userns_fd, pidns_fd, mntns_fd, netns_fd, root_fd);
else
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
if (r < 0) {
log_full_errno(prio, r, "Failed to join namespace: %m");
report_errno_and_exit(errno_pipe_fd[1], r);
+2 -1
View File
@@ -201,6 +201,7 @@ int namespace_fork_full(
int netns_fd,
int userns_fd,
int root_fd,
bool delegated,
PidRef *ret);
static inline int namespace_fork(
@@ -215,7 +216,7 @@ static inline int namespace_fork(
PidRef *ret) {
return namespace_fork_full(outer_name, inner_name, NULL, 0, flags,
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd,
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd, false,
ret);
}
+1
View File
@@ -223,6 +223,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
[SERVICE_RUNNING] = "running",
[SERVICE_EXITED] = "exited",
[SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions",
[SERVICE_REFRESH_CREDENTIALS] = "refresh-credentials",
[SERVICE_RELOAD] = "reload",
[SERVICE_RELOAD_SIGNAL] = "reload-signal",
[SERVICE_RELOAD_NOTIFY] = "reload-notify",
+1
View File
@@ -132,6 +132,7 @@ typedef enum ServiceState {
SERVICE_RUNNING,
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */
SERVICE_REFRESH_CREDENTIALS, /* ditto, but for credentials */
SERVICE_RELOAD, /* Reloading via ExecReload= */
SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */