前言
RustDesk 是一个强大的开源远程桌面软件,是中国开发者的作品,它使用 Rust 编程语言构建,提供安全、高效、跨平台的远程访问体验。可以说是目前全球最火的开源远程桌面软件了,GitHub 收藏数量达到了惊人的 92k!
教程功能
简单介绍RustDesk ,对RustDesk 原理进行简单说明
提供linux原生安装和docker安装两种方式自建RustDesk服务器
第三方开源web-api服务(RustDesk开源版本没有权限用户相关管理权限控制,pro版本才有)
如何发布RustDesk 服务端(主要为开放端口说明)
客户端简单使用RustDesk
视频比较细,时长较长,已经分段完成,各位小伙伴可以选择分段观看。
与 TeamViewer、ToDesk 、向日葵等专有远程访问解决方案相比,RustDesk 作为一个开源软件,提供了几个显著的优势:
RustDesk 完全免费使用,没有任何隐藏费用或订阅计划。
由于其开源特性,RustDesk 的代码是透明的,可以由社区审计,从而提供更高的安全性和可信度。
由于RustDesk 允许自建服务器,隐私性更高
RustDesk 自建服务器硬件需求非常低官方说法如下:
硬件要求非常低;基本云服务器的最低配置就足够了,CPU和内存要求极低。您也可以使用树莓派或类似设备。关于网络规模,如果TCP打洞直连失败,将消耗中继流量。中继连接的流量根据分辨率设置和屏幕更新在30 K/s到3 M/s(1920x1080屏幕)之间。如果仅用于办公需求,流量约为100 K/s。
由于被诈骗分子频繁使用,该项目现已暂停国内服务。即国内无法通过公共服务器连接
技术架构对比
功能差异对比
RustDesk 架构概述
RustDesk 采用了经典的客户端-服务器模型,其中涉及三个主要组件:RustDesk 客户端、RustDesk 服务器和 ID Server。
客户端-服务器模型
在 RustDesk 的架构中,客户端是运行在用户设备 (如笔记本电脑、平板电脑或智能手机) 上的应用程序。它提供了一个图形界面,允许用户发起远程访问请求并与远程计算机进行交互。另一方面,服务器组件运行在要远程访问的目标计算机上。它负责监听来自客户端的连接请求,并在建立连接后向客户端发送屏幕更新和接收输入事件。
ID Server 的角色
ID Server 在 RustDesk 的生态系统中扮演着重要的角色。它的主要职责是促进客户端和服务器之间的初始连接建立。当 RustDesk 服务器启动时,它会连接到 ID Server 并注册自己,提供如服务器 ID 和公网 IP 地址等信息。类似地,当客户端想要连接到特定的 RustDesk 服务器时,它会向 ID Server 查询目标服务器的连接信息。
ID Server 维护了一个已注册的 RustDesk 服务器目录,并充当客户端和服务器之间的中介,帮助它们建立直接的点对点 (P2P) 连接。一旦客户端从 ID Server 获得了服务器的连接信息,它就可以尝试直接连接到服务器,而无需进一步通过 ID Server 中继数据。
Relay Server 的角色
在某些网络环境下,RustDesk 客户端和服务器可能无法直接建立 P2P 连接,例如当它们位于 NAT (网络地址转换) 或防火墙后时。为了克服这一挑战,RustDesk 引入了 Relay Server。
如果客户端无法直接连接到服务器,它会向 ID Server 请求一个 Relay Server。然后,客户端和服务器都连接到指定的 Relay Server,并通过它来中继所有的网络通信。Relay Server 在这种情况下充当客户端和服务器之间的桥梁,转发来自一方的数据包到另一方。
值得注意的是,即使通过 Relay Server 进行通信,RustDesk 也会维护端到端加密,确保中继服务器无法访问明文数据。Relay Server 只是盲目地转发加密的数据包,而不能查看或修改其内容。
RustDesk 部署教程
linux原生部署
服务器配置说明:
下载 Rustdesk Server
方法1 本站直接下载
rustdesk-server-linux-amd64.zip
方法2 直接前往官方页面下载
https://github.com/rustdesk/rustdesk-server/releases
方法3 服务器直接或者代理下载
docker github代理地址:https://www.cnproxy.top/
使用githuab代理或者直接下载
wget https://ghproxy.cnproxy.top/https://github.com/rustdesk/rustdesk-se
rver/releases/download/1.1.14/rustdesk-server-linux-amd64.zip
解压Rustdesk Server
apt install unzip
unzip rustdesk-server-linux-amd64.zip
解压完成后会出现hbbr,hbbs,rustdesk-utils三个文件
hbbr:
hbbr
是RustDesk Relay Server,即RustDesk中继服务器,当客户端之间无法直接建立P2P连接时,会通过hbbr
中继服务器进行数据传输hbbr
允许无法直接建立P2P连接的客户端通过中继服务器进行通信。
hbbs:
hbbs
代表RustDesk ID / Rendezvous Server,即RustDesk ID注册服务器。它用于分配和注册ID,并且是RustDesk的中介服务器(Broker Server),用于管理和协调客户端连接。hbbs
帮助客户端找到并建立P2P连接,负责维护客户端的在线状态,并处理连接请求。当客户端A希望连接客户端B时,它会向hbbs
发送请求,hbbs
会帮助它们建立连接。
rustdesk-utils:
rustdesk-utils
是RustDesk的命令行工具,提供了一些管理和操作RustDesk服务器端的工具和命令。
配置系统服务且加入开机自启动
执行vim hbbr.service 配置系统级服务
[Unit]
Description=RustDesk Relay Server
After=network.target
[Service]
Type=simple
ExecStart=/opt/rustdesk/hbbr
Restart=on-failure
WorkingDirectory=/opt/rustdesk
[Install]
WantedBy=multi-user.target
执行vim hbbs.service 配置系统级服务
[Unit]
Description=RustDesk Relay Server
After=network.target
[Service]
Type=simple
ExecStart=/opt/rustdesk/hbbs
Restart=on-failure
WorkingDirectory=/opt/rustdesk
[Install]
WantedBy=multi-user.target
将服务添加到系统服务,且加入开机自启动
//将当前路径服务以软连接加入系统服务中
ln -s /opt/rustdesk/hbbr.service /etc/systemd/system/
ln -s /opt/rustdesk/hbbs.service /etc/systemd/system/
//重载服务
systemctl daemon-reload
//启动并且加入开机自启动
systemctl enable --now hbbs.service
systemctl enable --now hbbr.service
总结
解压完成后hbbr与hbbs可以移动位置,但是两个文件需放在同一目录中。
首次需要先启动hbbs在启动hbbr
docker部署教程
安装docker
见本站docker安装教程https://halo.blog360.sbs/archives/dockeran-zhuang-jiao-cheng
运行容器
docker 直接运行
# hbbs (id服务)
# -v:挂载文件目录。[本机目录]:[容器目录]
# --net=host:容器应用使用宿主机网络,不需要再配置docker的端口映射或者进行端口映射
# -r [服务器公网IP或域名]:对应当前服务器的地址,例如123.123.123.123,或域名mydomain.com
docker run --restart=always --name hbbs -v /opt/rustdesk:/root -td --net=host rustdesk/rustdesk-server hbbs -r [服务器公网IP或域名]
# hbbr (中继服务)
docker run --restart=always --name hbbr -v /opt/rustdesk:/root -td --net=host rustdesk/rustdesk-server hbbr
docker compose运行
version: '3'
networks:
rustdesk-net:
external: false
services:
hbbs:
container_name: hbbs
ports:
- 21115:21115
- 21116:21116
- 21116:21116/udp
- 21118:21118
image: rustdesk/rustdesk-server:latest
command: hbbs -r rustdesk.example.com:21117 # 公网ip或者域名,为中继连接节点
volumes:
- ./data:/root
networks:
- rustdesk-net
depends_on:
- hbbr
restart: unless-stopped
hbbr:
container_name: hbbr
ports:
- 21117:21117
- 21119:21119
image: rustdesk/rustdesk-server:latest
command: hbbr
volumes:
- ./data:/root
networks:
- rustdesk-net
restart: unless-stopped
总结
1. 注意配置公网访问中继节点
rustdesk-web-API-server 部署
首先我们为什么要部署rustdesk-API-server,我们需要指定rustdesk开源版本与pro版本的区别,其核心区别主要有下:
对应企业而言,前五条还是比较重要的,而pro版本的订阅价格还比较贵,我们可以通过自行部署其他第三方rustdesk-web-API来解决,当然第三方肯定无法和官方的pro比较,如果权限要求比较细也可以购买pro版本支持一下
那么我们本次部署所使用的第三方rustdesk-web-API可以提供哪些功能呢?基本如下
项目地址:https://github.com/lejianwen/rustdesk-api
安装docker
见本站docker安装教程
docker compose运行
添加docker-compose.yml文件
version: '3' # 必须声明版本(推荐 ≥3.5)
services:
rustdesk:
image: lejianwen/rustdesk-server-s6:latest
container_name: rustdesk-server
ports:
- "21114:21114" # 注意:所有端口必须加引号(YAML规范)
- "21115:21115"
- "21116:21116" # TCP端口
- "21116:21116/udp" # UDP端口(无需重复定义TCP)
- "21117:21117"
- "21118:21118"
- "21119:21119"
environment:
- RELAY=rustdesk.domain.com:21117 # 默认的中继服务器
- ENCRYPTED_ONLY=1
- MUST_LOGIN=Y # 是否强制登陆才运行使用
- TZ=Asia/Shanghai # 时区
# - RUSTDESK_API_GORM_TYPE=mysql
# - RUSTDESK_API_MYSQL_USERNAME=root
# - RUSTDESK_API_MYSQL_PASSWORD=123456
# - RUSTDESK_API_MYSQL_ADDR=192.168.1.1
# - RUSTDESK_API_MYSQL_DBNAME=rustdesk_db
- RUSTDESK_API_APP_SHOW_SWAGGER=0 # 是否启用swagger
- RUSTDESK_API_RUSTDESK_KEY=1222112= #jwt key
- RUSTDESK_API_RUSTDESK_API_SERVER=https://rustdesk.domain.com # 默认访问的api路径
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=rustdesk.domain.com:21117
- RUSTDESK_API_RUSTDESK_ID_SERVER=rustdesk.domain.com:21116
volumes:
- ./data:/data
- ./data/api:/app/data #将数据库挂载 sqlite 数据库文件挂载 为mysql时可以不配置
restart: unless-stopped
networks:
- rustdesk-net
networks: # 顶级配置(顶格书写)
rustdesk-net:
driver: bridge
完整参数配置
查看密码
执行
docker logs -f docker容器ID
查看密码
总结
注意这个服务包已经包含了hbbr与hbbs,可以不用再部署了。
默认超管密码将会在容器日志中输出。
默认容器已经禁用了注册功能。
RustDesk 发布教程
在发布之前,我们需要了解rustdesk各个端口的作用:
了解端口,确认发布端口
hbbs 服务所需端口
hbbr 服务所需端口
那么可以根据一下需求来
RustDesk 官方开源版本
rustdesk-web-API-server 第三方带服务版本
使用nginx代理发布端口
非web端口nginx stream 发布·
修改nignx配置文件,设置stream模块
stream { include /etc/nginx/stream.d/*; } # 注意与http模块同级别
添加stream 代理端口
server {
listen 21116;
proxy_pass 172.30.0.2:21116;
proxy_connect_timeout 5s;
}
# UDP端口代理(P2P打洞)
server {
listen 21116 udp reuseport;
proxy_pass 172.30.0.2:21116;
proxy_timeout 3s;
}
server {
listen 21117;
proxy_pass 172.30.0.2:21117;
}
web端口发布
在/etc/nginx/sites-available添加新域名配置文件,vim 你的域名
server {
listen 80;
server_name test.blog360.sbs;
# return 404; # managed by Certbot
location / {
client_max_body_size 50m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.30.0.2:21114/; #后端 真实服务器
index index.html index.htm A1-index.html;
}
}
建立软连接
ln -s /etc/nginx/sites-available/你的域名 /etc/nginx/sites-enabled/你的域名
web端口申请免费证书(ssl证书可选)
安装Certbot工具
# Ubuntu/Debian
sudo apt update
sudo apt install certbot python3-certbot-nginx
# CentOS/RHEL
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx
申请ssl证书(nginx)
sudo certbot --nginx -d your-domain.com
替换
your-domain.com
为你的域名
总结
1. 注意stream 与http同级。
2.发布尽量使用标准端口,若rustdesk-web-API-server使用非标端口注意启动容器配置
RustDesk 使用教程
RustDesk 开源社区版本使用
打开设置
配置网络
配置对应的id,中继服务器和key
key的配置在
1.服务器docker logs -f 容器id日志中查看
2在服务器安装,目录查看一个为 id_ed------.pub文件
完成配置后,回到主页,显示就绪即可正常使用
pro版本或者rustdesk-web-API-server版本
swagger使用
在配置启用swagger后,打开
后台文档
/admin/swagger/index.html
PC端文档
/swagger/index.html
即可进入对应swagger文档
用户初始化
未开放注册,若用户初始用户过多,可以通过调用api方式添加用户,这里提供一个Java 同步启用微信用户的方法
/**
* @program: fd-system
* @description:
* @author: Mr.ChangHao
* @create: 2025-07-09 11:33
**/
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.util.HashMap;
import java.util.Map;
public class WeComUserSync {
// 企业微信API参数(需替换实际值)
public static final String QY_HOST = "企业微信url路径";
public static final String QY_CORP_ID = "企业id";
public static final String QY_CORP_SECRET = "企业微信密钥";
public static final String RUST_DESK_HOST = "http://192.168.73.129:21114/api";
public static final String RUST_DESK_TOKEN = "430b712ba7ef99da742c9c15bc5ae4c0";
public static void syncUsers() {
String weComAccessToken = getWeComAccessToken();
// 1. 获取企业微信所有用户
JSONArray wecomUsers = getWeComUsers(weComAccessToken);
// 2. 转换数据并调用创建接口
for (Object user : wecomUsers) {
JSONObject wecomUser = (JSONObject) user;
JSONObject targetUser = convertToTargetFormat(wecomUser);
createUser(targetUser);
}
}
public static String getWeComAccessToken() {
// 构建请求参数
Map<String, Object> params = new HashMap<>();
params.put("corpid", QY_CORP_ID);
params.put("corpsecret", QY_CORP_SECRET);
// 发送GET请求
HttpResponse response = HttpRequest.get(QY_HOST +"/cgi-bin/gettoken")
.form(params)
.execute();
// 解析响应
JSONObject result = JSONUtil.parseObj(response.body());
// 检查错误码
if (result.getInt("errcode") != 0) {
throw new RuntimeException("获取access_token失败: " + result.getStr("errmsg"));
}
return result.getStr("access_token");
}
public static JSONArray getWeComUsers(String weComAccessToken) {
String departmentId = "1"; // 根部门ID 注意权限范围
String fetchChild = "1"; // 递归获取子部门
// 构建请求URL
String url = String.format(QY_HOST +"/cgi-bin/user/list?access_token=%s&department_id=%s&fetch_child=%s",
weComAccessToken, departmentId, fetchChild);
// 发送GET请求
HttpResponse response = HttpRequest.get(url).execute();
String body = response.body();
JSONObject result = JSONUtil.parseObj(body);
// 检查响应状态
if (result.getInt("errcode") != 0) {
throw new RuntimeException("企业微信API调用失败: " + result.getStr("errmsg"));
}
return result.getJSONArray("userlist");
}
public static JSONObject convertToTargetFormat(JSONObject wecomUser) {
// 企业微信字段到目标字段的映射
Map<String, Object> map = new HashMap<>();
map.put("group_id", 2); // 默认分组ID
map.put("is_admin", false); // 管理员标识
map.put("nickname", wecomUser.getStr("name")); // 昵称
map.put("status", 1); // 状态转换
map.put("username", wecomUser.getStr("userid")); // 用户名
return JSONUtil.parseObj(map);
}
public static void createUser(JSONObject userData) {
HttpResponse response = HttpRequest.post(RUST_DESK_HOST +"/admin/user/create")
.body(userData.toString()) // JSON请求体
.header("api-token", RUST_DESK_TOKEN)
.contentType("application/json")
.execute();
// 检查响应状态(根据实际接口规范调整)
if (response.getStatus() != 200) {
System.out.println("用户创建失败: HTTP " + response.getStatus());
}
JSONObject result = JSONUtil.parseObj(response.body());
System.out.println("添加响应结果:"+result);
}
public static void updateAllUsersPassword() {
int page = 1;
int pageSize = 100; // 每页大小,可根据实际情况调整
int totalPages = 1; // 初始值,会在循环中更新
while (page <= totalPages) {
// 1. 分页获取用户
JSONObject userListResponse = getUserList(page, pageSize);
// 检查响应是否成功
if (userListResponse == null || userListResponse.getInt("code") != 0) {
System.err.println("获取用户列表失败,页码: " + page);
page++;
continue;
}
JSONObject data = userListResponse.getJSONObject("data");
JSONArray users = data.getJSONArray("list");
// 更新总页数
totalPages = (int) Math.ceil((double) data.getInt("total") / pageSize);
// 2. 遍历用户并更新密码
for (Object userObj : users) {
JSONObject user = (JSONObject) userObj;
updateUserPassword(user);
}
page++;
}
}
public static JSONObject getUserList(int page, int pageSize) {
String url = RUST_DESK_HOST + "/admin/user/list"; // 假设用户列表接口路径是基础URL
try {
HttpResponse response = HttpRequest.get(url)
.header("api-token", RUST_DESK_TOKEN)
.form("page", page)
.form("page_size", pageSize)
.execute();
String body = response.body();
return JSONUtil.parseObj(body);
} catch (Exception e) {
System.err.println("获取用户列表异常: " + e.getMessage());
return null;
}
}
public static void updateUserPassword(JSONObject user) {
int userId = user.getInt("id");
String username = user.getStr("username");
String newPassword = username + "!@#123"; // 生成密码
String url = RUST_DESK_HOST + "/admin/user/changePwd"; // 假设修改密码接口路径
try {
if (userId >1) {
JSONObject requestBody = new JSONObject();
requestBody.set("id", userId);
requestBody.set("password", newPassword);
HttpResponse response = HttpRequest.post(url)
.body(requestBody.toString())
.header("api-token", RUST_DESK_TOKEN)
.contentType("application/json")
.execute();
JSONObject result = JSONUtil.parseObj(response.body());
if (result.getInt("code") == 0) {
System.out.println("用户密码更新成功: " + username);
} else {
System.err.println("用户密码更新失败: " + username +
" | 错误信息: " + result.getStr("message"));
}
}
} catch (Exception e) {
System.err.println("更新密码异常: " + username + " | " + e.getMessage());
}
}
public static void main(String[] args) {
// syncUsers(); 同步企业微信用户
updateAllUsersPassword(); //批量初始化用户密码
}
}
Oauth2接入(GitHub)演示
添加对应Oauth2配置
对于
Google
和Github
,Issuer
和Scopes
不需要填写.对于
OIDC
,Issuer
是必须的。Scopes
是可选的,默认为openid,profile,email
. 确保可以获取sub
,email
和preferred_username
github oauth app
在Settings
->Developer settings
->OAuth Apps
->New OAuth App
中创建,地址 https://github.com/settings/developerscallback URL 填写
{uri}/api/oauth/callbackAuthorization callback URL
填写http:///api/oidc/callback
,比如http://127.0.0.1:21114/api/oidc/callback
前往GitHub oauth app配置 https://github.com/settings/developers
rustdesk 页面配置
这样是存在bug
我们直接调用api添加该字段也不行,这个bug应该是由于使用sqlite会出现,mysql正常
curl --location --request POST 'http://192.168.73.129:21114/api/admin/oauth/create' \
--header 'api-token: 60aca81100daccf883c66f32b45d7b72' \
--header 'Content-Type: application/json' \
--data '{
"id": 0,
"op": "",
"oauth_type": "github",
"issuer": "",
"client_id": "2221",
"client_secret": "1123",
"redirect_url": "",
"scopes": "",
"auto_register": true,
"pkce_enable": false,
"pkce_method": "S256"
}'
完成后页面展示
第三方接入与OIDC 接入
根据up主的查阅OIDC 算比较前沿的,国内支持厂商并不多,好像企业微信在23年已经内测了,如果内部业务系统支持可以对接,或者查询所使用的系统看有没有对接的可能,或者直接简单开发对接api实现自定义Oauth2登录
其他权限管理
这里的话各位小伙伴可以自行探索,根据自己的需求来使用地址簿、标签、分组等等