【问题标题】:how to send commands to AWS Session manager websocket url using xterm.js?如何使用 xterm.js 向 AWS 会话管理器 websocket url 发送命令?
【发布时间】:2021-01-19 12:48:24
【问题描述】:

我有一个由 AWS 创建的 websocket url。 URL 由 aws ssm start session 使用 .net sdk 创建。 开始会话方法给了我 streamUrl、令牌和会话 ID。 网址格式如下:

wss://ssmmessages.ap-south-1.amazonaws.com/v1/data-channel/sessionidhere?role=publish_subscribe

在“sessionidhere”位置有我无法共享的实际会话 ID。

我想使用xterm.js 在网络上打开终端。我读过 xterm.js 可以连接到 websocket URL,发送消息和接收输出。

我的 javascript 代码在这里:

<!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>

现在,会话正在打开,我收到已建立连接的警报。它正在成功连接,但是每当我尝试发送命令时,都会通过说“打开数据通道的请求不包含令牌”来关闭连接。我尝试通过 3 种方式发送命令。

首先是:

ws.send("ls")

秒:

let data = {
    method: "command", command: curr_line
}
ws.send(JSON.stringify(data));

但面临同样的错误,即打开数据通道的请求不包含令牌,连接死了

第三个:

var enc = new TextEncoder("utf-8"); 
var data = "ls";           
ws.send(enc.encode(data));

第三,我没有收到任何错误,但也没有得到输出...有人可以帮忙吗?

【问题讨论】:

  • 有人可以帮忙吗?

标签: javascript amazon-web-services websocket aws-ssm xtermjs


【解决方案1】:

AWS Session manager 使用的协议包括以下内容:

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

从这一刻起,协议不再是 JSON。它在官方的Amazon SSM agent 中实现,如果您想从 AWS CLI 启动 SSM 会话,这是必需的。有效载荷必须根据this binary format发送和接收

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

浏览器可用的示例代码使用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

给你:

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

然后提供示例应用程序:

npm install http-server -g
http-server -a localhost -p 3000

去http://localhost:3000/test/web,输入websocket URI和token:

sample code for browser

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);
  });
}

【讨论】:

  • 嗨@Bertrand Martel,非常感谢您的回答。实施方法和您的解释看起来非常好且内容丰富。我正在尝试在我的代码中实现这一点。
  • @Jass 你也可以看到this aws admin project 这是我开发这个库的项目。这是一个 AWS 仪表板,使用设计为在本地使用的 api,在 SSM 部分中,您可以使用“ssm-session”库在浏览器中启动会话
  • 是的,@Bertrand Martel 我会看看的。但是在上面的代码中,导入对我不起作用。在浏览器中导入还有其他选择吗?
  • @Jass 你可以使用import {ssm} from "../../src/index.js" 检查示例的源代码here 在脚本中指定type="module"。您将需要获取源代码或使用转译器
  • 好的@Bertrand Martel,我可以从 index.js 导入 ssm。但是在ssm。 js 它给了我一个错误,你不能在模块之外导入。而在 ssm.js 中,我不能使用脚本标签来定义模块
猜你喜欢
  • 1970-01-01
  • 2020-10-13
  • 2010-12-03
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 2019-02-02
  • 1970-01-01
相关资源
最近更新 更多