Apache Guacamole with Keycloak SSO on Ubuntu 24.04 on Azure User Guide
Overview
Apache Guacamole is a clientless remote desktop gateway: it delivers RDP, VNC and SSH sessions to a plain web browser, with no plugin and no client software on the user's machine. Keycloak is an open source identity and access management server that speaks OpenID Connect and SAML.
Pairing them is the standard way to put real single sign-on in front of remote access, and it is the pairing Guacamole's own documentation uses as its reference example. The work in that pairing is the OpenID Connect wiring: installing the SSO extension, telling Guacamole which identity provider to trust, registering a client in the identity provider with exactly the right redirect URI, and making the two agree on an absolute issuer URL. This image ships that wiring already done, and proven. On first boot the VM creates its own Keycloak realm, registers Guacamole as an OpenID Connect client bound to that VM's own address, creates your administrator, and then refuses to finish unless it can observe, on that specific VM, the whole round trip: an unauthenticated request handed to Keycloak, a wrong password rejected, the correct password accepted, and Guacamole issuing a session for the expected user.
What is included:
- Apache Guacamole 1.6.0 (the
guacamoleweb application, theguacdproxy daemon, and the JDBC and OpenID Connect extensions) - Keycloak 26.7.1 running as a production Quarkus build against PostgreSQL
- Keycloak pre-wired as Guacamole's OpenID Connect identity provider, with single sign-on as the front door
- PostgreSQL 16 holding both datastores on a dedicated Azure data disk
- nginx terminating TLS on port 443 and serving both components from one address
- A TLS private key generated on your VM at first boot, never baked into the image
- Unique administrator credentials for both components, generated on the first boot of every VM
- No default login of any kind: Guacamole's
guacadminaccount is removed from the database entirely
Who it is for: teams who need auditable, browser-based remote access to servers or desktops, and who want the accounts that grant that access to live in a real identity provider rather than in the remote access tool itself.
Prerequisites
- An Azure subscription with permission to create virtual machines
- A network security group allowing inbound TCP 443 (and 22 for administration) from your network
- A VM size of at least
Standard_B2ms(2 vCPU / 8 GiB). This appliance runs two JVMs and a database; smaller sizes will thrash.
Step 1 - Deploy from the Azure Marketplace
Search the Azure Marketplace for Apache Guacamole with Keycloak SSO on Ubuntu 24.04 LTS, select Create, and choose Standard_B2ms or larger. Allow inbound 443 from the networks your users will connect from, and 22 from your administration network only.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group my-resource-group \
--name guacamole-sso \
--image cloudimg:guacamole-keycloak:default:latest \
--size Standard_B2ms \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# then open the ports your users need
az vm open-port --resource-group my-resource-group --name guacamole-sso --port 443 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<public-ip>
Step 4 - Confirm the appliance is running
The appliance is five services: the database, the Guacamole proxy daemon, the Tomcat instance serving the Guacamole web application, Keycloak, and the nginx front end.
sudo systemctl is-active postgresql guacd tomcat9 keycloak nginx
All five report active.
Only nginx is reachable from outside the VM. Tomcat, Keycloak and guacd are bound to the loopback interface, so the only way in is through the TLS front door:
sudo ss -ltn | grep -E ':(443|80|8080|8081|4822) '

Step 5 - Retrieve your credentials
Every credential on this VM was generated during its first boot and exists nowhere else. Read them once and store them in your password manager.
sudo cat /root/guacamole-keycloak-credentials.txt
You get two identities:
SSO_ADMIN_USER/SSO_ADMIN_PASSWORD- the account you sign in to Guacamole with. It lives in Keycloak.KEYCLOAK_ADMIN_USER/KEYCLOAK_ADMIN_PASSWORD- the Keycloak administration console, where you manage who is allowed to sign in.
The file is readable only by root:
sudo stat -c '%a %U:%G %n' /root/guacamole-keycloak-credentials.txt

