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
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>
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")
second:
let data = {
method: "command", command: curr_line
}
ws.send(JSON.stringify(data));
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));
For third, I'm not getting any error, but not getting output too... Can someone please help?
The protocol used by AWS Session manager consists of the following :
From this moment the protocol is not JSON anymore. It is implemented in the offical Amazon SSM agent which is required if you want start a SSM session from the AWS CLI. The payload must be sent & receive according this binary format
I had exactly the same requirement as you few months ago so I've made an AWS Session manager client library : https://github.com/bertrandmartel/aws-ssm-session for nodejs and browser. If you want more information about how the protocol works, checkout this
The sample code available for browser use xterm.js
First clone the project and generate websocket URL and token using aws-api with some utility script :
which gives you :
Then serve the sample app :
go to http://localhost:3000/test/web, enter the websocket URI and token :
The sample code for browser :