Ubuntu 部署 RustDesk 服务器

本文面向在 Ubuntu 上自托管 RustDesk 开源服务器(hbbs/hbbr)。适用于本地机房或云服务器(如阿里云、腾讯云、华为云等)。

  • 官方参考文档(强烈建议同时阅读):rustdesk.com/docs/zh-cn/...
  • 适用系统:Ubuntu 20.04/22.04/24.04(其他 Debian 系亦可参考)

0. 准备工作

  • 一台可公网访问的 Ubuntu 服务器(具备 sudo 权限)。
  • 确保可以通过 ssh 登录,执行基础维护命令。
  • 如果使用云厂商,先在云控制台开放端口。

1. 开放端口

云厂商安全组是"第一道防火墙",仅配置系统 ufw 不够,必须在云控制台放行端口。以阿里云为例:

  • 登录 阿里云 ECS 控制台 → 找到你的服务器 → 左侧「安全组」→ 配置规则。
  • 新增「入方向」规则,放行 RustDesk 所需端口:
协议 端口范围 授权对象 说明
TCP 21114-21119 0.0.0.0/0 RustDesk 核心 TCP 端口
UDP 21116 0.0.0.0/0 RustDesk NAT 打洞 UDP 端口

提示:0.0.0.0/0 表示允许所有 IP(适合测试)。生产环境建议缩小到你的办公网段或指定客户端 IP。

保存规则后,通常需等待 1--2 分钟生效(安全组有延迟)。

同时,在服务器系统内使用 ufw 放行端口(建议):

shell 复制代码
sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 21114:21119/tcp
sudo ufw allow 21116/udp
sudo ufw enable
sudo ufw status numbered

2. 安装 RustDesk(方式一:一键脚本)

如果你希望快速完成部署,可使用一键脚本:

shell 复制代码
wget https://ghfast.top/https://raw.githubusercontent.com/techahold/rustdeskinstall/master/install.sh -O install.sh
chmod +x install.sh
sudo ./install.sh

脚本会自动下载安装并以 systemd 服务运行 hbbs(ID 服务)与 hbbr(中继服务)。

  • 安装过程中会出现两处交互确认,按提示选择即可(见下图)。
  • 首次启动时,服务会在日志中输出一个 Key(密钥),请复制并保存(见下图)。

查看服务状态与日志:

shell 复制代码
systemctl status hbbs
systemctl status hbbr
journalctl -u hbbs -f
journalctl -u hbbr -f

检查端口是否监听:

shell 复制代码
ss -tunlp | grep -E '2111[4-9]|21116'

3. 安装 RustDesk(方式二:官方 deb 包,二选一)

也可在 GitHub Releases 下载官方 .deb 包并安装(适合熟悉 Debian 包管理的用户):

  1. 前往 RustDesk 服务器开源版发布页,下载适合架构的 *.deb

  2. 上传到服务器并安装:

    shell 复制代码
    sudo dpkg -i rustdesk-server*.deb
    sudo apt -f install
  3. 验证服务:

    shell 复制代码
    systemctl status hbbs
    systemctl status hbbr

两种安装方式任选其一即可,不必重复安装。

4. 客户端配置(Windows/macOS/Linux)

在 RustDesk 客户端中填写自托管信息(见下图):

  • 打开 RustDesk → 进入「设置」→ 找到与「自托管 / 服务器」相关的配置。
  • 填写以下内容:
    • ID 服务器:你的服务器域名或公网 IP(端口默认 21114)。
    • 中继服务器:你的服务器域名或公网 IP(默认端口 21117,可在客户端处填写为 IP:21117)。
    • Key:从服务器日志中复制的密钥。

保存后,返回主界面,两个客户端互相输入对方 ID 进行连接测试。若能直连,会显示直连;若 NAT 复杂,则会自动走中继。

5. 远程连通性验证(可选但推荐)

从你的本地电脑测试云端端口是否可达:

shell 复制代码
nc -zv <你的服务器IP或域名> 21114
nc -zv <你的服务器IP或域名> 21117

如显示 succeededopen,表示端口连通性正常。

