You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在基于Busybox的Initramfs(Arch Linux mkinitcpio)中向休眠Cryptsetup进程传递解锁密码的技术问题

解决initramfs中向休眠的cryptsetup进程传递密码的问题

首先得明确你遇到的核心问题:直接写入/proc/200/fd/0/dev/console没用,是因为你混淆了终端的输入和输出路径——写入/dev/console是输出到屏幕,而cryptsetup是从终端的输入缓冲区读取密码,不是从这个设备的写入端读取。

正常场景下的密码输入流程

当你在控制台执行cryptsetup open --type luks /dev/vda2 root后,进程进入休眠的本质是它在调用read()系统调用等待终端输入:

  1. 从你贴的interactive_pass代码片段可以看到,cryptsetup优先尝试打开/dev/tty(进程的控制终端)作为输入输出fd;如果失败,才回退到STDIN_FILENO(0)和STDERR_FILENO(2)。
  2. 当用户在键盘输入密码并回车时,每个字符会被内核的输入子系统处理,插入到对应终端(比如/dev/tty0)的输入缓冲区中。
  3. 内核检测到输入缓冲区有数据后,会唤醒处于休眠状态的cryptsetup进程,read()调用返回读取到的字符(包括回车),之后cryptsetup就会进行解密操作。

可行的解决方案

要向休眠的cryptsetup进程传递密码,你需要模拟键盘输入,将字符插入到它正在监听的终端的输入缓冲区,而不是直接写入设备文件或进程fd。以下是具体步骤:

1. 编译一个静态二进制工具来模拟键盘输入

编写一个简单的C程序(比如叫sendpass),利用TIOCSTI ioctl向终端输入缓冲区插入字符:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <string.h>

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <tty-device> <password>\n", argv[0]);
        return 1;
    }

    int fd = open(argv[1], O_RDWR);
    if (fd == -1) {
        perror("Failed to open tty device");
        return 1;
    }

    const char *pass = argv[2];
    // 逐个插入密码字符
    for (size_t i = 0; pass[i] != '\0'; i++) {
        if (ioctl(fd, TIOCSTI, &pass[i]) == -1) {
            perror("Failed to insert character");
            close(fd);
            return 1;
        }
    }

    // 插入换行符模拟按下回车
    char newline = '\n';
    if (ioctl(fd, TIOCSTI, &newline) == -1) {
        perror("Failed to insert newline");
        close(fd);
        return 1;
    }

    close(fd);
    return 0;
}

编译成静态二进制(确保在initramfs中无需动态链接库):

gcc -static sendpass.c -o sendpass

2. 将工具加入initramfs

修改你的mkinitcpio配置,把sendpass工具添加到initramfs中:

  • /etc/mkinitcpio.confBINARIES数组中加入sendpass
    BINARIES=(sendpass)
    
  • 重新生成initramfs:
    mkinitcpio -P
    

3. 远程登录后传递密码

当你通过Dropbear远程登录到initramfs环境后,执行以下命令(假设cryptsetup监听的是/dev/tty0,如果不确定可以尝试其他/dev/tty*设备):

sendpass /dev/tty0 "your-luks-password"

这个命令会把密码字符和换行符插入到控制台终端的输入缓冲区,内核会唤醒休眠的cryptsetup进程,读取到密码后即可完成解密,继续启动流程。

为什么之前的方法无效?

  • 直接写入/proc/200/fd/0:虽然这个fd指向/dev/console,但写入这个fd是向控制台输出内容,而不是作为输入传递给进程——cryptsetup是从终端的输入缓冲区读取,不是从这个fd的写入端读取。
  • 写入/dev/tty*设备文件:同样,直接写入设备文件是输出到屏幕,不会进入输入缓冲区,因此cryptsetup的read()调用不会被唤醒。

内容的提问来源于stack exchange,提问作者zar3bski

火山引擎 最新活动