vadimpronin / guacamole-lite

Node.js library for creating Guacamole-compatible servers. Guacamole is a RDP/VNC/SSH/Telnet client for HTML5 browsers.
Apache License 2.0
250 stars 78 forks source link

Getting Token validation failed, Closing connection with error: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined #43

Open manrajparmar53 opened 2 years ago

manrajparmar53 commented 2 years ago

@fredericosilva @luiso1979 @thelamer @vadimpronin @dnapier I am getting below error in backend code: node index.js [MyLog] Starting guacamole-lite websocket server [MyLog] [2021-11-29 09:57:27] [Connection 1] Client connection open [MyLog] [2021-11-29 09:57:27] [Connection 1] Token validation failed [MyLog] [2021-11-29 09:57:27] [Connection 1] Closing connection with error: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined at Function.from (buffer.js:331:9) at Function.base64decode (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Crypt.js:24:23) at Crypt.decrypt (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Crypt.js:12:39) at ClientConnection.decryptToken (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/ClientConnection.js:62:22) at new ClientConnection (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/ClientConnection.js:25:44) at Server.newConnection (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Server.js:149:59) at WebSocketServer.emit (events.js:315:20) at /Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:91:14 at completeHybiUpgrade2 (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:284:5) at completeHybiUpgrade1 (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:306:13) { code: 'ERR_INVALID_ARG_TYPE' } [MyLog] [2021-11-29 09:57:27] [Connection 1] Client connection closed

Backend code: ```const GuacamoleLite = require('guacamole-lite'); const express = require('express'); const http = require('http'); const app = express(); // const server = http.createServer(app); const websocketOptions = { port: 8080 // we will accept connections to this port };

const guacdOptions = { port: 4822 // port of guacd };

const clientOptions = { crypt: { cypher: 'AES-256-CBC', key: 'MySuperSecretKeyForParamsToken12' }, log: { level: 'DEBUG', stdLog: (...args) => { console.log('[MyLog]', ...args) }, errorLog: (...args) => { console.error('[MyLog]', ...args) } } };

// const guacServer = new GuacamoleLite({server}, guacdOptions, clientOptions); const guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions);

guacServer.on('open', (clientConnection) => { console.error("Guac Server call open on event"); // const url = 'http://our-backend-server/api/connection/open?userId=' + clientConnection.connectionSettings.userId // Http.request(url).end(); }); guacServer.on('close', (clientConnection) => { console.error("Guac Server call close on event"); // const url = 'http://our-backend-server/api/connection/close?userId=' + clientConnection.connectionSettings.userId // Http.request(url).end(); }); guacServer.on('error', (clientConnection, error) => { console.error("Guac Server error: ",clientConnection, error); });

// var serverPort = process.env.YOUR_PORT || process.env.PORT || 5000; // server.listen(serverPort, () => { // console.log("Started on : "+ serverPort); // })```

Frontend code: `import logo from './logo.svg'; import './App.css'; import Guacamole from "guacamole-common-js"; import { useEffect } from 'react';

function App() { useEffect(() => { let guaca = new Guacamole.Client(new Guacamole.WebSocketTunnel('ws://localhost:8080/?token=eyJjb25uZWN0aW9uIjp7InR5cGUiOiJyZHAiLCJzZXR0aW5ncyI6eyJob3N0bmFtZSI6ImxvY2FsaG9zdCIsInVzZXJuYW1lIjoiTWFucmFqIFBhcm1hciIsInBhc3N3b3JkIjoiMTIzNCIsImVuYWJsZS1kcml2ZSI6dHJ1ZSwiY3JlYXRlLWRyaXZlLXBhdGgiOnRydWUsInNlY3VyaXR5IjoiYW55IiwiaWdub3JlLWNlcnQiOnRydWUsImVuYWJsZS13YWxscGFwZXIiOmZhbHNlfX19&width=1024&height=768&dpi=32')); // let guaca = new Guacamole.Client(new Guacamole.WebSocketTunnel(ws://localhost:8080?token=${JSON.stringify({"connection":{"type":"rdp","settings":{"hostname":"localhost","username":"Manraj Parmar","password":"1234","enable-drive":true,"create-drive-path":true,"security":"any","ignore-cert":true,"enable-wallpaper":false}}})})); guaca.onerror = function (error) { alert(error); }; guaca.connect(); // Disconnect on close window.onunload = function () { guaca.disconnect(); } let display = document.getElementById("display"); display.appendChild(guaca.getDisplay().getElement()); }, [])

return (

Test

); }

export default App; ` Can you please have a look and help me on it. Might be only a small mistake in socket url in frontend code, something missing or wrong in token, so please check and help. please drop a comment.

manrajparmar53 commented 2 years ago

@fredericosilva @luiso1979 @thelamer @vadimpronin after using encrypted token using below code, I am getting another error:

Encrypt token code: var encrypt = (value) => { var iv = crypto.randomBytes(16); var cipher = crypto.createCipheriv(clientOptions.crypt.cypher, clientOptions.crypt.key, iv); let crypted = cipher.update(JSON.stringify(value), 'utf8', 'base64'); crypted += cipher.final('base64'); var data = { iv: iv.toString('base64'), value: crypted }; return new Buffer.from(JSON.stringify(data)).toString('base64'); };

Error: Error: connect ECONNREFUSED 127.0.0.1:4822 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1141:16) { errno: -61, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 4822 } [MyLog] [2021-11-29 18:24:35] [Connection 1] Closing connection with error: Error: connect ECONNREFUSED 127.0.0.1:4822 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1141:16) { errno: -61, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 4822 } [MyLog] [2021-11-29 18:24:35] [Connection 1] Closing guacd connection Guac Server call close on event [MyLog] [2021-11-29 18:24:35] [Connection 1] Client connection closed

please have a look on it and help me to fix it.

m-expunged commented 2 years ago

@manrajparmar53 Did you figure out the reason?

Edit: Had the same error, only happens when I run the guacamole-lite server in a docker container. If i run gaucd in docker and the guacamole-lite server as a regular application it works.

MarkSpencerTan commented 2 years ago

@m-expunged @manrajparmar53 I think this is a bug on guacamole-lite somewhere if you try to include the token with your websocket tunnel URL like so:

let tunnel = new Guacamole.WebSocketTunnel('ws://localhost:8080/?token=MYTOKEN');

I logged what the guacamole-lite was parsing out of line 19 in ClientConnection.js and saw an unusual behavior where the this.query variable had a ?undefined at the end of the token payload:

# line 19 of ClientConnection.js
this.query = Url.parse(this.webSocket.upgradeReq.url, true).query;
console.log(this.query)

output:

debug [Object: null prototype] {
  token: 'eyJpdi...?undefined' 
}

this is causing issues when it gets passed to crypto.decrypt() call since it makes the token an invalid base64 string. I'm not sure what's appending the '?undefined', but one fix I can think of is sanitizing it out of the token before it gets parsed.

As a workaround though in the front-end code or client code you can do this instead of passing the token query parameter as part of the websocket URL:

let tunnel = new Guacamole.WebSocketTunnel('ws://localhost:8080/');
let client = new Guacamole.Client(this.tunnel);
client.connect('token='+ MYTOKEN);

For some reason, this approach doesn't append the problematic ?undefined at the end of the token. I saw this example in the ClientExample.jsx file in the root of the project. I hope it works for you guys!

m-expunged commented 2 years ago

Hi, @MarkSpencerTan

Your solution is the correct way of opening a connection! Sadly, I have already been doing it that way, my problem was caused by something else.

No worries tho. Since I couldn't get guacamole-lite to work the way I wanted it to, I went a different route and wrote a C#/Docker replacement for the guacamole-client. If anyone's curious it's on my profile.