Step 6 - Watch the single sign-on wiring working
This is the part that is normally hand-built. Ask Guacamole for a session without credentials and it does not offer you a password box - it hands you to this VM's own Keycloak realm:
PUB=$(sudo grep -m1 '^GUACAMOLE_URL=' /root/guacamole-keycloak-credentials.txt | sed -E 's|^GUACAMOLE_URL=https://([^/]+)/?.*|\1|')
curl -sk -m 20 --resolve "$PUB:443:127.0.0.1" \
-X POST -H 'Content-Type: application/x-www-form-urlencoded' --data '' \
"https://$PUB/api/tokens" | python3 -m json.tool
The response carries a REDIRECT field pointing at /auth/realms/cloudimg/protocol/openid-connect/auth, with client_id=guacamole, response_type=id_token, and a single-use nonce that Guacamole itself generated and will insist on seeing again in the returned token.

Keycloak advertises itself at this VM's own absolute address, which is what makes the token Guacamole receives verifiable:
PUB=$(sudo grep -m1 '^GUACAMOLE_URL=' /root/guacamole-keycloak-credentials.txt | sed -E 's|^GUACAMOLE_URL=https://([^/]+)/?.*|\1|')
curl -sk -m 20 --resolve "$PUB:443:127.0.0.1" \
"https://$PUB/auth/realms/cloudimg/.well-known/openid-configuration" \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print("issuer:", d["issuer"])'
Step 7 - Prove the whole round trip
The image ships the same end-to-end check that first boot ran. It drives the real browser flow: unauthenticated request, Keycloak login page, a deliberately wrong password, then the correct one, then Guacamole accepting the resulting token.
sudo /usr/local/sbin/guacamole-keycloak-sso-proof.sh
It prints SSO_PROOF_OK when every step passed.
There is no default login to fall back on. Guacamole normally ships a guacadmin account whose password is guacadmin; on this image that account is not disabled or renamed, it does not exist:
sudo -u postgres psql -tAc "SELECT count(*) FROM guacamole_entity WHERE name='guacadmin'" -d guacamole_db
That returns 0, and the login is refused:
curl -sk -m 20 -X POST --data-urlencode 'username=guacadmin' \
--data-urlencode 'password=guacadmin' https://127.0.0.1/api/tokens

Step 8 - Sign in through the browser
Browse to https://<public-ip>/.
The TLS certificate was generated on your VM at first boot and is self-signed, so your browser shows a warning on the first visit. Step 12 covers replacing it.
You are taken straight to the Keycloak login page - this is the appliance's front door.

Sign in with SSO_ADMIN_USER and SSO_ADMIN_PASSWORD from Step 5. Keycloak checks the password and returns you to Guacamole, which admits you as that user.

Step 9 - Add your first connection
In Guacamole, open the user menu (top right) and choose Settings, then Connections, then New Connection.
Give it a name, choose the protocol (RDP, VNC or SSH), and fill in the target host under Network:
- RDP - hostname, port
3389, and a username and password for the target machine. For a Windows target, set Security mode toNLAand enable Ignore server certificate unless you have deployed a trusted certificate on it. - VNC - hostname, port
5901(or whichever display you serve), and the VNC password. - SSH - hostname, port
22, and either a username and password or a private key.
Save, then click the connection on the Guacamole home screen. The session opens in the browser.

Step 10 - Add more users
Users live in Keycloak, not in Guacamole. Sign in to the administration console at https://<public-ip>/auth/admin/ with KEYCLOAK_ADMIN_USER and KEYCLOAK_ADMIN_PASSWORD.
Switch the realm selector (top left) from master to cloudimg, open Users, then Add user. Give the account a username, first name, last name and email, then set a password on the Credentials tab.

