Apache Guacamole

From their home page:

Apache Guacamole is a clientless remote desktop gateway.

It’s pretty cool, you can configure RDP, SSH, or VNC connections in guacamole and then from a browser you can connect to any of the configured connections.

Deploy Guacamole in Docker

There are nice instructions on how to configure guacamole in docker. So let’s go that route.

Configure MariaDB

The best thing to do is to generate a MySQL/MariaDB install script. This is accomplished by running the following on your docker host:

<> docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql > initdb.sql

Now from a machine that has access to your MariaDB server, create the database and user for guacamole:

<> mysql -u root -p -h ub
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 9454
Server version: 10.2.10-MariaDB-10.2.10+maria~jessie mariadb.org binary distribution

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE guacamole;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> CREATE USER 'guac' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE ON guacamole.* TO 'guac'@'%';
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

MariaDB [(none)]> quit

Now that the database and user are ready, let’s populate the db:

<> mysql guacamole -u guac -pguacamole -h ub < initdb.sql

Create a docker-compose configs

I just ended up creating the following config:

<> cat docker-compose.yml
version: "2"
    image: "guacamole/guacd"
    container_name: guacd
    hostname: guacd
    restart: always
      - "/data/shared/guacamole/guacd/data:/data"
      - "/data/shared/guacamole/guacd/conf:/conf:ro"
      - "4822"
      - "4822:4822"
    network_mode: bridge

    image: "guacamole/guacamole"
    container_name: guacamole
    hostname: guacamole
    restart: always
      - "/data/shared/guacamole/guacamole/guac-home:/data"
      - "/data/shared/guacamole/guacamole/conf:/conf:ro"
      - "8080"
      - "8084:8080"
    network_mode: bridge
      - "GUACD_HOSTNAME=ub.kar.int"
      - "GUACD_PORT=4822"
      - "MYSQL_PORT=3306"
      - "MYSQL_DATABASE=guacamole"
      - "MYSQL_USER=guac"
      - "MYSQL_PASSWORD=password"
      - "GUACAMOLE_HOME=/data"

At this point if we run docker-compose up -d the services should come up and if we go to http://{DOCKER_HOST}:8084/guacamole/ you will see the login page:


And you can login with the default credentials:


Change that as soon as you login. After that you can create all the connections you want: RDP, SSH, and VNC.

Putting Guagamole behind a Reverse Proxy

I have done this in tha past with splunk and kibana. So I already had an apache server running on Debian and luckily it was version 2.4:

<> apache2ctl -v
Server version: Apache/2.4.25 (Debian)
Server built:   2017-09-19T18:58:57

which supports ws_proxy. All of the proxy configurations are covered in Chapter 4. Proxying Guacamole. Without ws-proxy, I kept getting disconnects on the RDP connections and I saw the following in the logs:

<> docker-compose logs -f
guacd        | guacd[1]: INFO:	Connection "$5ae57e16-799e-4ad3-803d-69d0d1185e20" removed.
guacd        | guacd[1]: INFO:	Creating new client for protocol "rdp"
guacd        | guacd[1]: INFO:	Connection ID is "$ba54551e-d8ca-4891-a7ee-65d2883609f5"
guacd        | guacd[90]: INFO:	Security mode: ANY
guacd        | guacd[90]: INFO:	Resize method: none
guacd        | guacd[90]: INFO:	User "@a681f5ff-0055-427c-8245-9ba428242edf" joined connection "$ba54551e-d8ca-4891-a7ee-65d2883609f5" (1 users now present)
guacd        | guacd[90]: INFO:	Loading keymap "base"
guacd        | guacd[90]: INFO:	Loading keymap "en-us-qwerty"
guacamole    | 03:16:01.955 [http-nio-8080-exec-3] INFO  o.a.g.tunnel.TunnelRequestService - User "elatov" connected to connection "1".
guacd        | guacd[90]: INFO:	guacdr connected.
guacd        | guacd[90]: INFO:	guacsnd connected.
guacd        | guacd[90]: INFO:	User "@a681f5ff-0055-427c-8245-9ba428242edf" disconnected (0 users remain)
guacd        | guacd[90]: INFO:	Last user of connection "$ba54551e-d8ca-4891-a7ee-65d2883609f5" disconnected
guacamole    | 03:16:34.946 [http-nio-8080-exec-8] INFO  o.a.g.tunnel.TunnelRequestService - User "elatov" disconnected from connection "1". Duration: 32990 milliseconds
guacamole    | Exception in thread "Thread-25" java.lang.IllegalStateException: Message will not be sent because the WebSocket session has been closed
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:381)
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:338)
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$TextMessageSendHandler.write(WsRemoteEndpointImplBase.java:730)
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:250)
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendString(WsRemoteEndpointImplBase.java:193)
guacamole    | 	at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:37)
guacamole    | 	at org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint$2.run(GuacamoleWebSocketTunnelEndpoint.java:167)
guacd        | guacd[90]: INFO:	Internal RDP client disconnected
guacd        | connected to gen.kar.int:9000
guacd        | connected to gen.kar.int:9000
guacd        | connected to gen.kar.int:9000
guacd        | guacd[1]: INFO:	Connection "$ba54551e-d8ca-4891-a7ee-65d2883609f5" removed.

So on the Debian machine first enable the ws_proxy module:

<> sudo a2enmod proxy_wstunnel
Considering dependency proxy for proxy_wstunnel:
Module proxy already enabled
Enabling module proxy_wstunnel.
To activate the new configuration, you need to run:
  systemctl restart apache2

Then create the following config:

<> cat /etc/apache2/conf-enabled/guac-proxy.conf
<Location /guacamole/>
    Order allow,deny
    Allow from all
    ProxyPass flushpackets=on

<Location /guacamole/websocket-tunnel>
    Order allow,deny
    Allow from all
    ProxyPass ws://
    ProxyPassReverse ws://

Then connecting over the proxy worked with out issues: