V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
leiuu
V2EX  ›  程序员

Java ssh 客户端 Jsch 怎么执行 sudo su 后再执行新命令

  •  
  •   leiuu · 2022-03-31 21:16:34 +08:00 · 1724 次点击
    这是一个创建于 728 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景是这样的。

    我在本地通过 jsch 对一台远程机器执行命令,但该命令需 sudo 权限。

    远程用户,只开放了登录用户的 sudo su 的权限。

    导致想执行 sudo 命令需分两步:

    sudo su -
    docker ps
    

    这里涉及到 session 切换,原生的 Jsch 的 ExecChannel 就用不了。

    改为 ChannelShell 。

    ChannelShell channel = (ChannelShell) session.openChannel("shell");
    
    OutputStream os = channel.getOutputStream();
    
    os.write(("sudo su - \r").getBytes());
    os.flush();
    os.write(("docker ps \r").getBytes());
    os.flush();
    os.write(("exit \r".getBytes());
    os.flush();
    os.write(("exit \r").getBytes());
    os.flush();
    

    但这个命令的返回,是一堆 shell 命令和结果糅合在一起,比较难提取真正的结果。

    sudo su -
    docker ps
    exit
    exit
    20:51:19  [email protected]:~ 
    
    $ sudo su -
    20:51:19  [email protected]:~ 
    
    # docker ps
    CONTAINER ID   IMAGE                                                COMMAND                  CREATED        STATUS        PORTS     NAMES
    2a3da88c5f8b   ca6e5bfba5a5                                         "/usr/bin/tini -- /u…"   3 days ago     Up 3 days               docker_job_xxx
    20:51:19  [email protected]:~ 
    
    # exit
    logout
    20:51:19  [email protected]:~ 
    
    $ exit
    logout
    

    而且如果远程机器换一个 os ,例如是 centos ,则:

    Last login: Thu Mar 31 20:27:32 2022 from 192.168.1.2
    
    sudo su -
    docker ps
    exit
    exit
    [[email protected] ~]$ sudo su -
    Last login: Thu Mar 31 14:17:40 CST 2022 on pts/3
    [[email protected] ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    8929e8f93280        img_xxx     "/usr/sbin/init"    5 months ago        Up 5 months                             docker_job_yyy
    [[email protected] ~]# exit
    logout
    [[email protected] ~]$ exit
    logout
    

    不同系统或者 terminal 可能会返回格式不同,导致真正想要的结果难以提取。

    请教是否有好的解决方法呢?

    文档:

    http://www.jcraft.com/jsch/

    9 条回复    2022-04-01 19:39:29 +08:00
    kekxv
        1
    kekxv  
       2022-03-31 21:49:40 +08:00 via iPhone
    sudo chmod +s docker 路径
    comlewin
        2
    comlewin  
       2022-03-31 22:01:01 +08:00
    /**
    * 执行单句的命令
    * 1 、命令之间使用; 分隔,各个命令的执行不会影响其他命令的执行,各个命令都会执行,但不保证每个命令都执行成功
    * 2 、命令之间使用&& 分隔,前面的命令执行成功,才会去执行后面的命令,保证执行过程都是成功的
    * 3 、命令之间使用|| 分隔,||是或的意思,只有前面的命令执行失败后才会去执行下一条命令,知道执行成功一条命令为止
    */
    public static String execCommand (Session session, String command, String charset) throws JSchException, IOException {
    ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
    channelExec.setPty(true);
    InputStream stdout = channelExec.getInputStream();
    InputStream stderr = channelExec.getErrStream();
    channelExec.setCommand(command);
    channelExec.connect();
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    StringBuffer sb = new StringBuffer();
    byte[] tmp = new byte[1024];
    while(true) {
    while(stdout.available() > 0) {
    int len = stdout.read(tmp, 0, 1024);
    if (len < 0 ) {
    break;
    }
    String s_out = new String(tmp, 0, len);
    sb.append(s_out);
    //System.out.println(s);
    }
    while(stderr.available() > 0) {
    int len = stderr.read(tmp, 0, 1024);
    if (len < 0 ) {
    break;
    }
    String s_err = new String(tmp, 0, len);
    sb.append(s_err);
    //System.out.println(s);
    }
    break;
    }

    //sb.append(outinfo);
    //sb.append(errinfo);

    channelExec.disconnect();
    // session 不做断开处理
    //session.disconnect();

    return sb.toString();
    }
    Puteulanus
        3
    Puteulanus  
       2022-04-01 00:39:01 +08:00   ❤️ 1
    sudo su -c 'docker ps'
    这样行吗
    biubiuF
        4
    biubiuF  
       2022-04-01 00:43:21 +08:00
    sudo -S -p 'password' "+ command
    ysc3839
        5
    ysc3839  
       2022-04-01 02:51:58 +08:00 via Android   ❤️ 1
    同样建议试试 sudo su -c
    实在不行的话,执行命令前后都 echo 随机字符串,然后根据这两段字符串来分割
    leiuu
        6
    leiuu  
    OP
       2022-04-01 10:53:36 +08:00
    @kekxv 老哥 这个会写操作 不安全

    @comlewin 是的 分隔符的方式适用没有 session 切换的场景

    @Puteulanus @ysc3839 谢谢 2 位 竟然可以了!

    @biubiuF 不知道密码的 不过如果有密码是可行的!
    undownding
        7
    undownding  
       2022-04-01 17:11:34 +08:00
    有没有一种可能,/var/run/docker.sock 可以用 curl 操作?(狗头)
    undownding
        8
    undownding  
       2022-04-01 17:22:08 +08:00   ❤️ 1
    su -c ' curl --unix-socket /run/docker.sock http://localhost/containers/json'
    leiuu
        9
    leiuu  
    OP
       2022-04-01 19:39:29 +08:00
    @undownding 老哥 太帅了 奥利给!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1036 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 22:28 · PVG 06:28 · LAX 15:28 · JFK 18:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.