6. 常见问题与排查

  • 无法获取 ID:
    • 检查 hbbs 服务是否运行:systemctl status hbbs
    • 检查云安全组与 ufw 是否已放行 21114--21119/TCP 与 21116/UDP。
  • 连接总是走中继:
    • 很可能是客户端之间 NAT 穿透失败。确保 21116/UDP 放行;或接受通过中继连接。
  • 提示 Key 不匹配:
    • 确保客户端填写的 Key 与服务器当前输出的一致;变更服务器后需更新客户端配置。
  • 服务名不确定:
    • 可执行 systemctl list-units | grep hbbsgrep hbbr 查找具体服务名称。

7. 运维与升级(简要)

  • 重启服务:

    shell 复制代码
    sudo systemctl restart hbbs
    sudo systemctl restart hbbr
  • 日志查看:

    shell 复制代码
    journalctl -u hbbs --no-pager -n 200
    journalctl -u hbbr --no-pager -n 200
  • 安全建议:

    • 生产环境将安全组授权对象从 0.0.0.0/0 收紧到可信网段。
    • 定期更新系统并重启服务,减少已知漏洞风险。

8. 一键更换默认端口脚本(含安全收敛)

为便于快速落地,提供一键脚本:

  • hbbs/hbbr 在自定义端口监听(需要提供中继主机与可选密钥文件)。

端口范围与建议:

  • 可修改范围:1024--65535(避免使用 0--1023 的保留端口)。
  • 推荐选择高位端口(如 20000--40000),并提前检查占用:ss -tunlp | grep <PORT>

将脚本文件 rustdesk-port-switch.sh 上传到服务器(例如 /usr/local/sbin/),并赋予执行权限:

shell 复制代码
#!/usr/bin/env bash
set -euo pipefail

# 常量: 默认端口
DEFAULT_NAT=21116
DEFAULT_RELAY=21117
DEFAULT_HBBS_WS=21118
DEFAULT_HBBR_WS=21119

BASE=""
NAT_PORT=""
RELAY_PORT=""
HBBS_WS=""
HBBR_WS=""
RELAY_HOST=""

# 函数: 打印用法说明与端口范围
usage() {
  cat <<'USAGE'
用法: sudo bash rustdesk-port-switch.sh
说明: 纯交互式,直接修改 hbbs/hbbr 监听端口(override 模式)
端口范围: 1024--65535,建议 20000--40000,避免 0--1023 保留端口
USAGE
}

# 函数: 检查是否 root
require_root() {
  if [ "${EUID:-$(id -u)}" -ne 0 ]; then
    echo "需要以 root 运行" >&2
    exit 1
  fi
}

# 函数: 验证端口范围与合法性
validate_port() {
  local p="$1"
  [[ "$p" =~ ^[0-9]+$ ]] || { echo "端口必须为数字: $p" >&2; exit 1; }
  if [ "$p" -lt 1024 ] || [ "$p" -gt 65535 ]; then
    echo "端口超出允许范围(1024--65535): $p" >&2
    exit 1
  fi
}

# 函数: 计算最终端口组合
compute_ports() {
  if [ -n "$BASE" ]; then
    NAT_PORT="$BASE"
    RELAY_PORT=$((BASE+2))
    # WebSocket 端口仅在显式指定时设置
  fi
  : "${NAT_PORT:=${NAT_PORT:-}}"
  : "${RELAY_PORT:=${RELAY_PORT:-}}"
  [ -n "$NAT_PORT" ] || NAT_PORT="$DEFAULT_NAT"
  [ -n "$RELAY_PORT" ] || RELAY_PORT="$DEFAULT_RELAY"

  validate_port "$NAT_PORT"; validate_port "$RELAY_PORT"
  [ -n "$HBBS_WS" ] && validate_port "$HBBS_WS"
  [ -n "$HBBR_WS" ] && validate_port "$HBBR_WS"
}

# 函数: 交互式询问模式/端口/安全选项
is_port_used() {
  local p="$1"
  ss -H -l -n | awk '{print $5}' | awk -F: '{print $NF}' | grep -qx "$p"
}

