UPDATED: I have created an all-in-one (AIO) version that includes nginx using TLS.
Apache Guacamole is a client-less remote desktop gateway. I use it in order to access my lab when traditional methods are not available. Guacamole does not use agents or fancy plugins, you only need an HTML5 supported browser and you can access your desktop or server in the cloud.
I started with version 0.9.14 of Guacamole. When version 1.0 was released, I decided to move away from Chase Wright’s all-in-one script and roll my own micro-services docker setup. This blog post covers the steps I took to deploy a containerized guacamole setup.
Prerequisites:
The first step was to get a backup and secure it somewhere safe:
mysqldump -u root -p --add-drop-table guacamole_db > guacamole_db.sql
We also need to get the database initialization schema. The database backup we just made is from version 0.9.14 so I wrote a script to get that version of the initialization schema from the guacamole repositories, but you can also use docker and the correct tag:
sudo docker run --rm guacamole/guacamole:0.9.14 /opt/guacamole/bin/initdb.sh --mysql > v0.9.14-initdb.sql
In addition to the initialization schema we will also need the applicable upgrade schema. If you are using a version of guacamole older than 0.9.14 then also copy the intermediate upgrades scripts.
wget -q https://archive.apache.org/dist/guacamole/1.0.0/binary/guacamole-auth-jdbc-1.0.0.tar.gz tar xf guacamole-auth-jdbc-1.0.0.tar.gz cp guacamole-auth-jdbc-1.0.0/mysql/schema/upgrade/upgrade-pre-1.0.0.sql .
At this point you should have a backup of the database, the initialization schema, and the upgrade schema(s).
Next create the persistent volume for your database:
sudo docker volume create db mech@labs(~):$ sudo docker volume ls DRIVER VOLUME NAME local db
Pull and launch the database docker image:
sudo docker run -e MYSQL_ROOT_PASSWORD=ladybug --name db -h db --mount source=db,target=/var/lib/mysql -d --rm mariadb:10.0
Verify the image is running (the output is abbreviated):
mech@labs(~):$ docker ps -l CONTAINER ID IMAGE PORTS NAMES bbcf0a744087 mariadb:10.0 3306/tcp db
Copy the backup database, the schema initialization file, and the applicable schema upgrade script(s) to the database container:
mech@labs(~):$ ls *.sql guacamole_db.sql upgrade-pre-1.0.0.sql v0.9.14-initdb.sql mech@labs(~):$ for i in *.sql; do sudo docker cp $i db:/tmp ; done
Enter the database container and configure the database:
sudo docker exec -it db bash roo@db:/# mysql -u root -p Enter password: MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | +--------------------+ 3 rows in set (0.00 sec) MariaDB [(none)]> create database guacamole_db; MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | guacamole_db | | information_schema | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec) MariaDB [(none)]> use guacamole_db; MariaDB [guacamole_db]> system ls /tmp guacamole_db.sql upgrade-pre-1.0.0.sql v0.9.14-initdb.sql
The following steps MUST be done in order:
1. Load the initialization schema.
2. Load the backup (mysqldump).
3. Load the schema upgrade script(s).
Note: If you have multiple upgrade schemas, they must be loaded individually and sequentially.
MariaDB [guacamole_db]> source /tmp/v0.9.14-initdb.sql MariaDB [guacamole_db]> source /tmp/guacamole_db.sql MariaDB [guacamole_db]> source /tmp/upgrade-pre-1.0.0.sql
Exit and stop the container.
sudo docker stop db
Create a project directory and support files:
mkdir guacamole && cd guacamole touch mysql-root guacamole-user touch .env docker-compose.yml
Warning: The following instructions employ credentials in clear-text on the file system. The use of docker secrets without a docker swarm is a workaround for development and testing. I would not recommend using this configuration in production without the proper mechanisms to protect the credentials. I am using this in a lab environment and have made some risk decisions specific to my use case. Credentials using environment variables are also easily leaked using docker inspect for any user with admin access to the docker command.
Edit the guacamole-user file so that it contains the password for the database user guacamole_user. Then edit the mysql-root file so it has the MySQL root user password.
The .env file is used to set environment variables that are referenced in the docker-compose.yml. If you look at line 46 below we are using the environment variable ${MYSQL_PASSWORD}. In order for docker compose to assign a value to this variable you will need to edit the .env file so it has a matching key=value pair. For instance: MYSQL_PASSWORD=cyb3rsecr3t
Note: The guacamole docker image does not support using secrets with the MYSQL_PASSWORD environment variable
This docker compose file will pull, launch and link mariadb, guacd, and guacamole:
## Guacamole 20190318 # @c0demech version: "3.1" services: # Configure MariaDB with persistent storage database: image: mariadb:10.0 hostname: db restart: always environment: MYSQL_ROOT_PASSWORD_FILE: '/run/secrets/mysql-root' MYSQL_DATABASE: 'guacamole_db' MYSQL_USER: 'guacamole_user' MYSQL_PASSWORD: '/run/secrets/guacamole-user' container_name: db volumes: - "db:/var/lib/mysql" secrets: - mysql-root - guacamole-user # Configure proxy daemon guacd: image: guacamole/guacd:1.0.0 container_name: guacd restart: always # Configure and link Guacamole to db and proxy # expose port 8080 to host. guacamole: image: guacamole/guacamole:1.0.0 container_name: guacamole restart: always ports: - 8080:8080 links: - guacd - database environment: GUACD_HOSTNAME: guacd MYSQL_HOSTNAME: db MYSQL_DATABASE: guacamole_db MYSQL_USER: guacamole_user MYSQL_PASSWORD: ${MYSQL_PASSWORD} secrets: - guacamole-user secrets: mysql-root: file: mysql-root guacamole-user: file: guacamole-user # This volume has to be created before docker-compose up volumes: db: external: true
We need to constrain the permissions of our sensitive files:
mech@labs(guacamole):$ pwd /home/mech/guacamole mech@labs(guacamole):$ ls -A docker-compose.yml .env guacamole-user mysql-root mech@labs(guacamole):$ chmod 600 .env mech@labs(guacamole):$ chmod 600 *
Remember to stop the previous running version of our database, guacd and guacamole. Once that is complete, run the following command:
mech@labs(guacamole):$ sudo /usr/local/bin/docker-compose up -d Creating network "guacamole_default" with the default driver Creating db ... done Creating guacd ... done Creating guacamole ... done
Finally, we check to see if our containers are running:
mech@labs(guacamole):$ sudo docker container ps CONTAINER ID IMAGE PORTS NAMES 72004c94f178 guacamole/guacamole:1.0.0 0.0.0.0:8080->8080/tcp guacamole d32546a0c1eb guacamole/guacd:1.0.0 4822/tcp guacd 8148f6916235 mariadb:10.0 3306/tcp db
Browse to your guacamole web address using port 8080 or adjust accordingly if you have a reverse proxy. Login with your credentials an if everything went well, remember to disable the startup of the old services. It is worth mentioning that I did not have to modify my reverse proxy. This is because port 8080 is exposed to the host by the guacamole container.
If you want to also use an apache or nginx docker container with this setup, it is fairly easy to adapt my docker-compose.yml above.
Thanks for reading.
How do you handle version upgrades (like from 1.0 to 1.1)?
@reedog117, I usually check the release notes to see if there are any special caveats such as upgrading the database schema, then I create a backup of my database followed by changing the version number in the docker-compose.yml for guacd and guacamole.
Before:
image: guacamole/guacd:1.0.0
image: guacamole/guacamole:1.0.0
After:
image: guacamole/guacd:1.1.0
image: guacamole/guacamole:1.1.0
This is one of the major reasons I switched to using containers and created my guacamole-docker-compose repo on GitHub.