Dear Guacamole Community,

I am currently integrating Apache Guacamole into a custom React-based frontend 
and am encountering an issue while establishing a connection using the 
WebSocket tunnel.

Here is a brief summary of my setup and the problem:

- I am constructing the tunnel URL as follows:
wss://localhost:8443/guacamole/websocket-tunnel?token=85A998473AB30BC6561687D237DDE49DE40D7EF165B238AEC9A45B2F1F8234FF&GUAC_ID=MzYAYwBwb3N0Z3Jlc3Fs&GUAC_TYPE=c?undefined

- The WebSocket successfully connects with status code `101 Switching 
Protocols`, but the following error appears in the Guacamole server logs:
ERROR o.a.g.w.GuacamoleWebSocketTunnelEndpoint - Creation of WebSocket tunnel 
to guacd failed: Illegal identifier - unknown type.

- It seems the issue is due to an improperly formatted `GUAC_TYPE` parameter 
(`c?undefined` instead of just `c`). I suspect that the URL construction on my 
end inadvertently introduced `?undefined`.

```jsx
useEffect(() => {
     const token = localStorage.getItem('token');
    if (!token || !connectionId) {
          console.error('Missing token or connectionId');
          return;
    }
    const cleanConnectionId = String(connectionId).split('?')[0];
    const dataSource = 'postgresql';
    const type = 'c';
    // Encode identifier as per Guacamole format: [id]\x00[type]\x00[dataSource]
    const encodedId = btoa(`${cleanConnectionId}\x00${type}\x00${dataSource}`)
    const tunnelURL = `wss://localhost:8443/guacamole/websocket-tunnel?  
    token=${token}&GUAC_ID=${encodedId}&GUAC_TYPE=${type}`;
    const tunnel = new Guacamole.WebSocketTunnel(tunnelURL);
    const client = new Guacamole.Client(tunnel);
    // Additional setup (display, mouse, keyboard, etc.)
    client.connect();
}, [connectionId]);

I have also attached the pages to understand my code.

I would like to confirm:

  1.
What is the correct and expected format for the `GUAC_ID` and `GUAC_TYPE` 
parameters from the server’s point of view?
  2.
Are there any changes or validations done by the Guacamole server that could 
reject a seemingly correct identifier?
  3.
Is there a recommended way to debug or log the parsed identifier as received by 
the server for better visibility?

Any clarification or guidance would be greatly appreciated.

Thank you for your time and for maintaining such a powerful remote access tool.

Best regards,
Rafi Shaik
[email protected]
import React from 'react';
import { useParams } from 'react-router-dom';
import GuacClientPanel from '../../shared/utils/guacamoleClient';

const GuacViewerPage = () => {
  const { connectionId: rawConnectionId } = useParams();

  const connectionId = rawConnectionId?.split('?')[0];

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <GuacClientPanel connectionId={connectionId} />
    </div>
  );
};

export default GuacViewerPage;
import React, { useEffect, useRef } from 'react';
import Guacamole from 'guacamole-common-js';
import { encodeGuacClientIdentifier } from './guacEncode';

const GuacClientPanel = ({ connectionId }) => {
  const displayRef = useRef(null);
  const guacClientRef = useRef(null);

  useEffect(() => {
    const token = localStorage.getItem('token');
    if (!token || !connectionId) {
      console.error('Missing token or connectionId');
      return;
    }

    const cleanConnectionId = String(connectionId).split('?')[0];
    const dataSource = 'postgresql';
    const type = 'c'; // Guacamole expects this for a connection
    const encodedId = `${dataSource}:${type}:${cleanConnectionId}`;
    //const encodedId = encodeGuacClientIdentifier('postgresql', 'c', `${cleanConnectionId}`)
    const baseWsURL = 'wss://localhost:8443/guacamole/websocket-tunnel';

    // Construct WebSocket URL with proper parameters
    const wsParams = new URLSearchParams({
      token: token,
      GUAC_ID: encodedId,
      GUAC_TYPE: type,
    });

    const tunnelURL = `${baseWsURL}?${wsParams.toString()}`;

    console.log('Tunnel URL:', tunnelURL);

    try {
      const tunnel = new Guacamole.WebSocketTunnel(tunnelURL);
      const client = new Guacamole.Client(tunnel);
      guacClientRef.current = client;

      // Setup display
      const display = client.getDisplay().getElement();
      displayRef.current.innerHTML = '';
      displayRef.current.appendChild(display);

      client.connect();

      // Setup input listeners
      const mouse = new Guacamole.Mouse(display);
      mouse.onEach(['mousedown', 'mouseup', 'mousemove'], (e) => {
        client.sendMouseState(e);
      });

      const keyboard = new Guacamole.Keyboard(document);
      keyboard.onkeydown = (key) => client.sendKeyEvent(1, key);
      keyboard.onkeyup = (key) => client.sendKeyEvent(0, key);

      client.onstatechange = (state) => {
        console.log('Guacamole state:', state);
      };

      client.onerror = (err) => {
        console.error('Guacamole client error:', err);
      };

      return () => {
        client.disconnect();
        displayRef.current.innerHTML = '';
      };
    } catch (error) {
      console.error('Tunnel setup failed:', error);
    }
  }, [connectionId]);

  return (
    <div>
      <h3>SSH Remote Terminal</h3>
      <div
        ref={displayRef}
        style={{
          width: '100%',
          height: '600px',
          background: '#000',
          overflow: 'hidden',
          border: '1px solid #444',
          borderRadius: '6px',
        }}
      />
    </div>
  );
};

export default GuacClientPanel;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to