ubuntu部署Guacamole,Guacamole集成springboot,vue
本文主要介绍的是如何docker安装Guacamole,进行浏览器远程桌面的访问。
前言
本文主要介绍的是如何docker安装Guacamole,进行浏览器远程桌面的访问。
Guacamole
Apache Guacamole 是一个无客户端远程桌面网关。它支持标准协议,如 VNC、RDP 和 SSH。我们称之为无客户端,因为不需要插件或客户端软件。
用户使用他们的网络浏览器连接到 Guacamole 服务器。用 JavaScript 编写的 Guacamole 客户端由 Guacamole 服务器内的网络服务器提供给用户。加载后,此客户端使用 Guacamole 协议通过 HTTP 连接回服务器。部署到 Guacamole 服务器的 Web 应用程序读取 Guacamole 协议并将其转发到 guacd,即原生的 Guacamole 代理。这个代理实际上解释了 Guacamole 协议的内容,代表用户连接到任意数量的远程桌面服务器。Guacamole 协议与 guacd 结合提供了协议不可知性:Guacamole 客户端和 Web 应用程序都不需要知道实际使用的是什么远程桌面协议
协议
Web 应用程序根本不了解任何远程桌面协议。它不包含对 VNC 或 RDP 或由 Guacamole堆栈支持的任何其他协议的支持。它其实只懂Guacamole协议,这是一个用于远程显示渲染和事件传输的协议。虽然具有这些属性的协议自然具有与远程桌面协议相同的能力,但远程桌面协议和 Guacamole 协议背后的设计原则是不同的:Guacamole 协议并非旨在实现特定桌面环境的功能。
作为远程显示和交互协议,Guacamole 实现了现有远程桌面协议的超集。因此,向 Guacamole 添加对特定远程桌面协议(如RDP)的支持涉及编写一个中间层,在远程桌面协议和 Guacamole 协议之间进行“转换”。实现这样的转换与实现任何本地客户端没有什么不同,只是这个特定的实现呈现到远程显示器而不是本地显示器。
处理这种翻译的中间层是 guacd
##guacd
guacd 是 Guacamole 的核心,它动态加载对远程桌面协议(称为“客户端插件”)的支持,并根据从 Web 应用程序收到的指令将它们连接到远程桌面。 guacd 是一个守护进程,它与 Guacamole 一起安装并在后台运行,监听来自 Web 应用程序的 TCP 连接。guacd 也不理解任何特定的远程桌面协议,而是实现了足够的 Guacamole 协议来确定需要加载哪些协议支持以及必须将哪些参数传递给它。加载客户端插件后,它会独立于 guacd 运行,并完全控制自身与 Web 应用程序之间的通信,直到客户端插件终止。 guacd 和所有客户端插件都依赖于一个公共库 libguac,这使得通过 Guacamole
协议进行的通信更容易也更抽象一些。
UltraVNC
UltraVNC是一款功能强大、易于使用且免费的远程 PC 访问软件,它可以在您自己的屏幕上显示另一台计算机的屏幕(通过互联网或网络)。该程序允许您使用鼠标和键盘远程控制另一台 PC。这意味着您可以在远程计算机上工作,就好像您坐在它前面一样,就在您当前的位置。
VNC,即远程帧缓冲协议 (RFB),允许通过 Internet 远程查看和控制桌面。VNC 服务器必须运行在共享桌面的计算机上,VNC 客户端必须运行在将访问共享桌面的计算机上
安装 Guacamole 所需的环境
本文编写环境: window11专业版 + VMware + Ubuntu官网最新版镜像虚拟机( 22.04 LTS)
docker安装教程: https://www.runoob.com/docker/ubuntu-docker-install.html
官网使用docker安装: https://guacamole.apache.org/doc/gug/guacamole-docker.html
docker推荐使用官方安装脚本自动安装
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
输入以上命令即可
接下来就可以输入docker的命令进行测试了
安装Guacamole
安装大体可以分为4部分:
- 安装Guacamole镜像 + 需要的数据库镜像(mysql/PostgreSQL)
- 由Guacamole镜像生成容器
- 初始化数据库 + 关联各个容器
- 启动容器
安装Guacamole镜像
docker pull guacamole/guacd
docker pull guacamole/guacamole
docker pull mysql
通过docker images
命令可以看到目前已经有了3个镜像
guacd容器初始化
docker run --name guacd -d guacamole/guacd
guacd 将在其默认端口 4822 上进行侦听
mysql容器初始化
docker run -itd --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql
- -p 3306:3306 :映射容器服务的 3306 端口到宿主机的 3306 端口,外部主机可以直接通过 宿主机ip:3306 访问到 MySQL 的服务。
- MYSQL_ROOT_PASSWORD=123456:设置 MySQL 服务 root 用户的密码。
通过 docker ps 命令查看mysql
和guacd
是否安装成功
由于Guacamole
需要使用数据库来进行身份验证,所以我们还需要对mysql数据写入一个供guacamole
访问的用户和数据库
guacamole
提供一个生成自己所需的sql文件供我们使用,使用以下的命令:
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql > initdb.sql
此命令会在你当前的文件夹下生成一个initdb.sql
文件,比如你在桌面目录下执行的,那么你桌面上就会生成这样的一个文件
生成此脚本后,我们需要完成:
- 为 Guacamole 创建一个数据库,例如
guacamole_db
. - 为 Guacamole 创建一个可以访问此数据库的用户,例如
guacamole_user
. - 在新创建的数据库上运行脚本
现在我们进入数据库:
//进入mysql容器
docker exec -it mysql /bin/bash
//进入数据库
mysql -uroot -p123456
//创建数据库
CREATE DATABASE guacamole_db;
//创建供Guacamole访问的用户,并赋予权限
//这里因为我们的mysql是用的容器,如果用@'localhost'到后面我们用宿主机进入后台的时候,是无法访问的,我们应该用'%'这个权限
//如果你的mysql是用的宿主机的mysql,那么可以试试localhost
CREATE USER 'guacamole_user'@'%' IDENTIFIED BY 'some_password';
GRANT SELECT,INSERT,UPDATE,DELETE ON guacamole_db.* TO 'guacamole_user'@'%';
//刷新用户
FLUSH PRIVILEGES;
//退出数据库
exit
//退出mysql容器内部
exit
//执行把宿主机的sql文件copy到mysql容器内部
//docker cp 要拷贝的文件路径 容器名:要拷贝到容器里面对应的路径
//我这里是因为我的文件就在桌面,如果你在其他目录下,填写路径就可以比如: /home/initdb.sql
docker cp initdb.sql mysql:/
//这个时候进入mysql容器查看文件
docker exec -it mysql bash
//此时应该就有这个sql文件了
ls
//执行mysql执行sql脚本
//语法: mysql –u用户名 –p密码 –D数据库 < [sql脚本文件路径全名]
mysql -uroot -p123456 -Dguacamole_db<initdb.sql
//查看脚本是否执行成功
//进入数据库
mysql -uroot -p123456
//查看数据库
show databases;
//进入guacamole_db数据库
use guacamole_db;
//查看guacamole_db表
show tables;
//如果看到十几个表就证明执行成功了
//退出mysql和容器
exit
exit
guacamole容器初始化
//请先不要执行,先看参数解析
docker run --name guacamole --restart=always --link guacd:guacd --link mysql:mysql -e MYSQL_DATABASE=guacamole_db -e MYSQL_USER=guacamole_user -e MYSQL_PASSWORD=some_password -d -p 9090:8080 guacamole/guacamole
--link guacd:guacd
使Guacamole容器和guacd容器关联,这个地方冒号前面的guacd是你创建这个容器时指定的name参数--link mysql:mysql
使Guacamole容器和mysql容器管理,这个地方冒号前面的guacd是你创建这个容器时指定的name参数-e MYSQL_DATABASE=guacamole_db
上面初始化数据库时,创建的guacamole_db数据库-e MYSQL_USER=guacamole_user
上面初始化用户时,创建的guacamole_user用户-e MYSQL_PASSWORD=song_password
上面初始化用户密码时,创建的some_password密码-p 9090:8080
绑定宿主机的9090端口
如果上面的操作中,创建的用户名、数据库名、密码不同的话请自行更改
其他额外的一些参数,如果严格按照上文的步骤是不需要的:
MYSQL_HOSTNAME
用于 Guacamole 身份验证的数据库的主机名。如果您不使用 Docker 来提供 MySQL 数据库,则这是必需的
MYSQL_PORT
Guacamole 在连接到 MySQL 时应该使用的端口。此环境变量是可选的。如果未提供,将使用标准 MySQL 端口 3306
此时可以执行docker ps
查看三个容器是否都正常执行
如果都启动成功了,那么就可以在宿主机上打开浏览器输入http://localhost:9090/guacamole,来进入后台管理页面,默认账号密码都是guacadmin
常见打不开后台报错
- 如果3个容器全部都正常启动,那么还是访问不了后台,比如我们访问localhost:9090也没有显示tomcat的404的标志,那可能是宿主机的端口映射没有被容器的端口映射上,如果你也是虚拟机环境,试试重启一下
重启之后我们需要重启启动容器
docker ps -a
docker start mysql
docker start guacd
docker start guacamole
docker ps -a
- 如果不能访问后台,我们需要先检查数据库的初始化表是否都正常初始化,用我们创建的用户,登录一遍mysql看看是否正确
如果mysql没有问题,那么我们就需要看看guacamole
容器的报错日志
docker logs 容器id
看看日志里面有没有什么报错,比如无法连接数据库之类的问题
- Cause: java.sql.SQLException: Access denied for user ‘guacamole_user’@‘172.17.0.4’ (using password: YES)
解决:如果在mysql创建用户的时候,权限使用的localhost的话,使用docker-mysql容器连接的话,是代表不了localhost的,需要使用%
- 如果我们挂起了虚拟机,然后再次使用的时候,会发现打不开后台了,这个时候也是需要重新启动下虚拟机,然后重启下容器即可
##使用DRP方式测试远程连接
云服务器配置:win10服务器版本 非window版本可以试试SSH连接
点击设置
创建一个RDP连接
编辑连接:
- 名称:随便起
- 位置:用默认的root就行
- 协议:RDP
参数:
网络:
- 主机名: 要被连接的主机ip
- 端口:支持RDP连接的window端口都默认是3389
认证:
- 用户名:远程服务器的用户名,云服务器默认用户名为: Administrator
- 密码:服务器密码
- 忽略服务器证书:这个要勾上
直接到最下面保存即可
来到首页,找到我们刚刚新建的连接,双击即可,等待连接成功
使用VNC方式测试远程连接
云服务器环境:window10服务器版本 非window版本可以试试SSH连接
创建一个VNC连接
编辑连接:
- 名称:随便起
- 位置:用默认的root就行
- 协议:VNC
参数:
网络:
- 主机名: 要被连接的主机ip
- 端口:安装的VNCServe 默认端口是5900
认证:
- 用户名:远程服务器的用户名,云服务器默认用户名为: Administrator
- 密码:安装的VNCServe里面的VNC密码
直接到最下面保存即可
这个时候我们只是创建了一个连接,我们还需要在被远程的机器上安装UltraVNC工具,下文有一章会介绍如何安装使用
使用DRP方式连接内网window机器
测试window版本:win10 + 专业版本
创建一个DRP连接
编辑连接:
- 名称:随便起
- 位置:用默认的root就行
- 协议:DRP
参数:
网络:
- 主机名: 要被连接的主机ip
- 端口:默认3389
- 忽略服务器证书:这个要勾上
认证:
- 用户名:电脑用户名,
- 密码:电脑密码
直接到最下面保存即可
这个地方如果你要访问的机器没有用户名和密码全部空着即可
如果访问不通,请把被访问机器里面的防火墙关掉或者把3389这个端口开通
打开被访问机器里面关于运行被远程控制等选项
win10家庭版不支持远程,所以无法使用DRP和VNC方式
如果有绑定微软账号,那么用户名其实是你微软账号的用户名,密码同理,如果你电脑开通了PIN登录,但是你需要输入的密码还是微软账号的密码
如果我们附加没有第二台机器供我们测试,我们可以测试用虚拟机里面的机器远程连接我们本机,PRD的方式会在连接成功后的几秒显示有冲突而退出PRD,VNC方式可以套娃一直连接
使用VNC方式连接内网window机器
测试window版本:win10 + 专业版本
创建一个VNC连接
编辑连接:
- 名称:随便起
- 位置:用默认的root就行
- 协议:VNC
参数:
网络:
- 主机名: 要被连接的主机ip
- 端口:安装的VNCServe 默认端口是5900
认证:
- 用户名:电脑用户名,
- 密码:安装的VNCServe里面的VNC密码
直接到最下面保存即可
这个地方如果你要访问的机器没有用户名和密码全部空着即可
如果访问不通,请把被访问机器里面的防火墙关掉或者把3389这个端口开通
打开被访问机器里面关于运行被远程控制等选项
win10家庭版不支持远程,所以无法使用DRP和VNC方式
如果有绑定微软账号,那么用户名其实是你微软账号的用户名,密码同理,如果你电脑开通了PIN登录,但是你需要输入的密码还是微软账号的密码
如果我们附近没有第二台机器供我们测试,我们可以测试用虚拟机里面的机器远程连接我们本机,PRD的方式会在连接成功后的几秒显示有冲突而退出PRD,VNC方式可以套娃一直连接
window安装UltraVNC
官网下载合适的版本即可: https://uvnc.com/
一直next即可
安装之后,他会在本地启一个uvnc_service 服务
我们打开安装 UltraVNC Server 软件,我们底部任务栏会出现他的图标,在他的图标上右边打开Admin properties
- VNC Password:VNC连接时的密码
- View-Only Password:连接后只允许看的密码
- 默认VNC的连接端口
更改完之后直接apply-ok即可
集成Guacamole API
需要的环境
https://guacamole.apache.org/api-documentation/
我们集成的话需要上面链接里面的 Java (guacamole-common)
JavaScript (guacamole-common-js)
两个包
-
Java (guacamole-common)
这个使用maven下载即可
<dependency> <groupId>org.apache.guacamole</groupId> <artifactId>guacamole-common</artifactId> <version>1.4.0</version> </dependency>
-
JavaScript (guacamole-common-js)
这个包也是有maven,但是我本机下不下来,我看其他人也有这种情况,所以此包在下文中采用下载包的形式
https://search.maven.org/artifact/org.apache.guacamole/guacamole-common-js/1.4.0/pom
选择下载zip包解压,即可
###Servlet项目集成
-
先新建一个Servlet项目
-
放入maven Guacamole依赖
<!-- Main Guacamole library -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>1.4.0</version>
</dependency>
- 创建相应文件
- 创建
TutorialGuacamoleTunnelServlet
类 - 在webapp目录下,放入上文下载的js压缩包里面的内容(里面的js其实真正用到的只有
all.js
和all.min.js
这两个选择一个使用即可) - 创建
index.html
文件
- 填充文件内容
TutorialGuacamoleTunnelServlet
文件内容:
public class TutorialGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet {
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
//此方法为http的连接方式,如果是websocker方式,请继承相关的类`GuacamoleWebSocketTunnelEndpoint`
// Create our configuration
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setProtocol("rdp");
config.setParameter("hostname", "自己测试的远程ip");
config.setParameter("port", "测试连接端口");
config.setParameter("username", "用户名");
config.setParameter("password", "密码");
config.setParameter("ignore-cert", "true");
// Connect to guacd - everything is hard-coded here.
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket("运行guacd的机器ip", 4822),
config
);
// Return a new tunnel which uses the connected socket
return new SimpleGuacamoleTunnel(socket);
}
}
web.xml
文件内容
<!-- Basic config -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!-- Guacamole Tunnel Servlet -->
<servlet>
<description>Tunnel servlet.</description>
<servlet-name>Tunnel</servlet-name>
<servlet-class>
<!-- TutorialGuacamoleTunnelServlet类的具体路径,具体到类名 -->
com.example.e.类名
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Tunnel</servlet-name>
<url-pattern>/tunnel</url-pattern>
</servlet-mapping>
index.html
文件内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript"
src="guacamole-common-js/all.min.js"></script>
<!-- Display -->
<div id="display"></div>
<script type="text/javascript"> /* <![CDATA[ */
// Get display div from document
var display = document.getElementById("display");
// Instantiate client, using an HTTP tunnel for communications.
var guac = new Guacamole.Client(
new Guacamole.HTTPTunnel("tunnel")
);
// Add client to display div
display.appendChild(guac.getDisplay().getElement());
// Error handler
guac.onerror = function (error) {
alert(error);
};
// Connect
guac.connect();
// Disconnect on close
window.onunload = function () {
guac.disconnect();
}
// Mouse
var mouse = new Guacamole.Mouse(guac.getDisplay().getElement());
mouse.onmousedown =
mouse.onmouseup =
mouse.onmousemove = function (mouseState) {
guac.sendMouseState(mouseState);
};
// Keyboard
var keyboard = new Guacamole.Keyboard(document);
keyboard.onkeydown = function (keysym) {
guac.sendKeyEvent(1, keysym);
};
keyboard.onkeyup = function (keysym) {
guac.sendKeyEvent(0, keysym);
};
/* ]]> */</script>
</body>
</html>
- 启动创建项目时配置的tomcat服务,打开tomcat服务配置里面的url,如果连接参数配置正确,此时就可以看到了
springboot + vue 项目集成
springboot + vue项目请自行创建
- 配置maven
<!-- 远程控制-->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>1.4.0</version>
</dependency>
<!-- 添加servlet支持-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
因为api依赖servlet的支持,所以需要额外添加servlet的包,Servlet集成的时候,新建项目自带这个包
- 创建
TutorialGuacamoleTunnelServlet
类,放入合适的包中即可
//配置请求路径 /tunnel
@WebServlet(name = "Tunnel", urlPatterns = "/tunnel")
public class TutorialGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet {
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
GuacamoleConfiguration config = new GuacamoleConfiguration();
//config.setProtocol("vnc");
//根据header参数来进行参数化
config.setProtocol(request.getHeader("loginType"));
config.setParameter("hostname", request.getHeader("hostName"));
config.setParameter("port", request.getHeader("port"));
config.setParameter("username", request.getHeader("userName"));
config.setParameter("password", request.getHeader("password"));
config.setParameter("ignore-cert", request.getHeader("ignore"));
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket("192.168.150.128", 4822),
config
);
return new SimpleGuacamoleTunnel(socket);
}
}
- 在springboot的启动类里面加上注解
//扫描这个目录下的servlet,这个不需要具体到类名,到包名即可
@ServletComponentScan("com.framework.servlet")
- 解除此url的访问后端接口权限(非必要步骤,可省略)因为集成在真实的项目里,而我们项目用的是
spring security
所以需要对这个url进行解除访问限制
//只允许登录用户访问,这个地方需要这个接口认证token,然后认证token,后面会讲
.antMatchers("/tunnel/**").authenticated()
//允许任何人访问
.antMatchers("/tunnel/**").permitAll()
这两个选一个即可
- 前端新建路由配置
{
path: "/remote-page",
//比如我准备展示远程桌面的页面放入了这个路径下面
component: () => import("@/views/business/tunnel/index"),
},
- 请自行在相应的目录下新建页面
- 我这里的逻辑是一个页面有一个登录按钮点击打开新页面,这个新页面就是展示远程桌面的页面,为此打开新页面的页面逻辑如下
//配置内容请自行填写
const loginForm = {
hostName: "0.0.0.0"
ignore: "true"
loginType: "rdp"
password: "123"
port: "3389"
userName: "pass"
}
let openUrl = window.location.origin + "/remote-page?";
for (const [key, value] of Object.entries(loginForm)) {
openUrl += "&" + key + "=" + value;
}
window.open(openUrl, "_blank");
- 展示远程桌面的文件内容
<template>
<div>
<!-- Display -->
<div id="display"></div>
</div>
</template>
<script>
//如果你后端配置的是需要token认证的,请使用你们项目里面自己带的获取token的方法
import { getToken } from "@/utils/auth";
export default {
data() {
return {
params: {},
};
},
mounted() {
this.initUrlParams();
this.initGuacamole();
},
methods: {
// 初始化url
initUrlParams() {
this.params = this.getQueryObject(window.location.href);
//添加token认证
this.params["Authorization"] = "Bearer " + getToken();
},
// 初始化视频
initGuacamole() {
var display = document.getElementById("display");
var guac = new Guacamole.Client(
new Guacamole.HTTPTunnel("tunnel", true, this.params)
);
display.appendChild(guac.getDisplay().getElement());
guac.onerror = (error) => {
if (error.code === 519) {
this.$message.error(
"连接失败,请检查连接参数,10秒后自动关闭本页面!"
);
setTimeout(() => {
window.close();
}, 10000);
} else {
this.$message.error(error);
}
};
guac.connect();
window.onunload = function () {
guac.disconnect();
};
var mouse = new Guacamole.Mouse(guac.getDisplay().getElement());
mouse.onmousedown =
mouse.onmouseup =
mouse.onmousemove =
function (mouseState) {
guac.sendMouseState(mouseState);
};
var keyboard = new Guacamole.Keyboard(document);
keyboard.onkeydown = function (keysym) {
guac.sendKeyEvent(1, keysym);
};
keyboard.onkeyup = function (keysym) {
guac.sendKeyEvent(0, keysym);
};
},
//解析url参数
getQueryObject(url){
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
},
};
</script>
<style lang="scss" scoped></style>
- 引入
guacamole-common-js
原包使用import导入的话会提示找不到Guacamole对象,所以我更改了下源码,建议大家先不改,先试试好不好报错,再来决定需要不需要改
这里我使用的是all.js
把里面的第一行代码
var Guacamole = Guacamole || {};
=>
window.Guacamole = Guacamole || {};
在main.js
里面引入
//请自行更改路径
import "@/utils/guacamole-common-js/all.js";
如果你不想更改源码并且也报错的话,那么可以直接放在public目录下,然后在index.html里面引入
<script src="<%= BASE_URL %>all.js"></script>
- 现在已经可以启动查看了,这中间的步骤如果大家有错误可能确实是我没想起来,没有写上,但是这些步骤已经足够精简了
更多推荐
所有评论(0)