需要临时登录办公室 Windows、家里的 Linux 主机或多台客户服务器时,直接把 3389、5900 和 22 暴露到公网既难管理,也会扩大攻击面。Apache Guacamole 提供了另一种结构:用户只需要浏览器,通过一个 HTTPS 网关访问后端 RDP、VNC、SSH 等连接,不必在个人电脑安装专用客户端。
但 Guacamole 不是“装完就安全”的魔法堡垒机。它掌握远程入口、账号权限和可能保存的目标凭据;一旦 Web 管理账号被盗,攻击者就可能借它进入更多机器。本文使用 Ubuntu 24.04、Apache Guacamole 1.6.0 官方 Docker 镜像、MariaDB、Caddy HTTPS、TOTP 和内置登录封禁,完整配置数据库初始化、远程连接、安全加固、备份、升级和故障排查。
Apache Guacamole 是无客户端远程桌面网关。浏览器与 Guacamole Web 应用通信,Web 应用再把连接交给 guacd 代理进程,由 guacd 连接真正的 RDP、VNC、SSH、Telnet 或 Kubernetes 目标。
本文部署结构如下:
浏览器
-> HTTPS 443
-> Caddy
-> Guacamole Web / Tomcat 8080
-> guacd 4822(仅 Docker 内网)
-> RDP 3389 / SSH 22 / VNC 5900 等目标主机
Guacamole Web
-> MariaDB 3306(仅 Docker 内网)
这里最关键的安全边界是:公网只看到 Caddy 的 80/443,Guacamole 8080 只绑定 VPS 回环地址,guacd:4822 和数据库 3306 都不映射到宿主机公网。
Apache 官方明确提醒,guacd 是被动代理,不执行任何身份认证。如果把 4822 暴露给不可信网络,攻击者可能把它当作进入其他系统的跳板。因此本文不会给 guacd 配置 ports。
| 方案 | 适合场景 | 客户端要求 | 主要风险或限制 |
|---|---|---|---|
| Apache Guacamole | 多用户、多协议、浏览器统一入口 | 现代浏览器 | 网关成为高价值入口,必须加 MFA 与权限隔离 |
| WireGuard | 自己的设备进入可信私网 | 安装 VPN 客户端 | 用户和连接管理不如 Guacamole 直观 |
| 直接公网 RDP/VNC | 临时测试 | 原生客户端 | 暴露高风险端口,不建议长期使用 |
| SSH 跳板机 | Linux 运维、命令行 | SSH 客户端 | 不直接解决图形桌面和普通用户访问 |
| 商业堡垒机/PAM | 企业审计、审批、凭据托管 | 取决于产品 | 成本和部署复杂度更高 |
如果只有自己三两台设备,先通过WireGuard VPN进入私网,往往更简单。Guacamole 更适合需要浏览器访问、集中分配连接、多人账号、连接历史或 RDP/VNC/SSH 混合管理的场景。
Guacamole 也不能替代目标主机安全:Windows 仍要更新补丁、启用 NLA 和强密码;Linux 仍应限制 SSH 用户和 sudo 权限;VNC 仍需可靠认证。网关只改变访问路径,不会修复后端服务漏洞。
Guacamole 本身不保存完整桌面系统,但需要转码屏幕更新、处理音频、剪贴板、文件传输和多个并发连接。延迟与带宽通常比磁盘容量更直接影响操作体验。
| 使用规模 | 建议起步配置 | 网络与存储重点 |
|---|---|---|
| 1–3 个轻量 SSH/RDP 会话 | 2 vCPU、4GB 内存、40GB SSD | 低延迟、稳定线路 |
| 5–10 个并发办公会话 | 4 vCPU、8GB 内存、80GB SSD | 100Mbps 以上、持续 CPU 性能 |
| 高清桌面、音频或多人并发 | 8 vCPU、16GB 内存起 | 高带宽、低抖动、实测容量规划 |
| 开启会话录制 | 在上述配置上增加独立数据盘 | 录制文件增长、备份和保留策略 |
屏幕分辨率、刷新频率、视频内容、音频和网络质量都会改变实际流量。浏览静态管理界面可能只需较低带宽,播放视频或频繁滚动则会大幅增加数据。不要只按“一个用户固定多少 Mbps”购买,应先用真实业务压测,再通过监控观察峰值和月流量。
机房应同时靠近用户和目标主机。访问路径是“用户 → Guacamole VPS → 目标主机”,任一段高延迟都会叠加。若用户在亚洲而目标主机在欧洲,把网关放在美洲通常不会改善体验。
本文使用:
| 端口 | 用途 | 公网策略 |
|---|---|---|
22/TCP | VPS 自身 SSH | 最好限制管理员来源 IP |
80/443 TCP | Caddy 与 HTTPS | 对需要访问的用户开放 |
8080/TCP | Guacamole Web | 仅绑定 127.0.0.1 |
4822/TCP | guacd | 仅 Docker 网络,不映射 |
3306/TCP | MariaDB | 仅 Docker 网络,不映射 |
目标主机的 3389、22 或 5900 也不需要向全网开放。若目标在另一台公网服务器,防火墙只允许 Guacamole VPS 的固定 IP;若目标在家庭或办公室内网,可以先建立 WireGuard、站点到站点 VPN 或其他受控隧道。
更新 Ubuntu:
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg openssl ufw
按 Docker 官方仓库安装 Docker Engine 与 Compose 插件后检查:
docker version
docker compose version
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
生产环境的日志轮转、资源限制、回滚和密钥管理可以配合VPS Docker Compose 生产环境配置清单检查。
创建项目目录:
sudo install -d -m 750 /opt/guacamole
sudo chown "$(id -u):$(id -g)" /opt/guacamole
cd /opt/guacamole
截至 2026 年 8 月,Apache Guacamole 最新正式版为 1.6.0。本文显式固定 Web 与 guacd 镜像版本,避免 latest 在无人注意时跨版本变化。部署前仍应查看Apache Guacamole Release Archives确认是否已有更新。
使用官方镜像内置脚本生成 MariaDB 初始化 SQL:
docker run --rm guacamole/guacamole:1.6.0 \
/opt/guacamole/bin/initdb.sh --mysql > initdb.sql
test -s initdb.sql
chmod 600 initdb.sql
head -n 5 initdb.sql
Guacamole 不会自动创建 JDBC 认证表,必须先初始化数据库。这个 SQL 文件会在 MariaDB 数据目录第一次为空时执行;如果以后复用已有数据库卷,Docker 入口脚本不会再次执行它。
使用随机值创建 /opt/guacamole/.env:
cd /opt/guacamole
umask 077
GUAC_DB_PASSWORD=$(openssl rand -hex 32)
GUAC_ROOT_PASSWORD=$(openssl rand -hex 32)
{
printf 'MARIADB_DATABASE=guacamole_db\n'
printf 'MARIADB_USER=guacamole_user\n'
printf 'MARIADB_PASSWORD=%s\n' "$GUAC_DB_PASSWORD"
printf 'MARIADB_ROOT_PASSWORD=%s\n' "$GUAC_ROOT_PASSWORD"
} > .env
unset GUAC_DB_PASSWORD GUAC_ROOT_PASSWORD
chmod 600 .env
确认变量名存在,但不要把密码值打印到共享终端或截图:
cut -d= -f1 .env
.env、数据库备份和 Guacamole 连接配置都可能包含敏感信息,不能提交到公开 Git 仓库。
创建 /opt/guacamole/compose.yaml:
services:
mariadb:
image: mariadb:11.4
container_name: guacamole-mariadb
environment:
MARIADB_ROOT_PASSWORD: "${MARIADB_ROOT_PASSWORD}"
MARIADB_DATABASE: "${MARIADB_DATABASE}"
MARIADB_USER: "${MARIADB_USER}"
MARIADB_PASSWORD: "${MARIADB_PASSWORD}"
volumes:
- mariadb-data:/var/lib/mysql
- ./initdb.sql:/docker-entrypoint-initdb.d/001-guacamole.sql:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
restart: unless-stopped
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: 10m
max-file: "3"
guacd:
image: guacamole/guacd:1.6.0
container_name: guacamole-guacd
environment:
LOG_LEVEL: info
restart: unless-stopped
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: 10m
max-file: "3"
guacamole:
image: guacamole/guacamole:1.6.0
container_name: guacamole-web
depends_on:
mariadb:
condition: service_healthy
guacd:
condition: service_started
environment:
GUACD_HOSTNAME: guacd
MYSQL_ENABLED: "true"
MYSQL_HOSTNAME: mariadb
MYSQL_DATABASE: "${MARIADB_DATABASE}"
MYSQL_USERNAME: "${MARIADB_USER}"
MYSQL_PASSWORD: "${MARIADB_PASSWORD}"
WEBAPP_CONTEXT: ROOT
REMOTE_IP_VALVE_ENABLED: "true"
TOTP_ENABLED: "true"
BAN_ENABLED: "true"
ports:
- "127.0.0.1:8080:8080"
restart: unless-stopped
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: 10m
max-file: "3"
volumes:
mariadb-data:
这份配置刻意做了以下限制:
- Guacamole Web 只映射到
127.0.0.1:8080; guacd没有宿主机端口映射,只允许 Compose 网络中的 Web 应用访问;- MariaDB 没有公网端口,只保存到命名卷;
- 数据库初始化 SQL 以只读方式挂载;
- Web、guacd 与数据库都设置日志轮转;
- Guacamole 开启 TOTP,并保留 1.6.0 Docker 镜像默认提供的登录失败封禁;
RemoteIpValve让反向代理后的应用能够识别真实客户端 IP,否则所有失败登录可能看起来都来自 Caddy。
WEBAPP_CONTEXT=ROOT 让登录页直接位于 /,而不是默认的 /guacamole/。如果删除这个变量,Caddy 和访问 URL 都要改成对应子路径,不能只改一边。
先让 Compose 展开并校验配置:
cd /opt/guacamole
docker compose config > /tmp/guacamole-compose-resolved.yaml
docker compose pull
docker compose up -d
docker compose ps
查看启动日志:
docker compose logs --tail=200 mariadb
docker compose logs --tail=200 guacd
docker compose logs --tail=200 guacamole
检查数据库表:
docker compose exec mariadb sh -c \
'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SHOW TABLES;"'
应能看到 guacamole_user、guacamole_connection 等表。如果表为空,优先检查 MariaDB 是否在生成 initdb.sql 之前已经创建过数据卷。初始化脚本只在空数据目录首次启动时执行,不应通过反复重启碰运气。
在 VPS 本机测试 Web:
curl -I http://127.0.0.1:8080
sudo ss -lntp | grep ':8080'
此时 http://VPS-IP:8080 从公网无法访问是正确结果。
在个人电脑通过 SSH 隧道临时访问:
ssh -L 8080:127.0.0.1:8080 [email protected]
203.0.113.10 是文档示例地址,需要替换为 VPS 真实公网 IP。保持 SSH 会话后,浏览器打开:
http://127.0.0.1:8080
官方数据库初始化 SQL 会创建默认管理员:
用户名:guacadmin
密码:guacadmin
这组默认凭据是公开信息,只用于第一次登录。进入系统后立即:
- 把
guacadmin密码改为密码管理器生成的随机强密码; - 完成 TOTP 绑定,并安全保存恢复流程;
- 创建新的日常管理员账号;
- 创建非管理员测试用户;
- 验证新账号可登录后,停止日常使用默认管理员;
- 不要为普通用户授予创建用户、修改系统或管理全部连接的权限。
若还没修改默认密码,不要把 HTTPS 域名开放给全网,也不要开始创建后端连接。
把 remote.example.com 的 DNS A/AAAA 记录指向 VPS。示例域名必须换成你自己的实际域名。
在 Caddyfile 中增加:
remote.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
}
Caddy 会自动处理 Guacamole 需要的 WebSocket 代理,一般不需要手写 Upgrade 头。校验并重载:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
sudo journalctl -u caddy -n 100 --no-pager
开放 Web 端口:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp comment 'Caddy HTTP'
sudo ufw allow 443/tcp comment 'Caddy HTTPS'
sudo ufw enable
sudo ufw status numbered
不要开放 8080、4822 或 3306。Caddy 的完整安装、DNS 和自动证书流程可参考VPS 用 Caddy 反向代理完全指南。
Guacamole 官方建议通过反向代理终止 TLS。REMOTE_IP_VALVE_ENABLED=true 会让 Tomcat 使用代理传入的客户端地址和协议信息,这对登录封禁、审计和基于来源 IP 的限制都很重要。
登录管理界面后,在设置中创建 Connection。先从低权限测试账号和非生产目标开始。
常用参数:
Protocol: SSH
Hostname: 10.20.0.10
Port: 22
Username: ops-test
优先使用独立低权限用户和 SSH Key。不要把 VPS 的 root 私钥复制到所有连接,也不要给普通 Guacamole 用户共享高权限连接。
常用参数:
Protocol: RDP
Hostname: 10.20.0.20
Port: 3389
Username: remote-user
Domain: 按实际环境填写
Security mode: NLA
Windows 目标应开启 NLA、系统更新、账户锁定和防火墙限制。若目标使用自签名 RDP 证书,先核对证书与目标身份,不要把“忽略证书错误”当成长期默认设置。
Protocol: VNC
Hostname: 10.20.0.30
Port: 5900
VNC 实现和加密能力差异很大。让 Guacamole 与 VNC 目标通过 VPN 或可信内网通信,不要在公网直接开放 5900。
如果目标主机无法连接,先在 Guacamole VPS 测试网络,而不是反复修改 Web 配置:
nc -vz 10.20.0.10 22
nc -vz 10.20.0.20 3389
nc -vz 10.20.0.30 5900
公网目标的防火墙只应允许 Guacamole VPS 来源 IP。端口监听、UFW 与云安全组可以按服务启动但外网访问不了排查清单逐层核对。
不要把所有人都设为管理员,也不要让所有用户共享同一个目标系统账号。
推荐分层:
- 系统管理员:只负责 Guacamole 配置和审计;
- 连接管理员:管理指定连接,不管理全部系统用户;
- 普通用户:只能使用分配给自己的连接;
- 只读或演示用户:禁用不需要的剪贴板、文件传输和输入能力;
- 临时用户:设置明确失效时间,任务结束后撤销。
敏感连接应考虑:
- 不在 Guacamole 中永久保存目标密码,改为登录时输入;
- 每个用户使用独立目标账号,便于审计和吊销;
- 关闭不需要的剪贴板、磁盘映射、打印、音频和文件传输;
- 将生产、测试和客户环境放入不同连接组;
- 限制用户可以登录的时间与来源网段;
- 定期检查 Active Sessions 和 History。
管理员权限应尽量少。系统管理员通常不受部分登录限制影响,不能把基于时间或来源地址的限制当成管理员账号的唯一防线。
Guacamole 1.6.0 官方 Docker 镜像支持通过 TOTP_ENABLED=true 启用标准 TOTP。本文同时启用数据库认证与 TOTP,因此用户输入密码后还需要动态验证码。
上线前至少测试:
- 管理员和普通用户都完成 TOTP 注册;
- 服务器时间准确,TOTP 不因时钟漂移持续失败;
- 丢失手机时有受控恢复流程;
- 恢复操作需要管理员核验,而不是关闭整个 MFA;
- 反向代理传入的客户端 IP 正确。
Guacamole 1.6.0 Docker 镜像默认带有登录失败封禁扩展。官方默认阈值为同一来源 5 次失败,封禁 300 秒。它能降低在线猜密码频率,但不能替代强密码、TOTP、来源限制和日志监控。
查看失败登录与封禁相关日志:
docker compose logs --since=1h guacamole | grep -i -E 'auth|fail|ban|invalid'
如果所有请求都显示为 127.0.0.1 或同一个 Docker 地址,先修复反向代理客户端 IP 传递,再配置基于来源地址的策略。
Guacamole 的用户、连接、权限和历史主要位于数据库。只备份 compose.yaml 不能恢复这些数据。
创建备份:
cd /opt/guacamole
backup_stamp=$(date +%Y%m%d-%H%M%S)
sudo install -d -m 700 /var/backups/guacamole
docker compose exec -T mariadb sh -c \
'mariadb-dump -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$MARIADB_DATABASE"' \
| gzip > "/var/backups/guacamole/database-${backup_stamp}.sql.gz"
sudo tar -C /opt/guacamole -czf \
"/var/backups/guacamole/config-${backup_stamp}.tar.gz" \
compose.yaml .env initdb.sql
sudo chmod 600 /var/backups/guacamole/*
校验备份:
gzip -t "/var/backups/guacamole/database-${backup_stamp}.sql.gz"
sudo tar -tzf "/var/backups/guacamole/config-${backup_stamp}.tar.gz"
ls -lh /var/backups/guacamole
备份文件包含密码哈希、连接信息和数据库凭据,必须加密传输并限制访问。不要只把备份留在同一块 VPS 磁盘;完整恢复演练可参考VPS 备份恢复演练教程。
更新前查看 Guacamole Release Notes。数据库 Schema 可能需要按版本顺序执行升级 SQL,不能只修改镜像标签后直接启动。
记录当前状态:
cd /opt/guacamole
docker compose images
docker image inspect guacamole/guacamole:1.6.0 \
--format '{{index .RepoDigests 0}}'
docker image inspect guacamole/guacd:1.6.0 \
--format '{{index .RepoDigests 0}}'
docker compose ps
更新流程:
- 选择维护窗口,通知正在使用的用户;
- 停止新连接,确认活动会话已经结束;
- 备份 MariaDB、
.env、Compose 和初始化文件; - 阅读从当前版本到目标版本的全部 Schema Upgrade 说明;
- 下载新镜像并验证版本;
- 按官方顺序执行数据库升级 SQL;
- 更新 Web 与 guacd 为相同版本;
- 重建容器,测试登录、TOTP、SSH、RDP、VNC 和审计历史;
- 保留旧镜像与备份,确认稳定后再清理。
Guacamole 1.x 组件具有一定协议兼容性,但 Web、guacd、扩展和数据库 Schema 长期保持同一版本更容易维护。第三方扩展必须单独确认 API 兼容性。
cd /opt/guacamole
docker compose ps
docker compose logs --tail=200 guacamole
docker compose logs --tail=200 mariadb
docker compose config
常见原因是 .env 变量缺失、数据库密码不一致、数据库尚未健康、初始化表不存在,或镜像版本与扩展不匹配。
检查数据库是否真的初始化:
docker compose exec mariadb sh -c \
'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SHOW TABLES; SELECT username FROM guacamole_entity WHERE type = '\''USER'\'';"'
如果库为空,不要在已有数据卷上反复改 initdb.sql。先查清数据卷是否包含需要保留的数据;只有确定是全新失败部署时,才考虑重新初始化。
curl -I http://127.0.0.1:8080
sudo systemctl status caddy --no-pager
sudo journalctl -u caddy -n 100 --no-pager
docker compose logs --tail=200 guacamole
sudo ss -lntp | grep -E ':443|:8080'
本机 8080 失败说明问题在 Guacamole 容器;本机成功而域名 502,更可能是 Caddy、DNS 或代理目标。远程桌面几分钟后断开,还要检查上游 CDN、代理超时和客户端网络。
先在 VPS 测试目标端口:
nc -vz 10.20.0.10 22
nc -vz 10.20.0.20 3389
nc -vz 10.20.0.30 5900
docker compose logs --tail=200 guacd
如果 TCP 不通,排查目标主机监听地址、系统防火墙、安全组、VPN 路由和来源 IP 白名单。如果 TCP 能通而协议失败,再检查用户名、域、NLA、证书、VNC 认证类型或 SSH Key 格式。
timedatectl status
date -u
docker compose logs --tail=200 guacamole
确保 VPS 与手机时间自动同步。不要通过关闭 TOTP 来掩盖时间或账户绑定问题;应使用受控管理员恢复流程重新注册。
分别观察两段网络和服务器资源:
docker stats
ping -c 20 10.20.0.20
sudo apt install -y sysstat
iostat -xz 1 10
free -h
降低远程分辨率、色深,关闭不需要的音频、打印和磁盘映射,再测试延迟。若 VPS 到目标主机的线路差,单纯给 VPS 增加 CPU 不会解决网络抖动。
- 使用当前受支持的 Guacamole 正式版和相同版本的 Web、guacd;
- 采用数据库认证,没有把
user-mapping.xml当成公网生产方案; - 默认
guacadmin / guacadmin已立即修改; - 管理员与普通用户均启用 TOTP;
REMOTE_IP_VALVE_ENABLED=true,审计能看到真实客户端 IP;8080只绑定127.0.0.1;4822和3306没有宿主机端口映射;- Caddy HTTPS 正常,HTTP 自动跳转 HTTPS;
- 目标 RDP、SSH、VNC 只允许 Guacamole VPS 或 VPN 网段访问;
- 普通用户没有系统管理和全部连接权限;
- 不需要的剪贴板、文件传输、打印和磁盘映射已关闭;
- 数据库、
.env和连接配置有加密备份; - 已测试数据库恢复、TOTP 恢复和镜像回滚;
- 登录失败、活动会话、CPU、内存、带宽和磁盘有监控;
- 删除临时用户、旧连接和离职人员权限有固定流程。
不需要。普通用户通过支持 HTML5 和 WebSocket 的现代浏览器访问。目标主机仍需启用对应的 RDP、SSH 或 VNC 服务。
不能完全代替。Guacamole 提供具体远程连接的浏览器入口;VPN 提供网络层私网访问。对家庭内网或跨云目标,常见做法是先让 Guacamole VPS 通过 WireGuard 进入目标网段,再由 guacd 连接内部地址。
guacd 本身不验证用户身份,是被动远程桌面代理。把它暴露给不可信网络可能让攻击者利用它连接其他目标。Docker 部署中应只让 Guacamole Web 容器访问。
少量轻量 SSH 会话可以测试,但 MariaDB、Tomcat、guacd 和系统都需要内存。用于长期 RDP/VNC 访问时建议从 4GB 起步,并根据并发、分辨率和音频实测扩容。
技术上可以,但会扩大网关泄露后的影响。敏感连接优先让用户每次输入凭据,或使用企业身份、专用低权限账号和受支持的密钥管理方案。
Guacamole Web 使用 HTTP/HTTPS 和 WebSocket,技术上可以经过支持 WebSocket 的代理,但长时间会话、超时、日志、访问策略和服务条款必须实测。无论是否使用 CDN,都不要通过它暴露 guacd:4822 或数据库。
官方 JDBC 初始化 SQL 创建 guacadmin / guacadmin。它只能用于首次登录,必须在公网开放前立即更换,并启用 TOTP。
Guacamole 部署完成的标准不是“登录页能打开”,而是公网只暴露 HTTPS、默认凭据已更换、TOTP 与封禁生效、真实客户端 IP 可审计、目标端口没有全网暴露、普通用户权限最小化,并且数据库备份和恢复真正演练过。先用一台低权限测试主机跑通完整链路,再逐步接入生产系统。
