如何通过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; }
问题分析与修复方案
核心问题拆解
- 初始代码错误:
ssh_channel_request_shell()与ssh_channel_request_exec()不能在同一会话混用——前者打开交互式shell,后者创建独立命令执行会话,二者互斥。 - 交互式代码错误:
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
相关产品推荐
相关产品推荐