suggest_base_port() {
  local tries=0 max=30 b
  while [ $tries -lt $max ]; do
    b=$((20000 + RANDOM % 20001))
    [ $((b+4)) -le 65535 ] || { tries=$((tries+1)); continue; }
    if is_port_used "$b" || is_port_used $((b+2)) || is_port_used $((b+3)) || is_port_used $((b+4)); then
      tries=$((tries+1)); continue
    fi
    BASE_SUG="$b"
    HBBS_WS_SUG=$((b+3))
    HBBR_WS_SUG=$((b+4))
    return 0
  done
  BASE_SUG=30022
  HBBS_WS_SUG=30025
  HBBR_WS_SUG=30026
}

interactive_prompt() {
  suggest_base_port
  echo "推荐基准端口: ${BASE_SUG} (hbbs(NAT)=${BASE_SUG}, hbbr(Relay)=$((BASE_SUG+2)); WS: hbbs=${HBBS_WS_SUG}, hbbr=${HBBR_WS_SUG})"
  echo "可输入基准端口(如 30022), 将自动设置: hbbs(NAT)=base, hbbr(Relay)=base+2"
  read -r -p "基准端口(回车使用推荐 ${BASE_SUG}): " BASE
  if [ -z "$BASE" ]; then BASE="$BASE_SUG"; fi
  validate_port "$BASE"

  NAT_PORT="$BASE"; RELAY_PORT=$((BASE+2))

  # 可选 WebSocket 端口
  read -r -p "是否设置 WebSocket 端口(推荐 hbbs=${HBBS_WS_SUG} hbbr=${HBBR_WS_SUG},官方默认 21118/21119)[y/N]: " ws_sel
  case "${ws_sel,,}" in
    y|yes)
      echo "推荐 WebSocket 端口: hbbs=${HBBS_WS_SUG} hbbr=${HBBR_WS_SUG}"
      read -r -p "hbbs WebSocket 端口(回车使用推荐 ${HBBS_WS_SUG}): " HBBS_WS
      [ -z "$HBBS_WS" ] && HBBS_WS="$HBBS_WS_SUG"
      validate_port "$HBBS_WS"
      read -r -p "hbbr WebSocket 端口(回车使用推荐 ${HBBR_WS_SUG}): " HBBR_WS
      [ -z "$HBBR_WS" ] && HBBR_WS="$HBBR_WS_SUG"
      validate_port "$HBBR_WS"
      ;;
    *) : ;;
  esac

  read -r -p "中继主机(域名或IP): " RELAY_HOST
  if [ -z "$RELAY_HOST" ]; then
    echo "需要提供中继主机"; exit 1
  fi

  echo "\n配置摘要:";
  echo "hbbs(NAT) 端口: ${NAT_PORT}  hbbr(Relay) 端口: ${RELAY_PORT}"
  [ -n "$HBBS_WS" ] && echo "hbbs WS: ${HBBS_WS}"
  [ -n "$HBBR_WS" ] && echo "hbbr WS: ${HBBR_WS}"
  echo "中继主机: ${RELAY_HOST}"

  read -r -p "继续执行? [y/N]: " go
  case "${go,,}" in y|yes) : ;; *) echo "已取消"; exit 1;; esac
}

# (移除 NAT 重定向逻辑)

# 函数: UFW 开放新端口
ensure_ufw_rules() {
  if ! command -v ufw >/dev/null 2>&1; then
    apt-get update -y && apt-get install -y ufw
  fi
  ufw allow "${NAT_PORT}/udp" || true
  ufw allow "${NAT_PORT}/tcp" || true
  ufw allow "${RELAY_PORT}/tcp" || true
  [ -n "$HBBS_WS" ] && ufw allow "${HBBS_WS}/tcp" || true
  [ -n "$HBBR_WS" ] && ufw allow "${HBBR_WS}/tcp" || true
  ufw status | cat
}

# (移除持久化 iptables 逻辑)

