“sudo”在启动“agetty ttyS0 9600”的终端(而不是“串行”终端)中提示输入密码

“sudo”在启动“agetty ttyS0 9600”的终端(而不是“串行”终端)中提示输入密码

更广泛的背景下,我使用 QEMU+KVM 来虚拟化 ubuntu 机器,如下所示:

qemu-system-x86_64 \
    -enable-kvm \
    -m 2048 \
    -nic none \
    -vnc :5 \
    -hda test-vm.raw \
    -device vfio-pci,host=01:00.0 \
    -serial unix:/tmp/console.sock,server,nowait

我用来/tmp/console.sock通过“串行”登录虚拟机:

socat stdin,raw,echo=0,escape=0x11 "unix-connect:/tmp/console.sock"

这可以正常工作,不会出现任何重大问题。

但是,当我输入密码时,sudo su启动该agetty进程的终端(而不是串行控制台)上会出现输入密码的提示。

我知道agetty从终端启动并不是通常的启动方式。

但是,我很好奇这是如何发生的以及如何在agetty没有终端的情况下从终端开始任何连接(sudo在已启动的终端上询问密码agetty让我相信有某些连接)到已启动它的终端。

我已经尝试过:

$ nohup agetty ttyS0 9600 &
$ disown <pid>

没有成功。

我很想知道这里起作用的潜在机制以及我如何解决这个问题*。

*如前所述,我知道agetty从普通终端开始并不是使用该程序的方式。我仍然对这种情况如何发生以及如何防止agetty这种行为感兴趣。

答案1

当您从终端运行时agetty,它会成为新会话的会话领导者,并继承调用它的控制终端。因此,在虚拟机中执行的任何命令都会继承控制终端,从而影响 sudo 等实用程序的行为。启动agetty 的终端被用作控制终端,这就是sudo 提示输入密码的原因。

终端会话通常与会话 ID (SID) 相关联,会话中运行的每个进程都属于该 SID。设置SID的进程称为会话领导者。当agetty启动时,它成为新会话的会话领导者并继承控制终端。因此,当您sudo在虚拟机中运行时,启动agetty进程的终端上会出现输入密码的提示。

要切断agetty和终端之间的连接,您可以使用setsid命令:

setsid agetty ttyS0 9600

setsid命令创建一个新会话,并将调用的命令设置为新会话的会话领导者。此操作还将命令设置为没有控制终端,从而确保新会话中生成的进程(如sudo)不会继承原始控制终端。从的 POSIX 规范setsid:

如果调用进程不是进程组领导者,setsid() 函数将创建一个新会话。返回后,调用进程应是该新会话的会话领导者,应是新进程组的进程组领导者,并且不应具有控制终端。调用进程的进程组ID 应设置为等于调用进程的进程ID。调用进程应该是新进程组中的唯一进程和新会话中的唯一进程。

您可能会问为什么sudo首先关心这个控制终端。该sudo命令尽力与调用它的用户直接交互,而无需通过任何中介,这与仅仅写入 stdout 或 stderr 不同。写入 stdout 或 stderr 可能存在泄露敏感信息的风险,或者其他进程可能会窃听此数据。

控制终端被认为更接近用户的直接线路,使其成为密码输入等用户敏感操作的更安全的通道。调用 sudo 时,它通常会绕过 stdout 或 stderr 将密码提示直接写入终端。这样做是为了确保密码提示对用户可见,并且不会意外重定向、记录或以其他方式捕获。

相关内容