Apache Guacamole 1.6.0
Overview
Apache Guacamole is a clientless remote desktop gateway. Your users open a web page and get a full RDP, VNC or SSH session to a server behind it, with nothing installed on their own machine. Guacamole keeps users, connections and permissions in its own database, and you manage them from its Settings pages.
This guide covers launching and verifying Apache Guacamole 1.6.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The guide hub describes the other VPN, proxy and remote access servers in the listing. If you want sign-in through an identity provider with multi-factor authentication, the same listing also offers Apache Guacamole 1.6.0 + Keycloak on Ubuntu 24.04.
What you get:
- Apache Guacamole 1.6.0: the web application on Apache Tomcat 9.0.123 and the guacd proxy daemon, built by SecureAnyCloud from the published 1.6.0 source, with RDP, VNC and SSH support
- PostgreSQL 16 holding Guacamole's users, connections, permissions and history, on
127.0.0.1only - nginx serving HTTPS on port 443 only, with a certificate generated for your instance on first boot
- The Guacamole extensions for PostgreSQL authentication and brute-force protection (
guacamole-auth-ban) - A self-test,
guacamole-selftest, and a helper,guacamole-set-host, that moves the instance to a DNS name or Elastic IP
Default user: ubuntu. Architecture: x86_64. Root volume: 12 GiB gp3.
Security posture
- No default credential. Guacamole's published
guacadmin/guacadminaccount is never created. First boot creates the administratorsac-adminwith a password generated for this instance only, together with the database password and the TLS key. It writes the sign-in details to/root/guacamole-credentials.txt(root only). - No shared database. The image carries no PostgreSQL data at all. First boot creates a new database cluster on your instance, so no password hash or identifier is shared with any other instance.
- One public port. nginx listens on TCP 443. Tomcat (8080),
guacd(4822) and PostgreSQL (5432) listen on127.0.0.1only. The listing's security group opens SSH (22) and HTTPS (443). - Fixed host name. nginx answers only to the name this instance is configured for, which is its public IPv4 address or the DNS name you set in Step 9. It closes the connection for any other
Hostheader. - Real client addresses. nginx overwrites
X-Forwarded-Forwith the address it sees, and Tomcat trusts forwarded headers from127.0.0.1only. Guacamole's logs and its brute-force ban therefore see the real client, and a forged header changes nothing. - Brute-force protection.
guacamole-auth-banbans an address for 5 minutes after 5 failed sign-ins from it. - Cross-site protection. A Guacamole WebSocket tunnel or a state-changing request whose
Originis not this instance's own address is refused with 403. Another web site therefore cannot open a session with your users' credentials. - No SSRF to instance credentials.
guacd, Tomcat, PostgreSQL and nginx each carry anIPAddressDenyrule for the metadata addresses (169.254.169.254, fd00:ec2::254, 168.63.129.16), and the AMI is registered IMDSv2 only.guacdcan still reach every other address, so your RDP, VNC and SSH targets work. - Patched components. Tomcat 9.0.123 is the current 9.0 release. The Guacamole 1.6.0 web application bundles Jackson 2.19.0 and Logback 1.3.15, which have published security fixes. This image replaces exactly those libraries with Jackson 2.21.8 and Logback 1.3.16.
guacdis built against Ubuntu's supported FreeRDP 3 libraries, and no build tools ship. - Fails closed. Every service starts only after first boot has written this instance's configuration and a ready marker. First boot finishes only after it has proved that the administrator can sign in over HTTPS. If it cannot, every service stays stopped and first boot starts over on the next boot.
- Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- The public IP ranges your users connect from, and network access from the instance to the servers you want to reach (security groups, VPC routing or a VPN)
Recommended instance type: m5.large (2 vCPU, 8 GiB). Tomcat and PostgreSQL use well under 1 GiB at rest. Add memory and CPU if you expect many concurrent sessions.
Connecting to your instance
Version Operating system SSH user
Apache Guacamole 1.6.0 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Apache Guacamole 1.6.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largeis recommended; the software charge is $0.04 per vCPU per hour) and your key pair. - For the security group, allow SSH (22) from your own address and HTTPS (443) from the ranges your users connect from. Nothing else is needed.
- Choose your VPC and a public subnet, or attach an Elastic IP later. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run this command on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name apache-guacamole \
--description "Apache Guacamole" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-client-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=apache-guacamole}]'You do not need --metadata-options, because the AMI already requires IMDSv2.
Connect and read the sign-in details
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot takes under a minute. It creates the database and the administrator, issues the certificate, starts the services, and then proves that the administrator can sign in. The details are in /root/guacamole-credentials.txt, which only root can read. This command prints everything except the password line:
sudo grep -v '^GUACAMOLE_ADMIN_PASSWORD=' /root/guacamole-credentials.txt# Apache Guacamole 1.6.0 - generated on first boot by guacamole-firstboot.service.
# Every value below is unique to this instance; nothing was baked into the image. GUACAMOLE_URL=https://52.87.161.224/
# Sign in to Guacamole with this administrator (change the password under Settings > Preferences). GUACAMOLE_ADMIN_USER=sac-admin
# The certificate is self-signed and unique to this instance (browsers warn on the first visit). TLS_CERT_SHA256=51:71:EA:26:DC:3E:F5:98:14:32:82:A4:68:E2:04:28:7E:8C:44:07:C9:1A:92:89:59:5B:0F:7E:BC:C9:75:4F
# New DNS name or Elastic IP:
sudo guacamole-set-host <name-or-ip>
# Self-test:
sudo guacamole-selftest
# Guide: https://secureanycloud.com/stacks/apache-guacamole-1-6-0/You sign in to Guacamole as sac-admin (GUACAMOLE_ADMIN_USER), a Guacamole administrator. Print the password only when you need it:
sudo sed -n 's/^GUACAMOLE_ADMIN_PASSWORD=//p' /root/guacamole-credentials.txtConfirm the stack is running
All four services and the first-boot unit are active, and only SSH and HTTPS listen on a public address. Run:
systemctl is-active postgresql@16-main.service guacd.service tomcat.service nginx.service guacamole-firstboot.service /usr/local/sbin/guacd -v sudo ss -Htln | awk '{print $4}' | sort -uactive active active active active Guacamole proxy daemon (guacd) version 1.6.0 0.0.0.0:22 0.0.0.0:443 127.0.0.1:4822 127.0.0.1:5432 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::ffff:127.0.0.1]:8080127.0.0.53 and 127.0.0.54 are Ubuntu's local DNS resolver.
Run the self-test
The self-test checks that every service is active and that guacd loads its RDP, VNC and SSH plugins. It then signs in as sac-admin through nginx and TLS exactly as a browser does, checks that the account is an administrator, and signs out again. It also proves that no guacadmin account exists. It makes no failed sign-in, which the brute-force ban would count, and it prints no password. Run:
sudo guacamole-selftestservices: postgresql@16-main, guacd, tomcat and nginx are active guacd plugins: rdp(88 params, VERSION_1_5_0) vnc(49 params, VERSION_1_5_0) ssh(42 params, VERSION_1_5_0) sign-in: the per-instance administrator signs in over HTTPS (PostgreSQL data source, ADMINISTER) and signs out defaults: the guacadmin account does not exist SELFTEST_OKSign in through the browser
Open GUACAMOLE_URL from Step 2 (for example https://52.87.161.224/). The certificate is self-signed, so the browser warns you on the first visit. Compare the fingerprint it shows with TLS_CERT_SHA256 and continue. Step 9 replaces the certificate with your own. Guacamole shows its sign-in page:
Sign in as sac-admin with GUACAMOLE_ADMIN_PASSWORD. The home page lists your connections. It stays empty until you add one:
Change the generated password to one of your own under Settings > Preferences in the top-right user menu. sudo guacamole-selftest reads the password from the credentials file, so put the new password there too. Otherwise the sign-in check fails.
Add your first connection
Open the user menu (top right), choose Settings, then Connections and New Connection. Give the connection a name and choose the protocol. Under Parameters > Network, fill in the target's Hostname and Port (3389 for RDP, 5900 for VNC, 22 for SSH). If you want Guacamole to sign in for the user, also enter the credentials under Authentication. Select Save, and the connection appears on the home page.
The guacd in this image supports RDP, VNC and SSH. The form also lists Telnet and Kubernetes, which this build does not include. Telnet is unencrypted.
The target must be reachable from the instance. On the target, allow RDP, VNC or SSH from the Guacamole instance's security group, not from the internet.
Add users
Choose Settings > Users > New User and set a user name and password. Under Connections, tick the connections (or connection groups) that the user may open. Tick Administer system only for people who should manage Guacamole itself.
Give every person their own user. Guacamole records each session under the user's name in Settings > History.
Guacamole also supports time-based one-time passwords (TOTP), LDAP, SAML and OpenID Connect through extensions published by the Apache Guacamole project. Download the 1.6.0 extension and place the .jar in /etc/guacamole/extensions/. Add its settings to /etc/guacamole/guacamole.properties, then restart Tomcat.
Brute-force protection
Guacamole loads the brute-force extension next to the PostgreSQL extension. Its settings are in guacamole.properties. Five failed sign-ins from one address ban that address for 300 seconds:
sudo ls /etc/guacamole/extensions
sudo grep -E '^ban-' /etc/guacamole/guacamole.propertiesguacamole-auth-ban-1.6.0.jar guacamole-auth-jdbc-postgresql-1.6.0.jar ban-max-invalid-attempts: 5 ban-address-duration: 300nginx passes the real client address and Tomcat trusts it from 127.0.0.1 only. The ban therefore applies to the attacker's address, not to everyone behind the proxy.
Use a DNS name or an Elastic IP
nginx, the certificate and the Origin check all name one host. First boot uses the instance's public IPv4 address.
If you stop and start the instance without an Elastic IP, the public address changes. The site then stops answering on the new address until you run the helper. The login message warns you when this happens. With no argument, guacamole-set-host reads the current address from the instance metadata:
sudo guacamole-set-host | tail -3Host changed from 52.87.161.224 to 52.87.161.224: Guacamole https://52.87.161.224/ Certificate SHA-256: 16:23:A2:FA:D1:F1:59:EB:A5:3C:A6:BA:8A:1C:F5:33:FA:F4:CC:AA:53:23:68:68:F2:C5:7E:D4:C1:62:47:B1 SET_HOST_OKTo use your own name, point a DNS record at the instance and pass the name: sudo guacamole-set-host guac.example.com. Attach an Elastic IP first so that the address never changes.
The helper updates nginx and the URL in the credentials file, issues a new self-signed certificate for that name, reloads nginx, and proves the sign-in again. If nginx rejects the change, the helper restores the previous settings.
Use your own certificate
After Step 9 has set the name, copy your certificate (full chain) and key over the self-signed pair, then reload nginx:
sudo install -m 0644 fullchain.pem /etc/nginx/tls/server.crt
sudo install -m 0600 privkey.pem /etc/nginx/tls/server.key
sudo nginx -t && sudo systemctl reload nginxRunning guacamole-set-host again later issues a new self-signed certificate, so copy your certificate back afterwards. Alternatively, put an Application Load Balancer with an AWS Certificate Manager certificate in front of port 443, and keep the self-signed certificate between the load balancer and the instance.
Metadata access is blocked
Every service of the stack carries an IPAddressDeny rule for the metadata addresses. A connection that someone configures in Guacamole, or anything else a user can influence, therefore cannot reach the instance's credentials. Verify the rule:
for u in postgresql@16-main guacd tomcat nginx; do printf '%-20s %s\n' "$u" "$(systemctl show -p IPAddressDeny --value "$u.service")"; donepostgresql@16-main fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32 guacd fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32 tomcat fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32 nginx fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32How the fail-closed start works
Every service is gated on /var/lib/sac/data/guacamole.ready, which first boot writes only after this instance's database, configuration and certificate exist. The files that hold secrets are readable only by root and the one service that needs them:
systemctl cat tomcat.service nginx.service | grep '^ConditionPathExists='
sudo stat -c '%a %U:%G %n' /root/guacamole-credentials.txt /etc/guacamole/guacamole.properties /etc/nginx/tls/server.keyConditionPathExists=/var/lib/sac/data/guacamole.ready ConditionPathExists=/var/lib/sac/data/guacamole.ready 600 root:root /root/guacamole-credentials.txt 640 root:tomcat /etc/guacamole/guacamole.properties 600 root:root /etc/nginx/tls/server.keyNetwork ports
Port Listens on Purpose
22/tcp all addresses SSH administration (restrict to your own address)
443/tcp all addresses Guacamole over HTTPS
8080/tcp 127.0.0.1 Tomcat (Guacamole web application), behind nginx
4822/tcp 127.0.0.1 guacd
5432/tcp 127.0.0.1 PostgreSQL 16
Port 80 is not used: give your users the https:// address.
Where things live
Path What it is
/root/guacamole-credentials.txt Sign-in URL, the administrator and its per-instance password
/etc/guacamole/guacamole.properties Guacamole settings (database, brute-force ban)
/etc/guacamole/extensions/ Guacamole extensions (PostgreSQL authentication, brute-force ban)
/etc/nginx/sites-available/sac-guacamole The HTTPS site; /etc/nginx/tls/ holds the certificate
/opt/tomcat/ Tomcat 9.0.123 with the Guacamole web application
/usr/local/sbin/guacd guacd 1.6.0
/var/lib/postgresql/16/main PostgreSQL data (created on first boot)
Logs are in the journal. Use journalctl -u tomcat for Guacamole sign-ins and connections, journalctl -u guacd for the proxy daemon, and journalctl -u guacamole-firstboot for first boot.
Troubleshooting
- The browser shows "connection closed" or nothing at all. You used a name that nginx is not configured for. Use
GUACAMOLE_URLexactly, or runsudo guacamole-set-host(Step 9). - "Too many failed authentication attempts".
guacamole-auth-banbanned your address for 5 minutes after repeated failures. Wait, or restart Tomcat to clear the list. - A connection fails to start. Check that the target allows RDP, VNC or SSH from the Guacamole instance, and look at
journalctl -u guacd. - First boot did not finish.
journalctl -u guacamole-firstbootshows the step that failed. Every service stays stopped until first boot succeeds, and a reboot retries it.
Security recommendations
- Keep TCP 443 open only to the ranges your users connect from, and TCP 22 only to your management addresses.
- Change the generated administrator password, and give each person their own Guacamole user with only the connections they need.
- Add TOTP or your identity provider as a second factor (Step 7).
- Attach an Elastic IP and a DNS name, and use a certificate that your users' browsers already trust (Steps 9 and 10).
- Leave unattended security upgrades on, and reboot after kernel updates.
Support and licensing
Apache Guacamole and Apache Tomcat are projects of the Apache Software Foundation, distributed under the Apache License 2.0. This image builds the unmodified Guacamole 1.6.0 server source, ships the Guacamole 1.6.0 web application with its Jackson and Logback libraries updated as described above, and ships the unmodified Tomcat 9.0.123 release. SecureAnyCloud provides the packaging, the integration, the secure defaults, the per-instance credentials and certificate, the metadata block, the fail-closed start, the self-test and 24/7 support.
SecureAnyCloud is not affiliated with or endorsed by the Apache Software Foundation. Apache, Apache Guacamole, Guacamole and Apache Tomcat are trademarks of the Apache Software Foundation. They are used here only to identify the software.
For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch Apache Guacamole 1.6.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com