# 函数: 写入 systemd override
write_override() {
  local hbbs_path hbbr_path unit path_guess

  # 优先使用用户指定路径
  hbbs_path="${HBBS_PATH:-}"
  hbbr_path="${HBBR_PATH:-}"

  # 通过 PATH 查找
  [ -z "$hbbs_path" ] && hbbs_path="$(command -v hbbs || true)"
  [ -z "$hbbr_path" ] && hbbr_path="$(command -v hbbr || true)"

  # 通过 systemd 单元解析 ExecStart
  if [ -z "$hbbs_path" ]; then
    unit="$(systemctl cat hbbs 2>/dev/null || true)"
    path_guess="$(echo "$unit" | awk -F= '/^ExecStart=/{print $2}' | awk '{print $1}' | tail -n1)"
    [ -n "$path_guess" ] && hbbs_path="$path_guess"
  fi
  if [ -z "$hbbr_path" ]; then
    unit="$(systemctl cat hbbr 2>/dev/null || true)"
    path_guess="$(echo "$unit" | awk -F= '/^ExecStart=/{print $2}' | awk '{print $1}' | tail -n1)"
    [ -n "$path_guess" ] && hbbr_path="$path_guess"
  fi

  # 常见路径回退
  [ -z "$hbbs_path" ] && for p in /usr/local/bin/hbbs /usr/bin/hbbs /opt/rustdesk/hbbs /opt/rustdesk-server/hbbs; do [ -x "$p" ] && hbbs_path="$p" && break; done
  [ -z "$hbbr_path" ] && for p in /usr/local/bin/hbbr /usr/bin/hbbr /opt/rustdesk/hbbr /opt/rustdesk-server/hbbr; do [ -x "$p" ] && hbbr_path="$p" && break; done

  [ -n "$hbbs_path" ] && [ -x "$hbbs_path" ] || { echo "未找到可执行的 hbbs, 请确认安装在 /opt/rustdesk/hbbs 或 /usr/local/bin/hbbs"; exit 1; }
  [ -n "$hbbr_path" ] && [ -x "$hbbr_path" ] || { echo "未找到可执行的 hbbr, 请确认安装在 /opt/rustdesk/hbbr 或 /usr/local/bin/hbbr"; exit 1; }

  # 写入 systemd 配置:若主单元存在则写 override;否则创建新的单元文件
  if systemctl cat hbbs >/dev/null 2>&1; then
    mkdir -p /etc/systemd/system/hbbs.service.d
    cat >/etc/systemd/system/hbbs.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=${hbbs_path} -p ${NAT_PORT} -r ${RELAY_HOST}:${RELAY_PORT}
EOF
  else
    cat >/etc/systemd/system/hbbs.service <<EOF
[Unit]
Description=RustDesk HBBS (ID server)
After=network.target

[Service]
Type=simple
ExecStart=${hbbs_path} -p ${NAT_PORT} -r ${RELAY_HOST}:${RELAY_PORT}
Restart=always
RestartSec=2s
User=root

[Install]
WantedBy=multi-user.target
EOF
  fi

  if systemctl cat hbbr >/dev/null 2>&1; then
    mkdir -p /etc/systemd/system/hbbr.service.d
    cat >/etc/systemd/system/hbbr.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=${hbbr_path} -p ${RELAY_PORT}
EOF
  else
    cat >/etc/systemd/system/hbbr.service <<EOF
[Unit]
Description=RustDesk HBBR (Relay server)
After=network.target

[Service]
Type=simple
ExecStart=${hbbr_path} -p ${RELAY_PORT}
Restart=always
RestartSec=2s
User=root

[Install]
WantedBy=multi-user.target
EOF
  fi
}

# 函数: 重载并重启服务
restart_services() {
  systemctl daemon-reload
  systemctl enable hbbs >/dev/null 2>&1 || true
  systemctl enable hbbr >/dev/null 2>&1 || true
  systemctl restart hbbs || true
  systemctl restart hbbr || true
  systemctl --no-pager -q status hbbs hbbr || true
}

