how to send commands to AWS Session manager websocket url using xterm.js?

Jas*_*ass 2 javascript amazon-web-services websocket xtermjs aws-ssm

I have a websocket url created by AWS. URL is created by aws ssm start session using .net sdk. Start session method gives me streamUrl, token and session ID. URL is in following format:

wss://ssmmessages.ap-south-1.amazonaws.com/v1/data-channel/sessionidhere?role=publish_subscribe
Run Code Online (Sandbox Code Playgroud)

There is actual session id at placeof "sessionidhere" that I can not share.

I want to open terminal on web using xterm.js. I've read that xterm.js can connect to websocket URL, send messages and receive outputs.

My javascript code is here :

<!doctype html>
<html>
<head>
    <link href="~/xterm.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-3.4.1.js"></script>
    <script src="~/Scripts/bootstrap.js"></script>
    <script src="~/xterm.js"></script>
</head>
<body>
    <div id="terminal"></div>
    <script type="text/javascript">
        var term = new Terminal({
            cursorBlink: "block"
        });
        var curr_line = "";
        var entries = [];
        term.open(document.getElementById('terminal'));    
        const ws = new WebSocket("wss://ssmmessages.ap-south-1.amazonaws.com/v1/data-channel/sessionid?role=publish_subscribe?token=tokenvalue");
        var curr_line = "";
        var entries = [];
      
        term.write("web shell $ ");

        term.prompt = () => {
            if (curr_line) {
                let data = {
                    method: "command", command: curr_line
                }
                ws.send(JSON.stringify(data));
            }
        };
        term.prompt();
        ws.onopen = function (e) {
            alert("[open] Connection established");
            alert("Sending to server");         
            var enc = new TextEncoder("utf-8"); // always utf-8
            // console.log(enc.encode("This is a string converted to a Uint8Array"));
            var data = "ls";
            console.log(enc.encode(data));
            alert(enc.encode(data));
            ws.send(enc.encode(data));
            alert(JSON.stringify(e));
        };
        ws.onclose = function (event) {
            if (event.wasClean) {
                alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
            } else {
                // e.g. server process killed or network down
                // event.code is usually 1006 in this case
                alert('[close] Connection died');
            }
        };

        ws.onerror = function (error) {
            alert(`[error] ${error.message}`);
        };

        // Receive data from socket
        ws.onmessage = msg => {
            alert(data);
            term.write("\r\n" + JSON.parse(msg.data).data);
            curr_line = "";
        };

        term.on("key", function (key, ev) {
            //Enter
            if (ev.keyCode === 13) {
                if (curr_line) {
                    entries.push(curr_line);
                    term.write("\r\n");
                    term.prompt();
                }
            } else if (ev.keyCode === 8) {
                // Backspace
                if (curr_line) {
                    curr_line = curr_line.slice(0, curr_line.length - 1);
                    term.write("\b \b");
                }
            } else {
                curr_line += key;
                term.write(key);
            }
        });

        // paste value
        term.on("paste", function (data) {
            curr_line += data;
            term.write(data);
        });
    </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

Now, the session is being opened, I am getting alert of connection established. It's being successful connection, but whenever I try to send commands, the connection is being closed by saying 'request to open data channel does not contain a token'. I've tried to send command in 3 ways.

First is :

ws.send("ls")
Run Code Online (Sandbox Code Playgroud)

second:

let data = {
    method: "command", command: curr_line
}
ws.send(JSON.stringify(data));
Run Code Online (Sandbox Code Playgroud)

But facing same error i.e. request to open data channel does not contain token, connection died

third:

var enc = new TextEncoder("utf-8"); 
var data = "ls";           
ws.send(enc.encode(data));
Run Code Online (Sandbox Code Playgroud)

For third, I'm not getting any error, but not getting output too... Can someone please help?

Ber*_*tel 7

AWS 会话管理器使用的协议包括以下内容:

  • 在流 URL 上打开一个 websocket 连接
  • 发送由以下字符串化的 JSON 组成的身份验证请求:
{
  "MessageSchemaVersion": "1.0",
  "RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "TokenValue": "<YOUR-TOKEN-VALUE>"
}
Run Code Online (Sandbox Code Playgroud)

从这一刻起,协议不再是 JSON。它在官方Amazon SSM 代理中实施,如果您想从 AWS CLI 启动 SSM 会话,则需要该代理。必须根据此二进制格式发送和接收有效负载

几个月前我和你有完全相同的要求,所以我为 nodejs 和浏览器制作了一个 AWS 会话管理器客户端库:https : //github.com/bertrandmartel/aws-ssm-session。如果您想了解有关该协议如何工作的更多信息,请查看

浏览器可用的示例代码使用 xterm.js

首先克隆项目并使用 aws-api 和一些实用程序脚本生成 websocket URL 和令牌:

git clone git@github.com:bertrandmartel/aws-ssm-session.git
cd aws-ssm-session
npm i
npm run build
node scripts/generate-session.js
Run Code Online (Sandbox Code Playgroud)

这给了你:

{
  SessionId: 'xxxxxx-xxxxxxxxxxxxxx',
  TokenValue: 'YOUR_TOKEN',
  StreamUrl: 'wss://ssmmessages.eu-west-3.amazonaws.com/v1/data-channel/user-xxxxxxxxxxxxxx?role=publish_subscribe'
}
Run Code Online (Sandbox Code Playgroud)

然后提供示例应用程序:

npm install http-server -g
http-server -a localhost -p 3000
Run Code Online (Sandbox Code Playgroud)

转到 http://localhost:3000/test/web,输入 websocket URI 和令牌:

在此处输入图片说明

对浏览器的代码示例

import { ssm } from "ssm-session";

var socket;
var terminal;

const termOptions = {
  rows: 34,
  cols: 197
};

function startSession(){
  var tokenValue = document.getElementById("tokenValue").value;
  var websocketStreamURL = document.getElementById("websocketStreamURL").value;
  
  socket = new WebSocket(websocketStreamURL);
  socket.binaryType = "arraybuffer";
  initTerminal()

  socket.addEventListener('open', function (event) {
    ssm.init(socket, {
      token: tokenValue,
      termOptions: termOptions
    });
  });
  socket.addEventListener('close', function (event) {
    console.log("Websocket closed")
  });
  socket.addEventListener('message', function (event) {
    var agentMessage = ssm.decode(event.data);
    //console.log(agentMessage);
    ssm.sendACK(socket, agentMessage);
    if (agentMessage.payloadType === 1){
      terminal.write(agentMessage.payload)
    } else if (agentMessage.payloadType === 17){
      ssm.sendInitMessage(socket, termOptions);
    }
  });
}

function stopSession(){
  if (socket){
    socket.close();
  }
  terminal.dispose()
}

function initTerminal() {
  terminal = new window.Terminal(termOptions);
  terminal.open(document.getElementById('terminal'));
  terminal.onKey(e => {
    ssm.sendText(socket, e.key);
  });
  terminal.on('paste', function(data) {
    ssm.sendText(socket, data);
  });
}
Run Code Online (Sandbox Code Playgroud)