Create Your Own Azure Bastion with Guacamole and Save $100+ a month

Azure Bastion Architecture from Microsoft

Apache Guacamole

From its website, Apache Guacamole is a clientless remote desktop gateway that supports standard protocols like VNC, RDP, and SSH. Clientless means your clients don’t need to install anything but just use a web browser to remotely access your fleet of VMs.

  • Guacamole Client which is a servelet container that user will log in and via web browser.
Guacamole Architecture from Guacamole Documentation

Network Topology

You use Azure Bastion or a jump server because you want to secure your VMs behind so having the right network design is needed. Here it mine:

Network Topology Modified from Azure Network Watcher by Author
  1. snet-default where I will deploy my backend pool of VM and enable remote access via Guacamole only.
Network Security Group Inbound Rules from Microsoft Docs
Network Security Group Outbound Rules from Microsoft Docs
  1. vm-ubuntu1804 in the snet-gateway where I will install Guacamole and use it as a jump server. I choose B2s size which cost around $39 per month but, of course, you can change its size later.

Install Guacamole Server

Once your Ubuntu Server 18.04 is created then log in via SSH. You may need to expose SSH port publicly for now. You can change SSH port to something than 22 to make it more secure. See how to do it in my previous blog.

# Update & upgrade system
sudo apt-get update && sudo apt-get --yes upgrade

# Install build tools
sudo apt install --yes build-essential

# Install build dependencies
sudo apt install --yes libcairo2-dev libjpeg-turbo8-dev libpng-dev libtool-bin libossp-uuid-dev

# Install optional dependencies
sudo apt install --yes \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
freerdp2-dev \
libpango1.0-dev \
libssh2-1-dev \
libtelnet-dev \
libvncserver-dev \
libwebsockets-dev \
libpulse-dev \
libssl-dev \
libvorbis-dev \

# Install runtime dependencies
sudo apt install --yes --no-install-recommends \
netcat-openbsd \
ca-certificates \
ghostscript \
fonts-liberation \
fonts-dejavu \
export GUAC_VERSION="1.3.0"
curl -fLO "${GUAC_VERSION}/source/guacamole-server-${GUAC_VERSION}.tar.gz"
tar -xzf "guacamole-server-${GUAC_VERSION}.tar.gz"
cd "guacamole-server-${GUAC_VERSION}"
./configure --with-init-dir=/etc/init.d
sudo make install
sudo ldconfig
sudo systemctl daemon-reload
sudo systemctl start guacd
sudo systemctl enable guacd
Guacamole server (guacd) service status captured by author

Install Guacamole Client

Unlike the server, we don’t need to build it (but you can if you want). So we will download the WAR file and install on Tomcat server then expose it through nginx via HTTPS.

Install Tomcat

First, we need Tomcat to run the WAR file. Use this script to install it.

export TOMCAT_VERSION="8.5.65"
TOMCAT_MAJOR_VERSION=$(echo ${TOMCAT_VERSION} | awk -F . '{print $1}')
sudo apt-get install --yes default-jdk
sudo groupadd tomcat
sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
sudo usermod -a -G tomcat "$USER"
curl -LO "${TOMCAT_MAJOR_VERSION}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz"
sudo mkdir -p /opt/tomcat
sudo tar -xzf "apache-tomcat-${TOMCAT_VERSION}.tar.gz" -C /opt/tomcat --strip-components=1
sudo chgrp -R tomcat /opt/tomcat
cd /opt/tomcat
sudo chmod -R g+r conf
sudo chmod g+x conf
sudo chown -R tomcat webapps/ work/ temp/ logs/
JAVA_ALT_TEXT="$(update-java-alternatives -l || true)"
JAVA_HOME="$(echo "${JAVA_ALT_TEXT}" | awk '{print $3}')"
echo "[Unit]
Description=Apache Tomcat Web Application Container


Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'



[Install]" | sudo tee /etc/systemd/system/tomcat.service > /dev/null
sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat
Tomcat service status captured by author

Add Guacamole Client Servlet

Download and add the WAR file to the Tomcat.

