无需创建中间进程即可切换用户
Switch user without creating an intermediate process
我可以使用 sudo
或 su
作为另一个用户执行命令。通过与 exec
结合,我能够用 sudo
或 su
替换当前进程,并且子进程 运行 命令。但是我想以另一个用户的身份用命令 运行 替换当前进程。我该怎么做?
使用 sleep inf
作为命令进行测试,someguy
作为用户进行测试:
exec su someguy -c 'sleep inf'
这给了我 pstree
:
bash───su───sleep
和
exec sudo -u someguy sleep inf
给予
bash───sudo───sleep
在这两种情况下,我只想要 sleep
命令,以 bash
作为父命令。
我希望我可以用 setuid()
和 exec()
.
的某种序列从 C 中做到这一点
我不同意观察和结论。见下文:
我创建了两个 shell脚本:
$ cat just_sudo.sh
#!/bin/bash
sudo sleep inf
$ cat exec_sudo.sh
#!/bin/bash
exec sudo sleep inf
所以,一个有 exec,一个没有。如果我执行 pstree
查看起始情况,我会得到:
$ pstree $$
bash───pstree
$ echo $$
17250
这给了我基线。接下来我启动了两个脚本:
$ bash just_sudo.sh &
[1] 1218
$ bash exec_sudo.sh &
[2] 1220
然后,pstree
给出:
$ pstree $$
bash─┬─bash───sleep
├─pstree
└─sleep
第一个是just_sudo
,第二个是exec_sudo
。 运行 作为 root:
$ ps -ef | grep sleep
root 1219 1218 0 14:01 pts/4 00:00:00 sleep inf
root 1220 17250 0 14:01 pts/4 00:00:00 sleep inf
再次第一个是 just_sudo
,第二个是 exec_sudo
。您可以看到 exec_sudo
中睡眠的父 PID 是启动脚本的交互式 shell PID 是 1220,这是我们在 中启动脚本时看到的 PID背景。
如果你使用两个终端 windows 并且不把它放在后台,这也可以工作:
terminal 1 terminal 2
$ echo $$
16053 $ pstree 16053
bash
$ sudo sleep inf
$ pstree 16053
bash───sleep
^C
$ exec sudo sleep inf
$ pstree 16053
sleep
^C
( window is closed )
因此,在 my linux 系统上,行为与您不同 suggest.The sudo 可能保留在进程树中的唯一方式是如果它 运行s 在现有的 tty 中(因此没有 exec),或者如果它是用伪终端调用的,例如 exec sudoedit
.
为了释放一个命令,你必须给他std io:
为此,您可以关闭所有 stdin、stdout 和 stderr 或者让他们指向别处。
试试这个:
su - someguy -c 'exec nohup sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'
注:
su - someguy -c 'exec nohup sleep 60 &'
够了,还有
su - someguy -c 'exec sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'
也可以。
考虑看看 man nohup
注2:在bash下,您可以使用:
su - someguy -c 'exec sleep 60 & disown -h'
... 并阅读 help disown
或 man bash
。
展示如何关闭所有IOs:
的小演示
su - someguy -c 'exec 0<&- ; exec 1>&- ; exec 2>&- ; exec sleep 60 &'
快速测试:
pstree $(ps -C sleep ho pid)
sleep
sudo sleep
和exec sudo sleep
的区别在于第二个命令sudo进程替换bash图像和调用shell进程在sleep退出时退出
pstree -p $$
bash(8765)───pstree(8943)
((sleep 1; pstree -p $$ )&); sudo -u user sleep 2
bash(8765)───sudo(8897)───sleep(8899)
((sleep 1; pstree -p $$ )&); exec sudo -u user sleep 2
sudo(8765)───sleep(8993)
然而,sudo
或 su
分叉一个新进程的事实取决于设计及其实现(找到一些来源 here)。
来自 sudo 手册页:
Process model
When sudo runs a command, it calls fork(2), sets up the execution environment as described above, and calls the execve system call in the child process. The main sudo
process waits until the command has completed, then passes the command's exit status to the security policy's close function and exits. If an I/O logging plugin is config-
ured or if the security policy explicitly requests it, a new pseudo-terminal (“pty”) is created and a second sudo process is used to relay job control signals between the
user's existing pty and the new pty the command is being run in. This extra process makes it possible to, for example, suspend and resume the command. Without it, the com-
mand would be in what POSIX terms an “orphaned process group” and it would not receive any job control signals. As a special case, if the policy plugin does not define a
close function and no pty is required, sudo will execute the command directly instead of calling fork(2) first. The sudoers policy plugin will only define a close function
when I/O logging is enabled, a pty is required, or the pam_session or pam_setcred options are enabled. Note that pam_session and pam_setcred are enabled by default on sys-
tems using PAM.
我不确定是否可以使用 sudo
或 su
来完成。但是您可以使用简单的 c
程序轻松实现这一点。我将展示一个带有硬编码命令和用户 ID 的极简版,但您始终可以根据自己的喜好对其进行自定义
test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
int runAs(int gid, int uid, char *command[]) {
setgid(gid);
setuid(uid);
char *args[]={"sleep","inf",NULL};
execvp(args[0],args);
}
int main(int argc, char *argv[] )
{
runAs(1000, 65534, argv);
return 0;
}
Note: on my machine 1000
is the uid/gid of vagrant
user and group. 65534
is uid and gid of nobody
user and group
build.sh
#!/bin/bash
sudo gcc test.c -o sosu
sudo chown root:root sosu
sudo chmod u+s sosu
现在是测试时间
$ pstree $$ -p
bash(23251)───pstree(28627)
$ ./sosu
现在从另一个终端
$ pstree -p 23251
bash(23251)───sleep(28687)
$ ps aux | grep [2]8687
nobody 28687 0.0 0.0 7288 700 pts/0 S+ 11:40 0:00 sleep inf
如您所见,进程是 运行 作为 nobody
并且它是 bash
的子进程
我可以使用 sudo
或 su
作为另一个用户执行命令。通过与 exec
结合,我能够用 sudo
或 su
替换当前进程,并且子进程 运行 命令。但是我想以另一个用户的身份用命令 运行 替换当前进程。我该怎么做?
使用 sleep inf
作为命令进行测试,someguy
作为用户进行测试:
exec su someguy -c 'sleep inf'
这给了我 pstree
:
bash───su───sleep
和
exec sudo -u someguy sleep inf
给予
bash───sudo───sleep
在这两种情况下,我只想要 sleep
命令,以 bash
作为父命令。
我希望我可以用 setuid()
和 exec()
.
我不同意观察和结论。见下文:
我创建了两个 shell脚本:
$ cat just_sudo.sh
#!/bin/bash
sudo sleep inf
$ cat exec_sudo.sh
#!/bin/bash
exec sudo sleep inf
所以,一个有 exec,一个没有。如果我执行 pstree
查看起始情况,我会得到:
$ pstree $$
bash───pstree
$ echo $$
17250
这给了我基线。接下来我启动了两个脚本:
$ bash just_sudo.sh &
[1] 1218
$ bash exec_sudo.sh &
[2] 1220
然后,pstree
给出:
$ pstree $$
bash─┬─bash───sleep
├─pstree
└─sleep
第一个是just_sudo
,第二个是exec_sudo
。 运行 作为 root:
$ ps -ef | grep sleep
root 1219 1218 0 14:01 pts/4 00:00:00 sleep inf
root 1220 17250 0 14:01 pts/4 00:00:00 sleep inf
再次第一个是 just_sudo
,第二个是 exec_sudo
。您可以看到 exec_sudo
中睡眠的父 PID 是启动脚本的交互式 shell PID 是 1220,这是我们在 中启动脚本时看到的 PID背景。
如果你使用两个终端 windows 并且不把它放在后台,这也可以工作:
terminal 1 terminal 2
$ echo $$
16053 $ pstree 16053
bash
$ sudo sleep inf
$ pstree 16053
bash───sleep
^C
$ exec sudo sleep inf
$ pstree 16053
sleep
^C
( window is closed )
因此,在 my linux 系统上,行为与您不同 suggest.The sudo 可能保留在进程树中的唯一方式是如果它 运行s 在现有的 tty 中(因此没有 exec),或者如果它是用伪终端调用的,例如 exec sudoedit
.
为了释放一个命令,你必须给他std io:
为此,您可以关闭所有 stdin、stdout 和 stderr 或者让他们指向别处。
试试这个:
su - someguy -c 'exec nohup sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'
注:
su - someguy -c 'exec nohup sleep 60 &'
够了,还有
su - someguy -c 'exec sleep 60 >/tmp/sleep.log 2>/tmp/sleep.err <<<"" &'
也可以。
考虑看看 man nohup
注2:在bash下,您可以使用:
su - someguy -c 'exec sleep 60 & disown -h'
... 并阅读 help disown
或 man bash
。
展示如何关闭所有IOs:
的小演示su - someguy -c 'exec 0<&- ; exec 1>&- ; exec 2>&- ; exec sleep 60 &'
快速测试:
pstree $(ps -C sleep ho pid)
sleep
sudo sleep
和exec sudo sleep
的区别在于第二个命令sudo进程替换bash图像和调用shell进程在sleep退出时退出
pstree -p $$
bash(8765)───pstree(8943)
((sleep 1; pstree -p $$ )&); sudo -u user sleep 2
bash(8765)───sudo(8897)───sleep(8899)
((sleep 1; pstree -p $$ )&); exec sudo -u user sleep 2
sudo(8765)───sleep(8993)
然而,sudo
或 su
分叉一个新进程的事实取决于设计及其实现(找到一些来源 here)。
来自 sudo 手册页:
Process model
When sudo runs a command, it calls fork(2), sets up the execution environment as described above, and calls the execve system call in the child process. The main sudo process waits until the command has completed, then passes the command's exit status to the security policy's close function and exits. If an I/O logging plugin is config- ured or if the security policy explicitly requests it, a new pseudo-terminal (“pty”) is created and a second sudo process is used to relay job control signals between the user's existing pty and the new pty the command is being run in. This extra process makes it possible to, for example, suspend and resume the command. Without it, the com- mand would be in what POSIX terms an “orphaned process group” and it would not receive any job control signals. As a special case, if the policy plugin does not define a close function and no pty is required, sudo will execute the command directly instead of calling fork(2) first. The sudoers policy plugin will only define a close function when I/O logging is enabled, a pty is required, or the pam_session or pam_setcred options are enabled. Note that pam_session and pam_setcred are enabled by default on sys- tems using PAM.
我不确定是否可以使用 sudo
或 su
来完成。但是您可以使用简单的 c
程序轻松实现这一点。我将展示一个带有硬编码命令和用户 ID 的极简版,但您始终可以根据自己的喜好对其进行自定义
test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
int runAs(int gid, int uid, char *command[]) {
setgid(gid);
setuid(uid);
char *args[]={"sleep","inf",NULL};
execvp(args[0],args);
}
int main(int argc, char *argv[] )
{
runAs(1000, 65534, argv);
return 0;
}
Note: on my machine
1000
is the uid/gid ofvagrant
user and group.65534
is uid and gid ofnobody
user and group
build.sh
#!/bin/bash
sudo gcc test.c -o sosu
sudo chown root:root sosu
sudo chmod u+s sosu
现在是测试时间
$ pstree $$ -p
bash(23251)───pstree(28627)
$ ./sosu
现在从另一个终端
$ pstree -p 23251
bash(23251)───sleep(28687)
$ ps aux | grep [2]8687
nobody 28687 0.0 0.0 7288 700 pts/0 S+ 11:40 0:00 sleep inf
如您所见,进程是 运行 作为 nobody
并且它是 bash