基于websocket和java实现webshell访问docker容器

基于websocket和java实现webshell访问docker容器
基于websocket和java实现webshell访问docker容器

需求:PaaS平台展示容器信息,并在web端实现与该容器webshell交互。

介绍:通过ws请求与主机建立websocket连接,执行docker exec -i [containerid] /bin/bash命令进入docker容器。进入容器是可以理解为进入进程,通过进程的输入输出流进行交互,并且process的InputStream的在read的时候是一个阻塞,因此在建立连接后启动输入流输出流线程来执行命令和获取结果。前端通过websocket的websocket。send(message)发送执行命令至后台@onmessage通过输出流线程在容器内执行,并且执行结果通过输入流响应到web端。注:需设置process.waitfor();这样输入流阻塞时存在有执行命令结果会响应到webshell,同时输出流在向容器写命令时最后需加上“\n”回车,这样容器才会执行命令

web代码(webshell界面代码从网上down,效果体验很好):


` python


 
  
  
  控制台
 



 

后台代码与容器建立进程,启动输入线程和输出线程进行交互:

package com.xxg.websocket;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/webSecureCRT")
public class WebSecureCRT {
    protected Runtime runtime = Runtime.getRuntime();
    protected Process process;
    protected InputStream inputStream;
    protected InputStream errStream;
    public static String termStr = "pp=`whoami`'@'`hostname`':'`pwd`'>' ";
    public String pwd = "";
    protected String termShowStr = "";
    public static String pwdCmdStr = "tt='pwd='`pwd` && echo $tt";
    public static String hostType = "/bin/sh -c"; //unix /usr/bin/ksh
    public static String hhStr = "\n";
    protected Map envMap = System.getenv();
    protected String[] envStrArray;

    public static List cmdList = new ArrayList();

    static {
        cmdList.add("tail");
        cmdList.add("more");
    }

    public WebSecureCRT() {
        super();
        System.out.println("创建WebSecureCRT对象");
    }   
  /**
       * 新的WebSocket请求开启
       */
              @OnOpen
                    public void onOpen(Session session) {
                        String message="pp=`whoami`'@'`hostname` && echo $pp ";
                        try {
                        System.out.println("connect to server start...");
                        String command = "docker exec -i b2873e708667 /bin/bash ";
                        String[] commandArray = {"/bin/sh","-c",command};
                        process =Runtime.getRuntime().exec(commandArray,null);
                        System.out.println(process);
                        onMessage(message, session);

                   } catch (Exception e) {
            e.printStackTrace();
        }
    }


/**
 * WebSocket请求关闭
 */
@OnClose
public void onClose() {
    try {
        if(inputStream != null)
            inputStream.close();
        if(errStream != null)
            errStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    if(process != null)
        process.destroy();
}

@OnError
public void onError(Throwable thr) {
    thr.printStackTrace();
}

/**
 * 收到客户端消息后调用的方法
 * @param message 客户端发送过来的消息
 * @param session 可选的参数
 * @throws ExecutionException 
 * @throws InterruptedException 
 * @throws Exception 
 */
@OnMessage
        public void onMessage(String message, Session session) throws InterruptedException, ExecutionException, Exception {
            System.out.println("from clinet message start [" + message+"]");
            System.out.println(process);
            StreamOutput outGobbler = new StreamOutput(process.getOutputStream(), "OUTPUT",message);  
            outGobbler.start();

    StreamGobbler infoGobbler = new  StreamGobbler(process.getInputStream(), "INPUT",session);  
    infoGobbler.start();

    StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR",session);  
    errorGobbler.start();
    System.out.println("from clinet message end...[" + message+"]");
}

}

输入流线程代码读取流响应到前端:

package com.xxg.websocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javax.websocket.Session;

/**
* 用于处理Runtime.getRuntime().exec产生的错误流及输出流
* @author shaojianye
*
*/
public class StreamGobbler extends Thread {
InputStream is;
String type;
Session session;

StreamGobbler(InputStream is, String type,Session session) { 
    this.is = is; 
    this.type = type; 
    this.session=session;
} 
public void run() { 
    try { 
        InputStreamReader isr = new InputStreamReader(is); 
        BufferedReader reader = new BufferedReader(isr); 
        String line=null; 
        if("INPUT".equals(type)){
            while((line = reader.readLine()) != null) {
                 System.out.println("---------");
                 System.out.println("in:"+line);
                 if(line.length()==0){
                     session.getBasicRemote().sendText("\n"); 
                 }else{
                     session.getBasicRemote().sendText(line);
                }
            }
        }
        if("ERROR".equals(type)){
             while((line = reader.readLine()) != null) {
                 System.out.println("in:"+line);
                 if(line.length()==0){
                     session.getBasicRemote().sendText("\n"); 
                 }else{
                     session.getBasicRemote().sendText(line);
                 }
            }
        }
    } catch (Exception ioe) { 
        ioe.printStackTrace();   
    } 
} 
} 

获取前端传入linux命令通过输出流线程响应到容器

package com.xxg.websocket;

import java.io.OutputStream;
import java.io.PrintWriter;

/**
* 用于处理Runtime.getRuntime().exec产生的错误流及输出流
* @author shaojianye
*
*/
public class StreamOutput extends Thread {
OutputStream out ;
String type;
String message;

StreamOutput(OutputStream out, String type,String message) { 
    this.out = out; 
    this.type = type; 
    this.message=message;
} 
public void run() { 
    try { 
        PrintWriter pw = null;
        String val=message+" \n";
        System.out.println(val);
        //判断exit是否可以退出,待测试
        if (out!=null){
            pw = new PrintWriter(out); 
            pw.println(val);
            pw.flush();
        } 
      } catch (Exception ioe) { 
        ioe.printStackTrace();   
    } 
  } 
} 

部署服务结果如图:

因为执行的docker exec -i containerid /bin/bash命令,没有-t参数(查相关资料-t参数在io流传输是会失效具体原因不太清楚)因此响应的webshell没有虚拟化终端样式
基于websocket和java实现webshell访问docker容器_第1张图片

测试demo在github地址https://github.com/SHAOLBJ/Webshell

访问宿主机的效果实现思路基本一致:

基于websocket和java实现webshell访问docker容器_第2张图片

你可能感兴趣的:(webscoket)