# 函数: 提取并保存最新 Key 到脚本同级目录,并打印
save_and_print_key() {
  local key line script_dir key_file
  # 从 hbbs 日志提取最新 Key
  line=$(journalctl -u hbbs -n 200 --no-pager 2>/dev/null | grep -E "Key:" | tail -n1 || true)
  if [ -z "$line" ]; then
    line=$(journalctl -u hbbr -n 200 --no-pager 2>/dev/null | grep -E "Key:" | tail -n1 || true)
  fi
  key=$(echo "$line" | sed -E 's/^.*Key:\s*([A-Za-z0-9+\/=]+).*$/\1/' )
  script_dir=$(cd -- "$(dirname -- "$0")" && pwd)
  key_file="$script_dir/rustdesk.key"
  if [ -n "$key" ]; then
    printf "%s\n" "$key" > "$key_file"
    chmod 600 "$key_file" 2>/dev/null || true
    echo "- ID 服务器 : ${RELAY_HOST}:${NAT_PORT}"
    echo "- 中继服务器 : ${RELAY_HOST}:${RELAY_PORT}"
    echo "- Key: $key"
    echo "已保存到: $key_file"
  else
    echo "- ID 服务器 : ${RELAY_HOST}:${NAT_PORT}"
    echo "- 中继服务器 : ${RELAY_HOST}:${RELAY_PORT}"
    echo "- Key: (未解析)"
    echo "未从日志解析到 Key。可稍后运行: journalctl -u hbbs -n 200 | grep -i Key"
  fi
}

require_root
interactive_prompt

if [ -z "$RELAY_HOST" ]; then
  echo "需要提供中继主机" >&2
  exit 1
fi
write_override
restart_services
ensure_ufw_rules
save_and_print_key
shell 复制代码
sudo mv rustdesk-port-switch.sh /usr/local/sbin/
sudo chmod +x /usr/local/sbin/rustdesk-port-switch.sh

交互式运行(无需参数):

shell 复制代码
sudo bash /usr/local/sbin/rustdesk-port-switch.sh
  • 将依次询问基准端口或分别输入 hbbs/hbbr 端口、是否设置 WebSocket 端口,以及中继主机与密钥文件,确认后自动执行。
  • 已内置自动探测 hbbs/hbbr 路径(包含 /opt/rustdesk/hbbs/opt/rustdesk/hbbr 等常见位置),无需手动输入路径。
  • 每次运行脚本都会随机推荐一组高位端口(避开已占用),包含 hbbs(NAT)hbbr(Relay) 以及可选的 WebSocket 端口;直接回车可使用推荐值。
  • 脚本会在执行完成后自动从日志解析最新 Key,并保存为脚本同级目录的 rustdesk.key(覆盖旧文件),同时在终端打印当前 Key

执行完成后终端将输出如下摘要,便于在客户端填写:

  • ID 服务器 : <你的域名或IP>:<hbbs端口>
  • 中继服务器 : <你的域名或IP>:<hbbr端口>
  • Key: <服务器当前Key>
  • 已保存到: <脚本同级目录>/rustdesk.key

记得去阿里云等安全组开放重新设置的端口

验证:3001[5-9]记得换成<新设置的端口>

shell 复制代码
ss -tunlp | grep -E '3001[5-9]'
nc -zv <域名或IP> <新设置的端口>
nc -zv <域名或IP> <新设置的端口>

9. 端口说明(了解即可)

RustDesk 服务器默认使用 21114--21119/TCP21116/UDP

  • 21116/UDP:NAT 打洞。
  • 21117/TCP:中继通道。
  • 其余端口用于 ID 注册、握手与控制信令等(保留默认即可)。
相关推荐
天宁1 小时前
Ubuntu 一键切换吉林大学镜像源
linux·ubuntu
垦***耪2 小时前
芯片设计,送一套工艺学习,模拟集成逆向全芯片电路,这是一款低噪声、低损耗的电源带载能力高达30...
ubuntu
双子座断点2 小时前
Ubuntu 硬盘扩容
linux·运维·ubuntu
❀搜不到2 小时前
问题:Ubuntu设置没有WiFi,且蓝牙也无法打开
linux·运维·ubuntu
遇到困难睡大觉哈哈3 小时前
Harmony os——ArkTS 语言笔记(七):注解(Annotation)实战理解
java·笔记·ubuntu·harmonyos·鸿蒙
2301_816073833 小时前
DNS域名解析错误
网络·ubuntu
开始King3 小时前
ubuntu 24.04.3 LTS + 5090 (安装ubuntu和5090显卡驱动)
linux·运维·ubuntu
林政硕(Cohen0415)3 小时前
Ubuntu 下 Qt Creator 远程连接 ARM 板卡环境搭建
arm开发·qt·ubuntu