Description
[ ↑ ]
Apache Guacamole is a clientless remote desktop gateway. It supports standard protocols like VNC, RDP, and SSH. It is called clientless because no plugins or client software are required. Thanks to HTML5, once Guacamole is installed on a server, all you need to access your desktops is a web browser.
It supports RDP, SSH, Telnet and VNC and is the fastest HTML5 gateway I know. Checkout the projects
homepage for more information.
This is a small documentation how to run a fully working Apache Guacamole instance behind an nginx reverse proxy using docker-compose.
Hint:
For the advanced users just download this
file, extract it and fire up docker-compose within the extracted folder.
Installing docker and docker-compose
[ ↑ ]
docker-compose CentOS: this works well!
curl -L https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
Make sure you use the latest versions. I had big problems with old versions and their bugs!
Files
[ ↑ ]
All necessary files are bundled within
guacamole-compose.tar.gz. It holds the following directory structure:
.
-reset
-docker-compose.yml
-init
\initdb.sql
-nginx
\mysite.template
\nginx.conf
Note:
If you use the files from the bundle you should read the comments inside docker-compose.yml
and re-create the initdb.sql
file by executing docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > ./init/initdb.sql
because it could be outdated when you read this document.
reset
A small bash script that resets the folder that holds the above files to its original state. **Be careful using it, it will delete all folders and files that were not initially delivered with guacamole-compose.tar.gz
. It looks like this:
#!/bin/bash
echo "This will delete your existing database (./data/)"
echo " delete your recordings (./record/)"
echo " delete your drive files (./drive/)"
echo " delete your certs files (./nginx/ssl/)"
echo ""
read -p "Are you sure? " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
chmod +x init
sudo rm -r -f ./data/ ./drive/ ./record/ ./nginx/ssl/
fi
docker-compose.yml
A standard docker-compose version 2 yaml-file that docker-compose
uses to to init and run guacd
, postgres
, guacamole
and nginx
with the following content:
####################################################################################
# docker-compose file for Apache Guacamole
# created by PCFreak 2017-06-28
#
# Apache Guacamole is a clientless remote desktop gateway. It supports standard
# protocols like VNC, RDP, and SSH. We call it clientless because no plugins or
# client software are required. Thanks to HTML5, once Guacamole is installed on
# a server, all you need to access your desktops is a web browser.
####################################################################################
#
# What does this file do?
#
# Using docker-compose it will:
#
# - create a network 'guacnetwork' with the 'bridge' driver.
# - create a service 'guacd' from 'guacamole/guacd' connected to 'guacnetwork'
# - create a service 'postgres' (1) from 'postgres' connected to 'guacnetwork'
# - create a service 'guacamole'(2) from 'guacamole/guacamole/' conn. to 'guacnetwork'
# - create a service 'nginx' (3) from 'nginx' connected to 'guacnetwork'
#
# (1)
# DB-Init script is in './init/initdb.sql' it has been created executing
# 'docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > ./init/initdb.sql'
# once.
# DATA-DIR is in './data'
# If you want to change the DB password change all lines with 'POSTGRES_PASSWORD:' and
# change it to your needs before first start.
# To start from scratch delete './data' dir completely
# './data' will hold all data after first start!
# The initdb.d scripts are only executed the first time the container is started
# (and the database files are empty). If the database files already exist then the initdb.d
# scripts are ignored (e.g. when you mount a local directory or when docker-compose saves
# the volume and reuses it for the new container).
#
# !!!!! MAKE SURE your folder './init' is executable (chmod +x ./init)
# !!!!! or 'initdb.sql' will be ignored!
#
# (2)
# Make sure you use the same value for 'POSTGRES_USER' and 'POSTGRES_PASSWORD'
# as configured under (1)
#
# (3)
# ./nginx/nginx.conf will be mapped read-only into the container at /etc/nginx/nginx.conf
# ./nginx/mysite.template will be mapped into the container at /etc/nginx/conf.d/mysite.template
# ./nginx/ssl will be mapped into the container at /etc/nginx/ssl
# At startup a self-signed certificate will be created. If you want to use your own certs
# just remove the part that generates the certs from the 'command' section and replace
# 'self-ssl.key' and 'self.cert' with your certificate.
# To debug nginx replace '&& nginx -g 'daemon off' with '&& nginx-debug -g 'daemon off'
# nginx will export port 8443 to the outside world, make sure that this port is reachable
# on your system from the "outside world". All other traffice is only internal.
#
# You could remove the entire 'nginx' service from this file if you want to use your own
# reverse proxy in front of guacamole. If doing so, make sure you change the line
# - 8080/tcp
# to - 8080:8080/tcp
# within the 'guacamole' service. This will expose the guacamole webinterface directly
# on port 8080 and you can use it for your own purposes.
#
# !!!!! FOR A FULL RESET (WILL ERASE YOUR DATABASE, YOUR FILES, YOUR RECORDS AND CERTS) DO A
# !!!!! ./reset
#
#
# The initial login to the guacamole webinterface is:
#
# Username: guacadmin
# Password: guacadmin
#
# Make sure you change it immediately!
#
# version date comment
# 0.1 2017-06-28 initial release
# 0.2 2017-07-18 added more comments to docker-compose.yml
####################################################################################
version: '2.0'
# networks
# create a network 'guacnetwork' in mode 'bridged'
networks:
guacnetwork:
driver: bridge
# services
services:
# guacd
guacd:
image: guacamole/guacd
networks:
guacnetwork:
restart: always
volumes:
- ./drive:/drive:rw
- ./record:/record:rw
# postgres
postgres:
environment:
PGDATA: /var/lib/postgresql/data/guacamole
POSTGRES_DB: guacamole_db
POSTGRES_PASSWORD: Vollgasdepp
POSTGRES_USER: guacamole_user
image: postgres
networks:
guacnetwork:
restart: always
volumes:
- ./init:/docker-entrypoint-initdb.d:ro
- ./data:/var/lib/postgresql/data:rw
# guacamole
guacamole:
depends_on:
- guacd
- postgres
environment:
GUACD_HOSTNAME: guacd
POSTGRES_DATABASE: guacamole_db
POSTGRES_HOSTNAME: postgres
POSTGRES_PASSWORD: Vollgasdepp
POSTGRES_USER: guacamole_user
image: guacamole/guacamole
links:
- guacd
networks:
guacnetwork:
ports:
## enable next line if not using nginx
## - 8080:8080/tcp
## enable next line when using nginx
- 8080/tcp
restart: always
# nginx
nginx:
restart: always
image: nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/mysite.template:/etc/nginx/conf.d/mysite.template
- ./nginx/ssl:/etc/nginx/ssl
ports:
- 8443:443
## environment:
## - NGINX_HOST=nginx
## - NGINX_PORT=443
links:
- guacamole
networks:
guacnetwork:
# install openssl, create self-signed certificate and run nginx
command: /bin/bash -c "apt-get -y update && apt-get -y install openssl && openssl req -nodes -newkey rsa:2048 -new -x509 -keyout /etc/nginx/ssl/self-ssl.key -out /etc/nginx/ssl/self.cert -subj '/C=DE/ST=BY/L=Hintertupfing/O=Dorfwirt/OU=Theke/CN=www.createyourown.domain/emailAddress=docker@createyourown.domain' && cp -f -s /etc/nginx/conf.d/mysite.template /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
####################################################################################
init/initdb.sql
This file contains the initial PostgreSQL database structure and will be bumped into PostgreSQL at first start of the PostgreSQL container (postgres). All future starts of the container will not run the script again! The initial initdb.sql
file has been created with the following command:
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > ./init/initdb.sql
Check
this link for more information.
Hint:
Make sure, the folder ./init
is executable (chmod +x ./init
) or the script will not work.
nginx/mysite.template
A standard nginx configuration file that will be placed in
/etc/nginx/conf.d/mysite.template
within the nginx container. According to the documentation of the official nginx container it will be read in by nginx as stated
here.
### BBB
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/self.cert;
ssl_certificate_key /etc/nginx/ssl/self-ssl.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling off;
ssl_stapling_verify off;
# resolver 8.8.8.8 8.8.4.4 valid=300s;
# resolver_timeout 5s;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://guacamole:8080/guacamole/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_cookie_path /guacamole/ /;
access_log off;
# allow large uploads (default=1m)
# 4096m = 4GByte
client_max_body_size 4096m;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx/nginx.conf
A standard nginx configuration file that will be placed in /etc/nginx/nginx.conf
within the nginx container. The nginx server within the nginx container will pick it up as its default configuration. It also makes sure, that any /etc/nginx/conf.d/*.conf
file within the container will be included in nginx' runtime configuration.
### AAA
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
How it works alltogether
[ ↑ ]
The first run of the containers should be done manually. Therefor we start the containers by just cd'ing into the directory with the docker-compose.yml
file and the other extracted files. Make sure the folder ./init
is executable, then execute
docker-compose up
to run the containers manually. At first start docker will pull 4 images
nginx
postgres
guacamole/guacamole
guacamole/guacd
and will then do the following:
- Create the network
guacnetwork
in bridged mode
- Start a container
guacd
attached to network guacnetwork
- expose the
guacd
container folder /tmp/guacamole/test/drive
to ./drive
- expose the
guacd
container folder /tmp/guacamole/test/record
to ./record
- Start a container
postgres
attached to the network guacnetwork
- The folder
./init
will be mapped inside the container as /docker-entrypoint-initdb.d
- The container
postgres
will be setup with the following valuesPGDATA: /var/lib/postgresql/data/guacamole
POSTGRES_DB: guacamole_db
POSTGRES_PASSWORD: Vollgasdepp
POSTGRES_USER: guacamole_user
- The container postgres will run the initdb.sql script and setup the initial database
- The folder
/var/lib/postgresql/data/guacamole
will be exposed to the host in the folder ./data
- Start a container
nginx
using ./nginx/nginx.conf
as /etc/nginx/nginx.conf
and ./nginx/mysite.template
as /etc/nginx/conf.d/mysite.template
- The container nginx will expose the folder
/etc/nginx/ssl
as ./nginx/ssl
to the host and will contain the later generated self-signed certificate.
- The container nginx will expose port
8443
to th host and map it internally to port 443
in the container
- The container nginx will be linked to container
guacamole
- The container
guacamole
will be started and linked to the container guacd
running within guacnetwork
- The application guacamole within container
guacamole
will be setup to use the following database connection and configuration:GUACD_HOSTNAME: guacd
POSTGRES_DATABASE: guacamole_db
POSTGRES_HOSTNAME: postgres
POSTGRES_PASSWORD: Vollgasdepp
POSTGRES_USER: guacamole_user
- The container
guacamole
will expose port 8080
only internally inside the guacnetwork
network
The exact way wit works can be determined by reading docker-compose.yml
.
After successful testing you should open a second shell in the same folder where you started the docker-compose and enter
docker-compose down
to shutdown the containers for now.
If everything worked fine you can now start the docker-compose process again, but now we tell docker to run these containers daemonized (-d
).
docker-compose up -d
Your containers should now start and guacamole is running. From now on it will start automatically on server startup (with docker daemon).
Upgrade to newer versions
[ ↑ ]
From time to time there will be new versions of Guacamole available. In general an update is no problem, you just stop your containers, delete one or more images and then re-run the containers (latest images will be pulled from internet).
But there is a problem!
Newer versions often change the database schema and when you try to run your containers based on the new images they will fail.
Here is the solution I came up with and it worked for me.
Shutdown Guacamole
user@host /dockers/compose/guacamole> docker-compose down
Enumerate images
First find all images of guacamole with this command
user@host /dockers/compose/guacamole> docker images --filter=reference='guacamole*/*'
REPOSITORY TAG IMAGE ID CREATED SIZE
guacamole/guacd latest ac5de9daf9f3 2 months ago 499MB
guacamole/guacamole latest b602d8daff1b 2 months ago 660MB
Delete images you want to upgrade
Delete them (use bash docker rmi <IMAGE ID>
) or run as single command:
user@host /dockers/compose/guacamole> docker rmi $(docker images --filter=reference='guacamole*/*' -q)
Untagged: guacamole/guacd:latest
Untagged: guacamole/guacd@sha256:358379b75e16e99f44b15e744ac0c958acc872e4510f375a7a7e2a265b2ec7ad
Deleted: sha256:ac5de9daf9f38def1ba96f4926d7923b9df9d3791162b1736a95d3d9c4305576
Deleted: sha256:d59f26605310129a585c684e38c76cd5fe7735febb12aba4de04fb5bf6ff1839
Deleted: sha256:0d6385ada3a8221027817bd4106d6f04af6210ffe12e568159938a17fc6820b4
Deleted: sha256:5066594657365b82de1da81266c057a7750b3d81484f0dcc9a1c69bfa6e776ce
Deleted: sha256:938454d723921cfbda3a01004b73bdf83f2c9bdc44db2f377ee4965a96d89692
Untagged: guacamole/guacamole:latest
Untagged: guacamole/guacamole@sha256:548dfb4dc315d981e2a7f5959fcffdb101fe3924a086a96ad9c94f880b6f313c
Deleted: sha256:b602d8daff1b146c932ec1c89c92824c8ae2fd92fd030206fdaefa292b8cf848
Deleted: sha256:b2d828d2ab2ff993b871f6221bb40a51f5d60e482899d5f397dbae8ffa605517
Deleted: sha256:efeabb36b2830f0e46b532a4a29f9f0b50bc74c3f99ccaab74c76f959302c1b0
Deleted: sha256:e2e1254697ca72ba8312cc7211b4cd0fd86aef7c485839332b3a20f755f8c954
Restart containers (pulling updated images)
Now restart your containers, this will pull down the latest version of the previously deleted images
user@host /dockers/compose/guacamole> docker-compose up -d --remove-orphans
Creating network "guacamole_guacnetwork" with driver "bridge"
Pulling guacd (guacamole/guacd:latest)...
latest: Pulling from guacamole/guacd
...
Status: Downloaded newer image for guacamole/guacd:latest
Pulling guacamole (guacamole/guacamole:latest)...
latest: Pulling from guacamole/guacamole
...
Status: Downloaded newer image for guacamole/guacamole:latest
...
...
Find IP of PostgreSQL container
Now we need to find the internal docker IP address of the PostGreSQL container.
user@host /dockers/compose/guacamole> docker network inspect guacamole_guacnetwork | grep -B1 -A 5 postgres
"3958c97a3457149d4209d32ea9fd0738a199710ca5d89a65683d05545e9ef601": {
"Name": "postgres_guacamole_elitearch",
"EndpointID": "911ac8589a3b397621251d9e72c46c98ebf9a406d52fc083ef1354f479d150d6",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
In the above example the IP is 172.18.0.2
Start interactive bash shell in guacamole/guacamole
Then we start a bash shell within the guacamole/guacamole container (you need the ID of guacamole/guacamole for this).
user@host /dockers/compose/guacamole> docker exec -i -t 50c0df15649c bash
root@50c0df15649c:/usr/local/tomcat
Retrieve list of update scripts
List the update scripts:
root@50c0df15649c:/usr/local/tomcat
total 28
-rw-rw-r-- 1 root root 5568 Jun 26 21:29 upgrade-pre-0.9.10.sql
-rw-rw-r-- 1 root root 1575 Jun 26 21:29 upgrade-pre-0.9.11.sql
-rw-rw-r-- 1 root root 1557 Jun 29 18:15 upgrade-pre-0.9.13.sql
-rw-rw-r-- 1 root root 1051 Jun 26 21:29 upgrade-pre-0.9.7.sql
-rw-rw-r-- 1 root root 1733 Jun 26 21:29 upgrade-pre-0.9.8.sql
-rw-rw-r-- 1 root root 1106 Jun 26 21:29 upgrade-pre-0.9.9.sql
For the next step, make sure you have the name of your database, your database user, your database password and the internal docker IP of your postgresql container handy.
Install psql within container
Install psql command line tool within guacamole/guacamole
root@50c0df15649c:/usr/local/tomcat
root@50c0df15649c:/usr/local/tomcat
Use DB update scripts
Execute the update script by connecting to the docker IP of the PostgreSQL container (in my case an upgrade from 0.9.x to 0.9.13:
root@50c0df15649c:/usr/local/tomcat
Password for user guacamole_user: <enter your password here>
CREATE TYPE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
Now the database is up-to-date and is compatible with the latest guacamole.
Exit the bash prompt by typing exit
.
Shutdown containers again
Shutdown the containers completely again
user@host /dockers/compose/guacamole> docker-compose down
Restart containers
Then restart the containers
user@host /dockers/compose/guacamole> docker-compose up -d --remove-orphans
You guacamole should now have the latest version and work!
Upgrade PostGres to new major version
[ ↑ ]
At the moment we have to explicitely specify the postgres version in docker-compose.yml
If your update process fails (postgres will not start because of major version changes), try to explicitely set the PostGRESQL version in the docker-compose file:
image: postgres:9.6.9
NOT YET TRIED
If your update process fails (postgres will not start because of major version changes), try to explicitely set the PostGRESQL version in the docker-compose file:
image: postgres:9.6.9
You should now be able to start the containers again. But still on old version. So I also added the following line in the postgres section of my docker-compose file to have a separate directory for database backups mapped:
- ./data_bck:/var/lib/postgresql/data_bck:rw
After this changes start the stuff as usual and open a shell into the postgres container:
docker exec -it bde8872ef8af /bin/bash
Now we need to dump the databases:
pg_dumpall -U postgres > /var/lib/postgresql/data_bck/fullbackup.dump
This will save fullbackup.dump
to ./data_bck
in the real filesystem. Now exit the container and shutdown the containers.
root@bde8872ef8af:/
user@localmachine> docker-compose down
Now change back
image: postgres:9.6.9
to
image: postgres
in docker-compose.yml
. Delete the postgres container(s) and rename the data dir to data.old
.
Now create a new initdb.sql with
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > ./init/initdb.sql
Now start guacamole (which will completely recreate an empty guacamole installation)
Again create a shell into the postgres container now
Problems
[ ↑ ]
If you can not connect to port 8443 on your host, make sure your firewall rules are correct and that you have exposed this port to the outside world. Often times it does not help to stop the firewall e.g. firewalld because docker-compose will use firewalld for its internal routing stuff, too.
You have to make sure, that your docker and firewall setup works flawlessly beforehand.