You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过libssh执行需sudo密码的Bash脚本?

如何通过libssh执行带sudo的远程脚本并传递密码

我需要通过libssh在远程主机上执行脚本,参考官方教程时遇到了sudo密码传递的问题。目前代码中sudo密码为硬编码(后续会修改),但执行后无任何反应。

初始代码

int shell_session(ssh_session session) {
    std::string cmd1 = {"sudo -S bash ./tmp/update_project"};
    std::string sudo_password = {"87654321"};
    ssh_channel channel;
    int rc;
    channel = ssh_channel_new(session);
    if (channel == NULL)
        return SSH_ERROR;
    rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK)
    {
        ssh_channel_free(channel);
        return rc;
    }

    rc = ssh_channel_request_shell(channel);
    if (rc != SSH_OK) return rc;

    rc = ssh_channel_request_exec(channel, cmd1.c_str());
    if (!(rc > 0))
        return SSH_ERROR;

    rc = ssh_channel_request_exec(channel, sudo_password.c_str());
    if (!(rc > 0))
        return SSH_ERROR;

    ssh_channel_close(channel);
    ssh_channel_send_eof(channel);
    ssh_channel_free(channel);
    return SSH_OK;
}

编辑:尝试交互式会话仍无效

int shell_session(ssh_session session) {
    std::string cmd1 = {"sudo bash /tmp/update_project"};
    std::string sudo_password = {"87654321"};
    ssh_channel channel;
    int rc;
    channel = ssh_channel_new(session);
    if (channel == NULL)
        return SSH_ERROR;
    rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK)
    {
        ssh_channel_free(channel);
        return rc;
    }

    rc = ssh_channel_request_pty(channel);
    if (rc != SSH_OK) return rc;
    rc = ssh_channel_change_pty_size(channel, 80, 24);
    if (rc != SSH_OK) return rc;
    rc = ssh_channel_request_shell(channel);
    if (rc != SSH_OK) return rc;

    char buffer[256];
    int nbytes, nwritten;
    std::vector<std::string> cmds {"sudo bash /tmp/update_project",
                                   "87654321"};
    std::vector<int> cmd_szs {sizeof(cmds[0]), sizeof(cmds[1])};
    while (ssh_channel_is_open(channel) &&
           !ssh_channel_is_eof(channel))
    {
        struct timeval timeout;
        ssh_channel in_channels[2], out_channels[2];
        fd_set fds;
        int maxfd;
        timeout.tv_sec = 30;
        timeout.tv_usec = 0;
        in_channels[0] = channel;
        in_channels[1] = NULL;
        FD_ZERO(&fds);
        FD_SET(0, &fds);
        FD_SET(ssh_get_fd(session), &fds);
        maxfd = ssh_get_fd(session) + 1;
        ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);
        if (out_channels[0] != NULL)
        {
            nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
            if (nbytes < 0) return SSH_ERROR;
            if (nbytes > 0)
            {
                nwritten = write(1, buffer, nbytes);
                if (nwritten != nbytes) return SSH_ERROR;
            }
        }
        nwritten = ssh_channel_write(channel, &cmds[0], cmd_szs[0]);
        nwritten = ssh_channel_write(channel, &cmds[1], cmd_szs[1]);
    }

    ssh_channel_close(channel);
    ssh_channel_send_eof(channel);
    ssh_channel_free(channel);
    return SSH_OK;
}

问题分析与修复方案

核心问题拆解

  1. 初始代码错误:ssh_channel_request_shell()与ssh_channel_request_exec()不能在同一会话混用——前者打开交互式shell,后者创建独立命令执行会话,二者互斥。
  2. 交互式代码错误:
    • cmd_szs计算错误:sizeof(cmds[0])获取的是std::string对象大小,而非字符串内容长度,应使用cmds[0].size()。
    • 未等待远程密码提示就发送输入,导致时机不匹配。
    • 发送内容未加换行符,远程shell无法识别为完整命令。

修复后的代码示例

int shell_session(ssh_session session) {
    const std::string sudo_cmd = "sudo -S bash /tmp/update_project\n";
    const std::string sudo_password = "87654321\n"; // 必须添加换行符模拟回车
    ssh_channel channel;
    int rc;

    channel = ssh_channel_new(session);
    if (channel == NULL)
        return SSH_ERROR;

    rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK) {
        ssh_channel_free(channel);
        return rc;
    }

    // 请求PTY并打开交互式shell
    rc = ssh_channel_request_pty(channel);
    if (rc != SSH_OK) {
        ssh_channel_free(channel);
        return rc;
    }
    rc = ssh_channel_request_shell(channel);
    if (rc != SSH_OK) {
        ssh_channel_free(channel);
        return rc;
    }

    char buffer[256];
    int nbytes, nwritten;
    bool sent_cmd = false;
    bool sent_password = false;

    struct timeval timeout;
    ssh_channel in_channels[2], out_channels[2];
    fd_set fds;
    int maxfd;

    while (ssh_channel_is_open(channel) && !ssh_channel_is_eof(channel)) {
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        in_channels[0] = channel;
        in_channels[1] = NULL;
        FD_ZERO(&fds);
        FD_SET(ssh_get_fd(session), &fds);
        maxfd = ssh_get_fd(session) + 1;

        // 等待远程输出
        rc = ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);
        if (rc < 0) {
            ssh_channel_free(channel);
            return SSH_ERROR;
        }

        if (out_channels[0] != NULL) {
            // 读取远程输出
            nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
            if (nbytes < 0) {
                ssh_channel_free(channel);
                return SSH_ERROR;
            }
            if (nbytes > 0) {
                write(1, buffer, nbytes); // 打印到本地终端用于调试

                std::string output(buffer, nbytes);
                // 根据远程输出触发对应操作
                if (!sent_cmd) {
                    // 发送sudo命令
                    nwritten = ssh_channel_write(channel, sudo_cmd.c_str(), sudo_cmd.size());
                    if (nwritten != sudo_cmd.size()) {
                        ssh_channel_free(channel);
                        return SSH_ERROR;
                    }
                    sent_cmd = true;
                } else if (!sent_password && output.find("[sudo] password for") != std::string::npos) {
                    // 收到密码提示后发送密码
                    nwritten = ssh_channel_write(channel, sudo_password.c_str(), sudo_password.size());
                    if (nwritten != sudo_password.size()) {
                        ssh_channel_free(channel);
                        return SSH_ERROR;
                    }
                    sent_password = true;
                }
            }
        }

        // 命令与密码都已发送,等待脚本执行完成
        if (sent_cmd && sent_password) {
            sleep(2); // 可根据脚本实际执行时长调整,或通过检测输出EOF判断完成
            break;
        }
    }

    ssh_channel_send_eof(channel);
    ssh_channel_close(channel);
    ssh_channel_free(channel);
    return SSH_OK;
}

关键注意事项

  • 必须使用sudo -S:让sudo从标准输入读取密码,而非终端设备,才能通过管道传递。
  • 所有输入内容需加换行符:模拟用户按下回车的操作,让远程shell识别为完整命令。
  • 按远程输出触发操作:通过检测密码提示字符串发送密码,避免输入时机错误。
  • 生产环境禁止硬编码密码:建议通过环境变量、密钥管理服务等安全方式传递。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:17:05