Secure Apache Guacamole
This guide shows you how to secure an instance of Apache Guacamole behind Pomerium using JWT Claim Headers to support HTTP header authentication. After a user authenticates against a configured identity provider, Pomerium will forward the user's email address in an unsigned HTTP header to Guacamole with the request. This setup allows for centralized, secure, and seamless authentication through Pomerium.
To complete this guide, you'll follow these steps:
- Install and configure Guacamole using Docker Compose.
- Replace the default Nginx with Pomerium in the Docker configuration.
- Configure Pomerium to forward identity headers to Guacamole.
- Securely run your setup and verify proper authentication.
This guide provides steps to secure access to the Guacamole gateway behind Pomerium. It does not cover how to add connections to Guacamole itself.
Before you start
To complete this guide, you need:
This guide uses the Hosted Authenticate Service, which provides its own hosted identity provider and authenticate service URL. If you prefer to configure your own identity provider, see Self-Hosted Authenticate Service.
Install Guacamole with Docker Compose
Installing Guacamole with Docker requires the following services:
guacamole/guacd
: The guacd daemon, which provides support for VNC, RDP, SSH, telnet, and Kubernetes.guacamole/guacamole
: The Guacamole web application running within a Tomcat 8 server with websocket support.mysql
orpostgres
: The relational database that stores authentication and connection configuration data.
Ensure that environment variables containing sensitive data, such as passwords, are stored securely. Consider using Docker secrets or an .env
file.
This guide uses an open-source Docker Compose configuration to install these services. This configuration file also includes an nginx
instance, which you'll replace with Pomerium.
To get started:
-
Clone the
guacamole-docker-compose
repository:git clone https://github.com/boschkundendienst/guacamole-docker-compose
-
Go into your project and run the provided
prepare.sh
script. This script initializes a PostgreSQL database that works with Guacamole. (It generates also a self-signed certificate and private key for Nginx, which you can ignore.)cd guacamole-docker-compose
./prepare.sh -
In the
guacamole
container, add the following environment variables:
guacamole:
container_name: guacamole_compose
depends_on:
- guacd
- postgres
environment:
GUACD_HOSTNAME: guacd
POSTGRES_DATABASE: guacamole_db
POSTGRES_HOSTNAME: postgres
POSTGRES_PASSWORD: 'ChooseYourOwnPasswordHere1234'
POSTGRES_USER: guacamole_user
# Enables HTTP header authentication
HEADER_ENABLED: true
# Adds expected HTTP header from incoming Pomerium requests
HTTP_AUTH_HEADER: X-Pomerium-Claim-Email
image: guacamole/guacamole
networks:
- guacnetwork_compose
volumes:
- ./record:/record:rw
ports:
- 8080/tcp
restart: always
Configure Pomerium
In your Docker Compose file, replace nginx
with Pomerium Core:
-
Remove the
nginx
configuration:# nginx
nginx:
container_name: nginx_guacamole_compose
restart: always
image: nginx
volumes:
- ./nginx/templates:/etc/nginx/templates:ro
- ./nginx/ssl/self.cert:/etc/nginx/ssl/self.cert:ro
- ./nginx/ssl/self-ssl.key:/etc/nginx/ssl/self-ssl.key:ro
ports:
- 8443:443
links:
- guacamole
networks: guacnetwork_compose -
Add the Pomerium Core configuration:
pomerium:
image: cr.pomerium.com/pomerium/pomerium:latest
# Mount your config file
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
ports:
- 443:443
networks:
- guacnetwork_compose
environment:
JWT_CLAIMS_HEADERS: email -
In line 5 above, you mount a Pomerium configuration file into Docker Compose. In your project's root directory, create a
config.yaml
file with the following configuration:Pomerium configuration fileauthenticate_service_url: https://authenticate.pomerium.app
jwt_claim_headers: email
routes:
- from: https://guacamole.localhost.pomerium.io
to: http://guacamole:8080
policy:
- allow:
or:
- email:
is: user@example.com
pass_identity_headers: trueDon't forget to replace
user@example.com
with your own email address.
A few things to note:
pass_identity_headers
instructs Pomerium to send the Pomerium JWT to Guacamole after the user authenticates successfully against the identity provider. You must enable this setting to forward identity headers with JWT Claims Headers.jwt_claims_headers
instructs Pomerium to forward additional claims as unsigned HTTP headers with the request. (Guacamole expects an incomingX-Pomerium-Claim-Email
HTTP header from Pomerium.)
Run Docker Compose
Now, run Docker Compose:
docker compose up -d
Navigate to the Guacamole route defined in config.yaml
by adding /guacamole
to its path. For example, if your route is guacamole.localhost.pomerium.io
, the route you'd access in the browser would be guacamole.localhost.pomerium.io/guacamole
.
After authenticating against the configured identity provider, Pomerium will redirect you to the Guacamole dashboard:
Configuration file reference
If you're stuck, the reference files below contain the configuration used to complete this guide. Compare your configuration to see if you missed something.
Docker Compose configuration reference file:
# networks
# create a network 'guacnetwork_compose' in mode 'bridged'
networks:
guacnetwork_compose:
driver: bridge
services:
# guacd
guacd:
container_name: guacd_compose
image: guacamole/guacd
networks:
- guacnetwork_compose
restart: always
volumes:
- ./drive:/drive:rw
- ./record:/record:rw
# postgres
postgres:
container_name: postgres_guacamole_compose
environment:
PGDATA: /var/lib/postgresql/data/guacamole
POSTGRES_DB: guacamole_db
POSTGRES_PASSWORD: 'ChooseYourOwnPasswordHere1234'
POSTGRES_USER: guacamole_user
image: postgres:15.2-alpine
networks:
- guacnetwork_compose
restart: always
volumes:
- ./init:/docker-entrypoint-initdb.d:z
- ./data:/var/lib/postgresql/data:Z
# guacamole
guacamole:
container_name: guacamole_compose
depends_on:
- guacd
- postgres
environment:
GUACD_HOSTNAME: guacd
POSTGRES_DATABASE: guacamole_db
POSTGRES_HOSTNAME: postgres
POSTGRES_PASSWORD: 'ChooseYourOwnPasswordHere1234'
POSTGRES_USER: guacamole_user
HEADER_ENABLED: true
HTTP_AUTH_HEADER: X-Pomerium-Claim-Email
image: guacamole/guacamole
networks:
- guacnetwork_compose
volumes:
- ./record:/record:rw
ports:
- 8080/tcp
restart: always
# pomerium
pomerium:
image: cr.pomerium.com/pomerium/pomerium:latest
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
ports:
- 443:443
networks:
- guacnetwork_compose
environment:
JWT_CLAIMS_HEADERS: email
Pomerium configuration reference file:
authenticate_service_url: https://authenticate.pomerium.app
jwt_claim_headers: email
routes:
pass_identity_headers: true
- from: https://guacamole.localhost.pomerium.io
to: http://guacamole:8080
policy:
- allow:
or:
- email:
is: user@example.com
pass_identity_headers: true