curl -LO "${GUAC_VERSION}/binary/guacamole-${GUAC_VERSION}.war"
sudo cp "guacamole-${GUAC_VERSION}.war" "/opt/tomcat/webapps/ROOT.war"
sudo chown tomcat:tomcat "/opt/tomcat/webapps/ROOT.war"
sudo rm -rf /opt/tomcat/webapps/ROOT
Guacamole login screen captured by author

Install nginx and certbot

We will use nginx as a proxy and certbot to get a certificate from Let’s Encrypt.

sudo apt install --yes nginx-core
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
export DOMAIN_NAME="<Your VM FQDN>"
export EMAIL="<Your Email Address>"
sudo certbot --nginx -d "${DOMAIN_NAME}" -m "${EMAIL}" --agree-tos -n
server_name your.server.fqdn; # managed by Certbot

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
server_name your.server.fqdn; # managed by Certbot

location / {

proxy_pass http://localhost:8080/;
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;
sudo nginx -t
sudo systemctl restart nginx

Configure User and Connections

We will create a user with two connections in the user-mapping.xml file so we can test logging on.

<authorize username="guacadmin" password="guacadmin">
<connection name="this-server-ssh">
<param name="hostname">localhost</param>
<param name="port">22</param>
<connection name="some-win10-rdp">
<param name="hostname">vm-win10</param>
<param name="port">3389</param>
<param name="username">username</param>
<param name="password">thisisyourpassword</param>
<param name="ignore-cert">true</param>
sudo systemctl restart tomcat
Guacamole home screen captured by author
Guacamole settings screen captured by author

Install MySQL

Let’s install MySQL.

sudo apt install --yes mysql-server
MySQL service status captured by author
# Download and install JDBC extensions
curl -fLO "${GUAC_VERSION}/binary/guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz"
tar -xzf "guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz"
sudo mkdir -p /etc/guacamole/extensions
sudo cp "guacamole-auth-jdbc-${GUAC_VERSION}/mysql/guacamole-auth-jdbc-mysql-${GUAC_VERSION}.jar" "/etc/guacamole/extensions/"

# Download and install MySQL Connector/J
curl -fLO "${CONNECTORJ_VERSION}.tar.gz"
tar -xvf "mysql-connector-java-${CONNECTORJ_VERSION}.tar.gz"
sudo mkdir -p /etc/guacamole/lib
sudo cp "mysql-connector-java-${CONNECTORJ_VERSION}/mysql-connector-java-${CONNECTORJ_VERSION}.jar" "/etc/guacamole/lib/"

Configure Database

Next, we need to create a database and a user. In this example, I will create the database named guacamole_db and the user named guacamole_user. Please note the password must meet complexity criteria.

sudo mysql --execute='CREATE DATABASE guacamole_db;'
sudo mysql --execute="CREATE USER 'guacamole_user'@'localhost' IDENTIFIED BY '${MYSQL_PASSWORD}';"
sudo mysql --execute="GRANT SELECT,INSERT,UPDATE,DELETE ON guacamole_db.* TO 'guacamole_user'@'localhost';"
sudo mysql --execute='FLUSH PRIVILEGES;'
cat guacamole-auth-jdbc-1.3.0/mysql/schema/*.sql | sudo mysql guacamole_db
# MySQL properties
mysql-hostname: localhost
mysql-port: 3306
mysql-database: guacamole_db
mysql-username: guacamole_user
mysql-password: <YourPasswordHere>
sudo rm -f /etc/guacamole/user-mapping.xml
sudo systemctl restart tomcat
Guacamole settings screen captured by author

Enable Two-Factor Authentication

If you want you can optionally install TOTP module to enable two-factor authentication to add more security to your VM pool. Just download the extension and restart Tomcat.

# Download and install TOTP extension
curl -fLO "${GUAC_VERSION}/binary/guacamole-auth-totp-${GUAC_VERSION}.tar.gz"
tar -xzf "guacamole-auth-totp-${GUAC_VERSION}.tar.gz"
sudo mkdir -p /etc/guacamole/extensions
sudo cp "guacamole-auth-totp-${GUAC_VERSION}/guacamole-auth-totp-${GUAC_VERSION}.jar" "/etc/guacamole/extensions/"

# Restart tomcat
sudo systemctl restart tomcat
Multi-factor authentication setup screen captured by author


If you don’t want to perform all above steps one by one, you may leverage bash scripts I created in this repository. |