Skip to content

[Feature] 提权通道:config 增加 sudo/su 凭据组 + exec --sudo/--su #2

Description

@adrian803

环境 / Env

  • agent-ssh-cli 0.4.1(npm 发行版 + Rust native)
  • 远端 util-linux:su 通道建议 >= 2.38su -P,见下);旧版需 fallback script 包装

动机 / Motivation

Agent 运维远端服务器时,身份切换 / 提权是刚需,但目标环境差异很大:

  • 自管服务器:sudo(认证用执行者自己的密码);
  • 大公司 / 甲方:常无个人 sudo,只有共享业务账号(oracle / app / 中间件)密码或共享 root 密码,只能 su

当前 exec 没有任何安全凭据通道。临时解法是远端落盘密码文件再重定向喂给 sudo -S——以下脚本来自本项目实际运维实践(由 AI 助手协助设计,已在真实服务器上运行验证),并非通用最佳实践,仅作现状说明:

agentsshcli exec <conn> "echo '<pw>' > /tmp/.pw && chmod 600 /tmp/.pw \
  && sudo -S -p '' <cmd> < /tmp/.pw; unlink /tmp/.pw"

该方案存在:① 明文密码进 argv(本机 shell history / ps / Agent 日志可见);② 远端临时文件落盘残留窗口;③ 复合 shell 串无法被 commandWhitelist 精确匹配(validate_command 对整条命令正则校验)。

建议:复用项目已有的 SSH 密码本地加密机制(password 首次连接被动加密 → secrets.json ChaCha20-Poly1305 密文 → passwordRef),新增 sudo / su 双提权通道,密码只经 SSH 通道 stdin 喂给远端,全程零 argv / 零落盘。

提案 / Proposal

config.json(双通道平铺,均可选)

{
  "name": "conn-name",
  "host": "10.0.0.1",
  "username": "linxi",
  "password": "",
  "passwordRef": "agentsshcli:conn-name",

  // sudo 通道(可选;多数场景 sudoPassword 可留空,自动 fallback SSH 密码)
  "sudoUser": "root",
  "sudoPassword": "",
  "sudoPasswordRef": "agentsshcli:conn-name:sudo",

  // su 通道(可选;suUser + suPassword 需成对)
  "suUser": "oracle",
  "suPassword": "",
  "suPasswordRef": "agentsshcli:conn-name:su"
}

用法

# sudo:密码取用优先级 sudoPasswordRef → SSH passwordRef(sudo 认证=自己密码的常见语义)→ 报错
agentsshcli exec --sudo conn "systemctl status app"

# su:切共享业务账号(suPassword 必配;suUser 缺省 root)
agentsshcli exec --su conn "ls -la ~/appdata"

凭据迁移规则

sudoPassword / suPassword 明文首次写入 → 首次对应 flag 连接时加密存入 secrets.json → 明文置空、写入 sudoPasswordRef / suPasswordRef;更新密码 = 重新填明文,下次连接自动覆盖(与现有 password 机制完全一致)。

验收标准 / Acceptance

  • --sudo / --su 时密码不出现在任何 argv / 本机与远端 shell history / 进程列表,远端零落盘
  • sudoPasswordRef 缺失时 --sudo 自动 fallback 复用 passwordRef 解密结果(SSH 密码认证场景免重复配置)
  • 密码经 SSH channel stdin(data() + eof())喂入,命令包装:sudo → sudo -u <user> -S -p '' <cmd>;su → su -P -c '<cmd>' <user>
  • requiretty(RHEL/CentOS 7 默认)与旧 util-linux(无 su -P)环境自动 fallback script -qec 包装
  • 不传 --sudo / --su 行为与现状完全一致(默认安全,零回归);两 flag 互斥;凭据缺失明确报错
  • --json 模式透传真实 exitCode / stdout / stderr,认证失败如实返回非零码
  • secrets.json 中 SSH / sudo / su 三组密文 key 互不覆盖(agentsshcli:<name> / :sudo / :su

实现方向 / Implementation notes

参考 native/src/main.rs(@ main):

  • 加密基元可直接复用:encrypt_password / decrypt_password(L770 / L789,ChaCha20-Poly1305,与密码语义无关)
  • 迁移逻辑照抄 migrate_plain_password_for_connection(L847,泛型 serde_json::Value 操作字段);注意 ref key 命名空间隔离,避免 secrets.json HashMap insert 覆盖 SSH 密码密文
  • 传输层:execute_remote_command_with_session_async(L1841)当前只消费 stdout/stderr,从不写 channel stdin;需补一次 channel.data() + eof()(一次性喂密码,无需交互 pty 桥)
  • 白名单兼容:validate_command(L1504)校验用户原始命令,sudo/su 前缀为 CLI 注入的受信包装,不参与正则匹配
  • 新字段遵循 RawConnection#[serde(rename_all = "camelCase")],勿用小写

兼容与安全边界 / Notes

  • 建议依赖 util-linux su -P>= 2.38,stdin 非 tty 时自动关 ECHO 防回显);旧版 fallback script -qec
  • 不做 --sudo-password <明文> 之类的便捷参数(会把问题引回 argv)
  • su 到 root 需要 root 密码,本特性仅提供安全通道,凭据托管责任在调用方

我可以尝试提交 PR

如果本特性方向认可,我可以尝试提交 PR:实现已拆分为可独立合入的提交(commit 1:凭据层 = 数据模型 + sudo/su 迁移 + 解密;commit 2:执行层 = 命令包装器 + channel stdin + 远端探测;commit 3:README 与测试),代码位置已按上文定位。也可以先只合入凭据层(commit 1),为后续能力打底。

参考 / workaround

上述远端落盘方案为当前临时做法,特性落地后退役。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions