目录
前排提醒:文章属于博主个人结合个人认知即兴创作,如有疏漏,还请各位大佬指点。
其实远程连接类的核心问题就是需要一个端点设备都能访问到的公网ip。
前言
为什么用rustdesk?
开源免费(pro版貌似不开源)。之前一直用todesk,todesk借助疫情期间免费策略快速发展,当时比向日葵好用很多。结果到最近多次修改免费策略,不断限制免费版连接时长,对于经常需要远程访问的朋友来说很不友好。
其实相比于一些第三方的远程软件,也可以使用windows系统自带的远程连接(mstsc),但是需要多台设备处于同一局域网下。可以内网穿透一下,目前我还没有尝试过,通过zerotier让两台处于不同局域网中的设备搞到一个局域网中,然后再使用mstsc远程连接。
为什么要自建?
因为自2022年起,RustDesk官方已主动屏蔽所有中国IP的访问请求,国内用户通过公共服务器连接时会收到“Connection not allowed”的错误提示。也就是说国内网络是不能直接连接官方服务器的。据说是因为诈骗团伙的滥用,官方为了避免法律风险以及项目连带责任,所以一刀切屏蔽了中国IP。
然后要注意的是官方从未在我国境内架设过服务器节点,所有公网服务都是依赖海外的官方基础设施。
挂梯子能否连接?
博主没有尝试,有试过的伙伴可以分享一下(todo:后续试一下)。总的来说:
技术上可行,但有几个问题:
1. 数据安全风险:这个大家都懂。
2. 延迟与卡顿:这个看梯子质量,但实际上肯定还是有明显的延迟。
3. 稳定性、可靠性:也看梯子质量。
rustdesk架构
github上找了俩图,大家凑活看:How does RustDesk work? · rustdesk/rustdesk Wiki
整体上分为:服务端 和 客户端。
然后rustdesk服务器的核心组件主要有两个部分/服务:
- ID Server(hbbs):负责设备身份验证与连接协商
- 中继服务器(hbbr) :当P2P直连失败时中转数据(如NAT穿透失败)
ps:即使经中继服务器,数据仍全程加密,中继节点无法解密。
通信原理官方介绍:
- 只要 RustDesk 在机器上运行,机器就会不断 ping ID 服务器(hbbs)以告知其当前 IP 地址和端口。
- 当您从计算机 A 启动到计算机 B 的连接时,计算机 A 联系 ID 服务器并请求与计算机 B 通信。
- 然后 ID 服务器尝试使用打洞技术将 A 和 B 直接连接。
- 如果打洞失败,A 将通过中继服务器(hbbr)与 B 通信。
- 在大多数情况下,打洞是成功的,从不使用中继服务器。
客户端安装
这个很简单就不赘述了,直接官网跳转github下载。
只是要注意的是:作为被控端的设备建议安装(msi)使用。如果直接运行exe程序,那么主控端在控制时会遇到权限不足的提示,导致无法操作。
服务端安装
前言
服务端的安装是客户端能否互相连接的关键。正如文章开头所说,服务端安装的核心就是需要一个公网ip来保证让客户端能正常访问。
ps:其实公网ip也不必须,甚至可以直接跑在自己电脑上或者虚拟机上,还有各种代理、穿透、NAT转换、路由桥接等进阶操作,本篇文章暂不涉及。
对于大众来说获得公网ip比较方便的方式就是购买云厂商的云服务器,购买后会自动分配到一个公网ip,这样我们既可以在服务器上进行部署,并且也有了连接必备的公网ip。缺点嘛,无非就是买服务器的钱还是小贵吧,如果没有优惠的话,并且由于服务端所需配置不高,导致有种杀鸡用牛刀的错觉,大部分的资源是闲置的。
ps:网上也有各种白嫖方法,也可以使用按量付费的服务器,大家自行探索,本篇暂不涉及。
要求
来自官方文档:
硬件要求非常低;基本云服务器的最低配置就足够了,CPU和内存要求极低。您也可以使用树莓派或类似设备。关于网络规模,如果TCP打洞直连失败,将消耗中继流量。中继连接的流量根据分辨率设置和屏幕更新在30 K/s到3 M/s(1920x1080屏幕)之间。如果仅用于办公需求,流量约为100 K/s。
所需端口
新手建议开放涉及的所有端口。
rustdesk需要开放的端口包括:21114 到 21119,具体用途如下:
- 21114:API服务器端口,用于客户端与服务器之间的通信
- 21115:hbbs端口,负责客户端的ID管理
- 21116:hbbr端口,处理数据转发
- 21117:备用hbbr端口,通常与21116一起使用
- 21118:网页客户端端口(可选),如果不需要网页客户端支持,可以不开放
- 21119:备用网页客户端端口(可选),如果不需要网页客户端支持,可以不开放
(todo:列举tcp/udp使用情况。。)
部署
假设你现在已经拥有了一台服务器,我们根据官方文档开始部署:
官方推荐docker方式安装,我也推荐docker。但是,还没来得及试,这里先不记录了😅。
我们说说脚本安装的方式:
官方文档中提到运行下面的命令,会下载并设置中继和信号服务器(hbbr和hbbs),生成配置并将其托管在受密码保护的网页上,以便简单部署到客户端:
wget https://raw.githubusercontent.com/techahold/rustdeskinstall/master/install.sh
chmod +x install.sh
./install.sh
脚本地址:https://github.com/techahold/rustdeskinstall
脚本内容:
#!/bin/bash
# Get user options
while getopts i:-: option; do
case "${option}" in
-)
case "${OPTARG}" in
help)
help="true";;
resolveip)
resolveip="true";;
resolvedns)
val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
resolvedns=${val};;
install-http)
http="true";;
skip-http)
http="false";;
esac;;
i) resolveip="true";;
esac
done
function displayhelp() {
if [[ ! -z $help ]]; then
echo 'usage: install.sh --resolveip --resolvedns "fqdn"'
echo "options:"
echo "--resolveip Use IP for server name. Cannot use in combination with --resolvedns or -d"
echo '--resolvedns "fqdn" Use FQDN for server name. Cannot use in combination with --resolveip or -i'
echo "--install-http Install http server to host installation scripts. Cannot use in combination with --skip-http or -n"
echo "--skip-http Skip installation of http server. Cannot use in combination with --install-http or -h"
exit 0
fi
}
displayhelp
# Get Username
uname=$(whoami)
gname=$(id -gn ${uname})
admintoken=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c16)
ARCH=$(uname -m)
# identify OS
if [ -f /etc/os-release ]; then
# freedesktop.org and systemd
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
UPSTREAM_ID=${ID_LIKE,,}
# Fallback to ID_LIKE if ID was not 'ubuntu' or 'debian'
if [ "${UPSTREAM_ID}" != "debian" ] && [ "${UPSTREAM_ID}" != "ubuntu" ]; then
UPSTREAM_ID="$(echo ${ID_LIKE,,} | sed s/\"//g | cut -d' ' -f1)"
fi
elif type lsb_release >/dev/null 2>&1; then
# linuxbase.org
OS=$(lsb_release -si)
VER=$(lsb_release -sr)
elif [ -f /etc/lsb-release ]; then
# For some versions of Debian/Ubuntu without lsb_release command
. /etc/lsb-release
OS=$DISTRIB_ID
VER=$DISTRIB_RELEASE
elif [ -f /etc/debian_version ]; then
# Older Debian/Ubuntu/etc.
OS=Debian
VER=$(cat /etc/debian_version)
elif [ -f /etc/SuSe-release ]; then
# Older SuSE/etc.
OS=SuSE
VER=$(cat /etc/SuSe-release)
elif [ -f /etc/redhat-release ]; then
# Older Red Hat, CentOS, etc.
OS=RedHat
VER=$(cat /etc/redhat-release)
else
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
OS=$(uname -s)
VER=$(uname -r)
fi
# output debugging info if $DEBUG set
if [ "$DEBUG" = "true" ]; then
echo "OS: $OS"
echo "VER: $VER"
echo "UPSTREAM_ID: $UPSTREAM_ID"
exit 0
fi
# Setup prereqs for server
# common named prereqs
PREREQ="curl wget unzip tar"
PREREQDEB="dnsutils"
PREREQRPM="bind-utils"
PREREQARCH="bind"
echo "Installing prerequisites"
if [ "${ID}" = "debian" ] || [ "$OS" = "Ubuntu" ] || [ "$OS" = "Debian" ] || [ "${UPSTREAM_ID}" = "ubuntu" ] || [ "${UPSTREAM_ID}" = "debian" ]; then
sudo apt-get update
sudo apt-get install -y ${PREREQ} ${PREREQDEB} # git
elif [ "$OS" = "CentOS" ] || [ "$OS" = "RedHat" ] || [ "${UPSTREAM_ID}" = "rhel" ] ; then
# opensuse 15.4 fails to run the relay service and hangs waiting for it
# needs more work before it can be enabled
# || [ "${UPSTREAM_ID}" = "suse" ]
sudo yum update -y
sudo yum install -y ${PREREQ} ${PREREQRPM} # git
elif [ "${ID}" = "arch" ] || [ "${UPSTREAM_ID}" = "arch" ]; then
sudo pacman -Syu
sudo pacman -S ${PREREQ} ${PREREQARCH}
else
echo "Unsupported OS"
# give them the option to continue
echo -n "Would you like to continue? Dependencies may not be satisfied... [y/n] "
read continue_no_dependencies
if [ $continue_no_dependencies == "y" ]; then
echo "Continuing..."
elif [ $continue_no_dependencies != "n" ]; then
echo "Invalid answer, exiting."
exit 1
else
exit 1
fi
fi
# Choice for DNS or IP
if [[ -z "$resolveip" && -z "$resolvedns" ]]; then
PS3='Choose your preferred connection method: auto-resolve current WAN IP or enter your DNS/Domain:'
WAN=("IP" "DNS/Domain")
select WANOPT in "${WAN[@]}"; do
case $WANOPT in
"IP")
wanip=$(dig @resolver4.opendns.com myip.opendns.com +short)
break
;;
"DNS/Domain")
echo -ne "Enter your preferred domain/dns address ${NC}: "
read wanip
#check wanip is valid domain
if ! [[ $wanip =~ ^[a-zA-Z0-9]+([a-zA-Z0-9.-]*[a-zA-Z0-9]+)?$ ]]; then
echo -e "${RED}Invalid domain/dns address${NC}"
exit 1
fi
break
;;
*) echo "invalid option $REPLY";;
esac
done
elif [[ ! -z "$resolveip" && ! -z "$resolvedns" ]]; then
echo -e "\nERROR: You cannot use both --resolveip & --resolvedns options simultaneously"
exit 1
elif [[ ! -z "$resolveip" && -z "$resolvedns" ]]; then
wanip=$(dig @resolver4.opendns.com myip.opendns.com +short)
elif [[ -z "$resolveip" && ! -z "$resolvedns" ]]; then
wanip="$resolvedns"
if ! [[ $wanip =~ ^[a-zA-Z0-9]+([a-zA-Z0-9.-]*[a-zA-Z0-9]+)?$ ]]; then
echo -e "${RED}Invalid domain/dns address${NC}"
exit 1
fi
fi
# Make Folder /opt/rustdesk/
if [ ! -d "/opt/rustdesk" ]; then
echo "Creating /opt/rustdesk"
sudo mkdir -p /opt/rustdesk/
fi
sudo chown "${uname}" -R /opt/rustdesk
cd /opt/rustdesk/ || exit 1
#Download latest version of Rustdesk
RDLATEST=$(curl https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest -s | grep "tag_name" | awk -F'"' '{print $4}')
echo "Installing Rustdesk Server"
if [ "${ARCH}" = "x86_64" ] ; then
wget "https://github.com/rustdesk/rustdesk-server/releases/download/${RDLATEST}/rustdesk-server-linux-amd64.zip"
unzip rustdesk-server-linux-amd64.zip
mv amd64/* /opt/rustdesk/
elif [ "${ARCH}" = "armv7l" ] ; then
wget "https://github.com/rustdesk/rustdesk-server/releases/download/${RDLATEST}/rustdesk-server-linux-armv7.zip"
unzip rustdesk-server-linux-armv7.zip
mv armv7/* /opt/rustdesk/
elif [ "${ARCH}" = "aarch64" ] ; then
wget "https://github.com/rustdesk/rustdesk-server/releases/download/${RDLATEST}/rustdesk-server-linux-arm64v8.zip"
unzip rustdesk-server-linux-arm64v8.zip
mv arm64v8/* /opt/rustdesk/
fi
chmod +x /opt/rustdesk/hbbs
chmod +x /opt/rustdesk/hbbr
# Make Folder /var/log/rustdesk/
if [ ! -d "/var/log/rustdesk" ]; then
echo "Creating /var/log/rustdesk"
sudo mkdir -p /var/log/rustdesk/
fi
sudo chown "${uname}" -R /var/log/rustdesk/
# Setup Systemd to launch hbbs
rustdesksignal="$(cat << EOF
[Unit]
Description=Rustdesk Signal Server
[Service]
Type=simple
LimitNOFILE=1000000
ExecStart=/opt/rustdesk/hbbs
WorkingDirectory=/opt/rustdesk/
User=${uname}
Group=${gname}
Restart=always
StandardOutput=append:/var/log/rustdesk/signalserver.log
StandardError=append:/var/log/rustdesk/signalserver.error
# Restart service after 10 seconds if node service crashes
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
)"
echo "${rustdesksignal}" | sudo tee /etc/systemd/system/rustdesksignal.service > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable rustdesksignal.service
sudo systemctl start rustdesksignal.service
# Setup Systemd to launch hbbr
rustdeskrelay="$(cat << EOF
[Unit]
Description=Rustdesk Relay Server
[Service]
Type=simple
LimitNOFILE=1000000
ExecStart=/opt/rustdesk/hbbr
WorkingDirectory=/opt/rustdesk/
User=${uname}
Group=${gname}
Restart=always
StandardOutput=append:/var/log/rustdesk/relayserver.log
StandardError=append:/var/log/rustdesk/relayserver.error
# Restart service after 10 seconds if node service crashes
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
)"
echo "${rustdeskrelay}" | sudo tee /etc/systemd/system/rustdeskrelay.service > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable rustdeskrelay.service
sudo systemctl start rustdeskrelay.service
while ! [[ $CHECK_RUSTDESK_READY ]]; do
CHECK_RUSTDESK_READY=$(sudo systemctl status rustdeskrelay.service | grep "Active: active (running)")
echo -ne "Rustdesk Relay not ready yet...${NC}\n"
sleep 3
done
pubname=$(find /opt/rustdesk -name "*.pub")
key=$(cat "${pubname}")
echo "Tidying up install"
if [ "${ARCH}" = "x86_64" ] ; then
rm rustdesk-server-linux-amd64.zip
rm -rf amd64
elif [ "${ARCH}" = "armv7l" ] ; then
rm rustdesk-server-linux-armv7.zip
rm -rf armv7
elif [ "${ARCH}" = "aarch64" ] ; then
rm rustdesk-server-linux-arm64v8.zip
rm -rf arm64v8
fi
echo "Grabbing installers"
string="{\"host\":\"${wanip}\",\"relay\":\"${wanip}\",\"key\":\"${key}\",\"api\":\"https://${wanip}\"}"
string64=$(echo -n "$string" | base64 -w 0 | tr -d '=')
string64rev=$(echo -n "$string64" | rev)
echo "$string64rev"
function setuphttp () {
# Create windows install script
wget https://raw.githubusercontent.com/dinger1986/rustdeskinstall/master/WindowsAgentAIOInstall.ps1
sudo sed -i "s|secure-string|${string64rev}|g" WindowsAgentAIOInstall.ps1
# Create linux install script
wget https://raw.githubusercontent.com/dinger1986/rustdeskinstall/master/linuxclientinstall.sh
sudo sed -i "s|secure-string|${string64rev}|g" linuxclientinstall.sh
# Download and install gohttpserver
# Make Folder /opt/gohttp/
if [ ! -d "/opt/gohttp" ]; then
echo "Creating /opt/gohttp"
sudo mkdir -p /opt/gohttp/
sudo mkdir -p /opt/gohttp/public
fi
sudo chown "${uname}" -R /opt/gohttp
cd /opt/gohttp
GOHTTPLATEST=$(curl https://api.github.com/repos/codeskyblue/gohttpserver/releases/latest -s | grep "tag_name"| awk '{print substr($2, 2, length($2)-3) }')
echo "Installing Go HTTP Server"
if [ "${ARCH}" = "x86_64" ] ; then
wget "https://github.com/codeskyblue/gohttpserver/releases/download/${GOHTTPLATEST}/gohttpserver_${GOHTTPLATEST}_linux_amd64.tar.gz"
tar -xf gohttpserver_${GOHTTPLATEST}_linux_amd64.tar.gz
elif [ "${ARCH}" = "aarch64" ] ; then
wget "https://github.com/codeskyblue/gohttpserver/releases/download/${GOHTTPLATEST}/gohttpserver_${GOHTTPLATEST}_linux_arm64.tar.gz"
tar -xf gohttpserver_${GOHTTPLATEST}_linux_arm64.tar.gz
elif [ "${ARCH}" = "armv7l" ] ; then
echo "Go HTTP Server not supported on 32bit ARM devices"
echo -e "Your IP/DNS Address is ${wanip}"
echo -e "Your public key is ${key}"
exit 1
fi
# Copy Rustdesk install scripts to folder
mv /opt/rustdesk/WindowsAgentAIOInstall.ps1 /opt/gohttp/public/
mv /opt/rustdesk/linuxclientinstall.sh /opt/gohttp/public/
# Make gohttp log folders
if [ ! -d "/var/log/gohttp" ]; then
echo "Creating /var/log/gohttp"
sudo mkdir -p /var/log/gohttp/
fi
sudo chown "${uname}" -R /var/log/gohttp/
echo "Tidying up Go HTTP Server Install"
if [ "${ARCH}" = "x86_64" ] ; then
rm gohttpserver_"${GOHTTPLATEST}"_linux_amd64.tar.gz
elif [ "${ARCH}" = "armv7l" ] || [ "${ARCH}" = "aarch64" ]; then
rm gohttpserver_"${GOHTTPLATEST}"_linux_arm64.tar.gz
fi
# Setup Systemd to launch Go HTTP Server
gohttpserver="$(cat << EOF
[Unit]
Description=Go HTTP Server
[Service]
Type=simple
LimitNOFILE=1000000
ExecStart=/opt/gohttp/gohttpserver -r ./public --port 8000 --auth-type http --auth-http admin:${admintoken}
WorkingDirectory=/opt/gohttp/
User=${uname}
Group=${gname}
Restart=always
StandardOutput=append:/var/log/gohttp/gohttpserver.log
StandardError=append:/var/log/gohttp/gohttpserver.error
# Restart service after 10 seconds if node service crashes
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
)"
echo "${gohttpserver}" | sudo tee /etc/systemd/system/gohttpserver.service > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable gohttpserver.service
sudo systemctl start gohttpserver.service
echo -e "Your IP/DNS Address is ${wanip}"
echo -e "Your public key is ${key}"
echo -e "Install Rustdesk on your machines and change your public key and IP/DNS name to the above"
echo -e "You can access your install scripts for clients by going to http://${wanip}:8000"
echo -e "Username is admin and password is ${admintoken}"
if [[ -z "$http" ]]; then
echo "Press any key to finish install"
while [ true ] ; do
read -t 3 -n 1
if [ $? = 0 ] ; then
exit ;
else
echo "waiting for the keypress"
fi
done
break
fi
}
# Choice for Extras installed
if [[ -z "$http" ]]; then
PS3='Please choose if you want to download configs and install HTTP server:'
EXTRA=("Yes" "No")
select EXTRAOPT in "${EXTRA[@]}"; do
case $EXTRAOPT in
"Yes")
setuphttp
break
;;
"No")
echo -e "Your IP/DNS Address is ${wanip}"
echo -e "Your public key is ${key}"
echo -e "Install Rustdesk on your machines and change your public key and IP/DNS name to the above"
echo - e "You can get a free API with Addressbook etc via https://github.com/infiniteremote/installer"
echo "Press any key to finish install"
while [ true ] ; do
read -t 3 -n 1
if [ $? = 0 ] ; then
exit ;
else
echo "waiting for the keypress"
fi
done
break
;;
*) echo "invalid option $REPLY";;
esac
done
elif [ "$http" = "true" ]; then
setuphttp
elif [ "$http" = "false" ]; then
echo -e "Your IP/DNS Address is ${wanip}"
echo -e "Your public key is ${key}"
echo -e "Install Rustdesk on your machines and change your public key and IP/DNS name to the above"
fi
这个脚本实际会到github下载4个东西。这里有一个问题:服务器ping不通github。所以官方的一键安装脚本没法执行。。
我这里是直接修改了脚本并且提前下载好所需的4个服务,上传到服务器(其他方法也行,能直接让脚本去github下载最好了)。大家如果自己实在不会修改,可以先去问问ai,改为读取本地已有的文件即可。
如果大家需要博主的脚本可以后面私信我。
脚本执行过程:
注意:如果服务器没有unzip命令先安装一下再运行脚本!
ps:上面图片上的注释写错了,不需要使用打印出来的ip!
拿到上面的key,然后填写到 主控端 和 被控端 的rustdesk客户端上:
点击确认后不出意外,客户端左下角会立即显示就绪,表示已经连接上了我们刚刚部署的服务器:
这个时候在主控端输入被控端的id,点击连接即可。
实测,默认画质、操作手感啥的大致与todesk免费版相当。。。😅请问这是巧合吗
注意避坑
1. 如果全部配置完毕后仍不显示就绪状态,先看看是不是服务器的防火墙没有关闭,导致端口无法访问。
ps:不建议大家直接关闭防火墙,还是在云服务器控制台开放对应端口。
2. 云服务器控制台中的防火墙是添加规则表示开放某个端口,不要理解成添加规则就表示禁用。。
3. 有没有在主控端和被控端都填写配置。
上述主要是博主遇到的问题,如果大家有其他问题欢迎评论区一起讨论。
参考资料
Release 1.4.1 · rustdesk/rustdesk
techahold/rustdeskinstall: Easy install Script for Rustdesk
rustdesk/rustdesk-server: RustDesk Server Program
其他资料:
貌似还有一个知乎上的文章写的不错,大家感兴趣可以去看看。。
最后求个👍,十分感谢!