Fill in first name, last name and email. Keycloak's default profile policy asks a user to complete missing profile fields at first sign-in, which interrupts the hand-off back to Guacamole.
A new user can sign in immediately, but sees no connections until you grant them some: in Guacamole, Settings → Connections → select the connection → Permissions, or grant through a group.
Because identity lives in Keycloak, everything Keycloak offers applies here without further configuration - two-factor authentication (Authentication → Required actions → Configure OTP), password policy, account lockout, and federation to an existing LDAP or Active Directory directory (User federation).
Step 11 - Point the appliance at your own hostname
The image binds itself to the VM's public IP address on first boot, because OpenID Connect redirect URIs are absolute and must match exactly. To use a DNS name instead, three things must agree.
In the Keycloak administration console, realm cloudimg → Clients → guacamole, set Valid redirect URIs to https://<your-domain>/ and Web origins to https://<your-domain>.
Then, on the VM, update Guacamole's copy and restart it:
sudo sed -i 's|https://[^/]*/auth|https://<your-domain>/auth|g; s|^openid-redirect-uri:.*|openid-redirect-uri: https://<your-domain>/|' /etc/guacamole/guacamole.properties
sudo sed -i 's|^KC_HOSTNAME=.*|KC_HOSTNAME=https://<your-domain>/auth|' /etc/keycloak/keycloak.env
sudo systemctl restart keycloak tomcat9
Step 12 - Replace the TLS certificate
The interim certificate is self-signed and unique to your VM. To install your own, replace the two files and reload nginx:
sudo install -m 600 /path/to/<your-domain>.key /etc/nginx/tls/server.key
sudo install -m 644 /path/to/<your-domain>.crt /etc/nginx/tls/server.crt
sudo nginx -t && sudo systemctl reload nginx
Network ports
| Port | Purpose |
|---|---|
| 22 | SSH administration. Restrict to your management network. |
| 80 | HTTP. Redirects to HTTPS; nothing is served over it. |
| 443 | The Guacamole workspace at / and the Keycloak identity server at /auth/. |
Tomcat (8080), Keycloak (8081) and guacd (4822) listen on the loopback interface only and are not reachable from the network.
Where things live
| Path | Contents |
|---|---|
/etc/guacamole/guacamole.properties |
Guacamole configuration, including the OpenID Connect settings |
/etc/guacamole/extensions/ |
The JDBC and OpenID Connect extensions |
/etc/keycloak/keycloak.env |
Keycloak runtime configuration |
/opt/keycloak/ |
Keycloak installation |
/opt/tomcat/ |
Tomcat 9 and the Guacamole web application |
/var/lib/postgresql/ |
Dedicated Azure data disk holding both databases |
/root/guacamole-keycloak-credentials.txt |
Per-VM credentials, mode 600 |
Troubleshooting
The browser warns about the certificate. Expected on first visit - the certificate is self-signed and generated on your VM. Step 12 replaces it.
Signing in loops back to the login page. The redirect URI, the Keycloak hostname and Guacamole's configuration must all name the same address. If you changed the hostname, re-check all three parts of Step 11. Confirm what Keycloak is advertising:
sudo grep -E '^openid-(issuer|redirect-uri)' /etc/guacamole/guacamole.properties
A user signs in but sees no connections. Keycloak proved who they are; Guacamole decides what they may open. Grant them permission on a connection (Step 10).
Keycloak did not start. It needs its database. Check both:
sudo systemctl is-active postgresql keycloak
sudo journalctl -u keycloak.service -n 40 --no-pager
Check first boot completed. The appliance writes a sentinel when its bootstrap finished:
ls -l /var/lib/cloudimg/guacamole-keycloak-firstboot.done
sudo tail -20 /var/log/cloudimg-firstboot.log
Remote sessions. guacd on Ubuntu 24.04 is version 1.3.0 and negotiates the 1.3 protocol with the 1.6 web application. RDP, VNC and SSH all work; parameters introduced after Guacamole 1.3 are not available. Note that guacd authenticates to SSH targets using RSA SHA-1 signatures, which OpenSSH 8.8 and later reject by default - use password authentication, or an ed25519 key, for SSH targets running a recent OpenSSH.
Support
This image is maintained by cloudimg. For support, contact support@cloudimg.co.uk.
Apache Guacamole is a trademark of The Apache Software Foundation. Keycloak is a trademark of the Linux Foundation. cloudimg is not affiliated with, endorsed by, or sponsored by either project or foundation; this image packages their freely available open source software, which remains licensed under the Apache License 2.0.