使用 systemd 服务创建的子进程sudo
放置在user.slice
.仅在 CentOS 8 (x86_64 20230606) 上观察到此行为,而在 Ubuntu 20.04.5 LTS 上则未观察到,在 Ubuntu 20.04.5 LTS 上,此类子进程被放置在其中system.slice
。
这是一个很大的问题,因为这会导致主服务进程驻留在不同的 cgroup 中,而创建的子进程sudo
最终位于另一个 cgroup 中。因此,systemctl stop
在服务上调用只会停止主服务进程,而不会停止使用 创建的子进程sudo
,因此它们最终会挂起。
复制步骤:
- 以非 root 用户(具有
sudo
权限)身份登录:
$ ssh centos@{ADDR}
script.sh
创建包含以下内容的bash 脚本:
#!/bin/bash
sudo sleep 1000
- 使脚本可执行:
$ chmod +x script.sh
- 将脚本作为 systemd 服务运行:
$ sudo systemd-run ./script.sh
Running as unit: run-r16ed2fef94d442b5800035653bcbbe01.service
- 检查状态:
$ sudo systemctl status run-rf4fe2865d42240c1a95f1ed497575494.service
● run-rf4fe2865d42240c1a95f1ed497575494.service - /home/centos/./script.sh
Loaded: loaded (/run/systemd/transient/run-rf4fe2865d42240c1a95f1ed497575494.service; transient)
Transient: yes
Active: active (running) since Wed 2023-11-15 10:01:49 UTC; 1min 1s ago
Main PID: 10587 (script.sh)
Tasks: 1 (limit: 97480)
Memory: 1.7M
CGroup: /system.slice/run-rf4fe2865d42240c1a95f1ed497575494.service
└─10587 /bin/bash /home/centos/./script.sh
您可以看到结果cgroup 中既没有列出子进程也没有列出sudo
。sleep
注意 PID 10587。
- 检查睡眠进程是否存在:
$ ps aux | grep sleep
root 10588 0.0 0.0 333172 8140 ? S 10:01 0:00 sudo sleep 1000
root 10591 0.0 0.0 217092 848 ? S 10:01 0:00 sleep 1000
centos 10687 0.0 0.0 221940 1164 pts/0 S+ 10:03 0:00 grep --color=auto sleep
停止创建的 systemd 进程不会杀死这两个sudo
和sleep
进程(PID 分别为 10588 和 10591)。
- 检查这些进程所属的systemd片:
- 主要的systemd服务进程(10587,见
systemctl status
上文):
$ cat /proc/10587/cgroup | grep name=
1:name=systemd:/system.slice/run-rf4fe2865d42240c1a95f1ed497575494.service
- 子
sudo
进程(10588):
$ cat /proc/10588/cgroup | grep name=
1:name=systemd:/user.slice/user-0.slice/session-c37.scope
- 子进程
sleep
(10591)"
$ cat /proc/10591/cgroup | grep name=
1:name=systemd:/user.slice/user-0.slice/session-c37.scope
您可以观察到主脚本进程属于system.slice
,而子进程属于user.slice
。
在 Ubuntu 20.04.5 LTS 中,使用完全相同的步骤会导致所有子进程被放置在同一个 cgroup 中,位于以下位置system.slice
:
$ sudo systemd-run ./script.sh
Running as unit: run-r09332b0da31a4dd198286a87a917e55f.service
ubuntu@ip-10-0-0-92:~$ sudo systemctl status run-r09332b0da31a4dd198286a87a917e55f.service
● run-r09332b0da31a4dd198286a87a917e55f.service - /home/ubuntu/./script.sh
Loaded: loaded (/run/systemd/transient/run-r09332b0da31a4dd198286a87a917e55f.service; transient)
Transient: yes
Active: active (running) since Wed 2023-11-15 10:06:55 UTC; 7s ago
Main PID: 1729 (script.sh)
Tasks: 3 (limit: 18627)
Memory: 1.4M
CGroup: /system.slice/run-r09332b0da31a4dd198286a87a917e55f.service
├─1729 /bin/bash /home/ubuntu/./script.sh
├─1730 sudo sleep 1000
└─1731 sleep 1000
因此,停止该服务会杀死所有子进程。
那么 CentOS 中发生了什么?我怎样才能使 systemd 的行为与 Ubuntu 中的行为相同?我可以强制将子进程放置在 下的同一个 cgroup 中system.slice
吗?
我检查了 CentOS 中创建的服务的 systemd 参数,它们实际上与 Ubuntu 的相同。特别是,该Slice
参数在 CentOS 中设置为system.slice
:
$ sudo systemctl show run-rf4fe2865d42240c1a95f1ed497575494.service | grep Slice=
Slice=system.slice
答案1
Systemd 服务配置不会接管子进程的创建,也不会将子进程移动到任何地方。这是由 PAM 配置sudo
本身完成的,它会不必要地调用pam_systemd
(可能是故意为了达到您不想要的结果,或者可能只是试图在人们尝试以 root 身份运行 GUI 应用程序时为目标用户设置 XDG_RUNTIME_DIR?我不知道)。
PAM 模块创建一个 systemd-logind 会话,这会导致 logind 将调用进程移至“会话”cgroup,与您自己的 shell 进程移出 sshd.service 或[电子邮件受保护]当您登录时。
编辑/etc/pam.d/sudo
以禁用此模块。但是,请注意不要为其他 PAM 配置禁用它 – 如果文件包含 /etc/pam.d/system-auth (控制台和 SSH 登录也使用它),则您需要将 pam_systemd 保留在那里;相反,你可能需要插入一个pam_succeed_if欺骗 PAM 跳过它:
session [success=1 default=ignore] pam_succeed_if.so service = sudo
session whatever pam_systemd.so
(如果 pam_succeed_if 返回 PAM_SUCCESS,“success=1”会跳过 1 个下一个模块,而“default=ignore”会导致忽略所有其他结果。)