0%

FRP+SS+XRDP/VNC实现内网穿透远程控制内网计算机

Abstract

我在实验室部署开发环境的时候,为了图省事选择了TeamViewer作为远程控制的解决方案。随着我一通操作(前后添加了4台并且频繁使用),我成功被TV标记为了商业用户,每个session限时5min并且严重限制连接频率。唉,TV吃相越来越难看了,我只能去官网提交了申诉,不知道何时才能解封。但是作为一个穷的哭出声的(伪)geek,咋能不自己想想解决办法呢?

查了一堆资料和结合自己现有的资源,打算利用FRP+SS+XRDP/VNC来解决这个问题:在任意一个拥有公网访问的地方,通过RDP/VNC协议来访问内网里的一台机器。

Update 2020/04/05 新增了bbr优化相关

Update 2019/12/12 新增了关于ss加密算法的建议。todo: bbr优化

Update 2019/12/11 新增了关于shadowsocks-libev的描述

Update 2019/12/08 修正了关于proxifier代理配置的描述

Update 2019/08/20 经过了半个月的申诉,teamviewer终于解封了我的账户,现在我可以正常使用了。

Part 0 准备工作

内网穿透的基本思路

内网电脑装上ss-server(sss)和frp-client(frpc),自己的电脑装ss-client(ssc),服务器端装frp-server(frps)。

ssc会在本地(localhost/127.0.0.1)启动一个Socks5 Listener,监听本地的1088端口,即127.0.0.1:1088,并且将监听到的数据转发到服务器的1919端口,即a.b.c.d:1919。运行在服务器上的frps会将接受到的指定端口的数据通过7000端口转发回内网机器,内网机器上的frpc也通过7000端口接收到frps发来的数据之后,再转发给sss的8888端口。sss会监听8888端口,再转发数据到达目的地。这样就完成了整个内网穿透的过程,以VNC为例,即:

VNC viewer发起访问114.5.1.4的请求 -> 127.0.0.1:1088 (自己电脑,ssc监听) -> a.b.c.d:1919 (服务器,frps监听) -> a.b.c.d:7000 (服务器,frps) -> a.b.c.d:7000 (内网电脑,frpc) -> 127.0.0.1:8888 (内网电脑,sss监听) -> 127.0.0.1:5091 (内网电脑,VNC server)

硬件要求

  • 一台运行在内网中,可以连接到公网的电脑。这是你需要在外网远程控制的电脑。
  • 一台你自己的电脑。这是用来控制内网电脑的电脑。

你需要额外拥有:

  • 一台有公网ip的服务器,并且保证内网电脑和你的电脑都可以访问这台服务器。我的服务器正好在村里,延迟比较低。

操作系统/软件要求

实际上对以上三台电脑的操作系统并没有什么硬性的要求,不同的操作系统和桌面环境下都会有对应的解决方案,请自行搜索,思路是不变的。本文中,我使用了:

  • 内网电脑:Xubuntu 18.04 LTS,内网ip:114.5.1.4
  • 自己的电脑:macOS Majove 10.14.6
  • 服务器:Ubuntu 18.04 LTS,公网ip:a.b.c.d

Xubuntu和Ubuntu的区别是,前者的桌面环环境是xfce4,后者是gnome 3。这两者之间的区别有坑,mac系统在一些情况下也有坑,后面会说。

自己电脑

需要ss客户端,这里直接安装SSX-NG。你也可以使用homebrew安装(并且两个ss可以共存),但是需要使用配置文件配置,请自行搜索。

此外,你还需要proxifier来配置代理。谷歌可得可用序列号,如有能力请支持正版。

服务器

更新

1
$ sudo apt update && sudo apt upgrade

此外,下载最新版本的frp,并解压到~/frp。

内网机器

更新,安装软件

如果你在使用树莓派等嵌入式设备作为内网电脑(这样你使用这个梯子的时候就可以访问内网中的别的机器的了),请不要安装shadowsocks,而安装shadowsocks-libev作为替代。将下面的命令中的shadowsocks改为shadowsocks-libev即可。前者使用pytyhon实现,效率较低,后者使用c实现,专为嵌入式设备设计,效率较高。

1
2
$ sudo apt update && sudo apt upgrade
$ sudo apt install vim openssh-server tightvncserver shadowsocks
  • openssh-server: SSH服务器,ubuntu桌面版只带SSH客户端,不带服务器端,无法远程使用SSH连接。
  • tightvncserver: VNC服务器
  • shadowsocks: 代理。它的作用可比爬梯子大多了2333333

下载最新版本的frp,并解压到~/frp。

Part 1 内网穿透

本章节介绍了如何利用上面安装的一堆软件来实现内网穿透。

内网机器

SSH server配置

SSH这里用来确保vnc服务的可用性,或者在一些不需要图形界面的情况下,ssh就可以完成对应的任务。

使用如下命令查看ssh服务是否启动:

1
$ ps -ax | grep ssh

你应该可以看到ssh-agent(客户端)和ssh-server(服务端),如果没有,使用如下命令启动:

1
$ sudo service ssh start

sss配置

设置sss的端口、密码和加密方式等

新建/etc/shadowsocks.json,配置sss。输入以下内容,设置代理监听的端口,密码和加密方法。

1
2
3
4
5
6
7
8
{
"server":"0.0.0.0",
"server_port":8888,
"local_port":1080,
"password":"wdnmd_team_viewer",
"timeout":300,
"method":"aes-256-cfb",
}

如果你使用的是树莓派或别的不支持aes加速的设备,推荐将加密算法替换为chacha20-ietf-poly1305。树莓派的cpu内并没有aes加速指令集,这是由于aes加速是armv8的一个可选指令集,得加钱从arm那买license。更扯的是树莓派用了一个arm64的cpu,却还用的是32位的系统。

如果你使用shadowsocks-libev,请使用如下命令编辑sss的配置文件:sudo vim /etc/shadowsocks-libev/config.json

配置sss系统服务

新建sss服务的配置文件

1
$ sudo vim /etc/systemd/system/shadowsocks-server.service

复制粘贴(ExecStart后面的-c之前的路径是sss的地址,可能会有细微不同,可以通过whereis ssserver命令来确定;-c之后的路径是上面的sss自己的配置文件地址)

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Shadowsocks Server
After=network.target

[Service]
ExecStart=/usr/local/bin/ssserver -c /etc/shadowsocks.json
Restart=on-abort

[Install]
WantedBy=multi-user.target

如果你使用shadowsocks-libev,你可以忽略新建配置文件这步

保存之后,启动并开机启动sss

1
2
$ sudo systemctl start shadowsocks-server
$ sudo systemctl enable shadowsocks-server

如果你使用shadowsocks-libev,请将shadowsocks-server替换为shadowsocks-libev.

frpc配置

编辑~/frp/frpc.ini,配置frpc通过7000端口与frps通信,并且将服务器端1919端口转发来的包转发到本地8888端口。

1
2
3
4
5
6
7
8
9
[common]
server_addr = a.b.c.d
server_port = 7000

[ss]
type = tcp
local_ip = 127.0.0.1
local_port = 8888
remote_port = 1919

编辑frpc服务配置文件

1
$ sudo vim ~/frp/systemd/frpc.service

修改ExecStart和ExecReload后的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Frp Client Service
After=network.target

[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/home/username/frp/frpc -c /home/username/frp/frpc.ini
ExecReload=/home/username/frp/frpc reload -c /home/username/frp/frpc.ini

[Install]
WantedBy=multi-user.target

设置frpc开机自启动并启动frpc

1
2
3
$ sudo mv ~/frp/systemd/frpc.service /etc/systemd/system/frpc.service
$ sudo systemctl start frpc
$ sudo systemctl enable frpc

BBR优化

Google的BBR算法可以用来优化TCP连接,并且4.9+的Linux内核已经合并了该功能。最新版的Raspbian内核版本已经是4.9+,所以我们直接讨论该种情况。如果你的内核版本低于4.9,请自行查阅相关资料。

先切换到root,然后写入系统配置并保存

1
2
3
4
$ su root
$ echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
$ echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
$ sysctl -p

测试内核是否已经开启bbr

1
$ sysctl net.ipv4.tcp_available_congestion_control

输出类似于

1
output: net.ipv4.tcp_available_congestion_control = bbr cubic reno

查看bbr是否已经启动

1
$ lsmod | grep bbr

如果看到一下信息,说明已经成功启动

1
tcp_bbr                20480  14

Credit to: https://www.moerats.com/archives/297/

服务器设置

编辑~/frp/frps.ini,配置frps通过7000端口与frpc通信。

1
2
3
[common]
server_addr = a.b.c.d
server_port = 7000

编辑frps服务配置文件

1
$ sudo vim ~/frp/systemd/frps.service

修改ExecStart后的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=Frp Server Service
After=network.target

[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/home/username/frp/frps -c /home/username/frp/frps.ini

[Install]
WantedBy=multi-user.target

设置frpc开机自启动并启动frps

1
2
3
$ sudo mv ~/frp/systemd/frps.service /etc/systemd/system/frps.service
$ sudo systemctl start frps
$ sudo systemctl enable frps

自己电脑设置

首先打开SS,添加代理信息。

Address: a.b.c.d, port: 1919 (为frpc[ss]中的remote port)
Encryption: sss中的method,即aes-256-cfb
Password: sss中的password,即wdnmd_team_viewer

接着将ss切换到手动模式(Manual Mode)。然后点Prefences - Advanced - Local Socks5 Listen Port,记下该端口号(本文中使用1088,你可以自己设置)。这样,ss就配置好了,我们只需要使用proxifier配置指定的APP/网站的流量走socks5代理即可。

首先在proxifier中添加这个代理。打开Proxifier - Proxies - Add,Address为127.0.0.1,port为1088,Protocol为SOCKS VERSION 5,然后点OK。

接着设置我们访问该内网ip时,使用这个代理。Proxifier - Rules - Add,Target Hosts填写内网ip 114.5.1.4,Action为Proxy SOCKS5 127.0.0.1:1088,点ok。

现在,打开terminal,你应该可以使用ssh登录你的内网电脑了。

Part 2 VNC/XRDP配置

我觉得需要先讲一下这里的坑。

VNC对gnome 3存在兼容性问题,而现在的ubuntu默认安装的就是gnome 3,这使得我花费了大量时间来试图解决这个问题。虽然网上有些教程讲的是可以的,但是我并没有成功,反而发现了一些文章在说VNC和gnome 3不兼容,这也是为什么我换了Xubuntu。如果你正在使用ubuntu,don’t panic,因为你可以再新装一个xfce4桌面环境,可以参考这篇文章

XRDP的坑在于,你使用apt install安装的版本是0.9.5,而对于mac客户端来说,XRDP存在一个字库丢失的问题,这个问题在0.9.8版本中才被修复。所以如果你在使用mac作为客户端,可以考虑参考官方教程在内网电脑上自行编译安装XRDP。

VNC

VNC这玩意搞过树莓派的应该都知道,Raspbian自带vnc支持的,早期连个显示器太费劲,大家都用vnc远程控制它。

在终端输入

1
$ vncserver

来配置VNC服务器。密码长度必须介于6-8个字符,超过8个字符会被截断。

1
2
3
4
You will require a password to access your desktops.

Password:
Verify:

vncserver会询问你是否要创建一个view-only password,选择n,不然只能view,不能控制。然后会启动一个编号为1的vnc session(your_hostname:1)。有的情况下会出现报错(和桌面有关),1启动不了,会启动一个编号为2的vnc session,这里需要注意。

1
2
3
4
5
6
7
8
Would you like to enter a view-only password (y/n)? n
xauth: file /home/test/.Xauthority does not exist

New 'X' desktop is your_hostname:1

Creating default startup script /home/test/.vnc/xstartup
Starting applications specified in /home/test/.vnc/xstartup
Log file is /home/test/.vnc/your_hostname:1.log

然后杀死现在的VNC session,备份,修改配置文件。

1
2
3
$ vncserver -kill :1
$ sudo mv ~/.vnc/xstartup ~/.vnc/xstartup.bak
$ sudo vim ~/.vnc/xstartup

在vim中输入以下内容(请谷歌vim用法,不然会变成随机字符生成器233333)

1
2
3
4
5
6
7
8
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
startxfce4 &

[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey

保存文件,重新启动vncserver

1
2
$ sudo chmod +x ~/.vnc/xstartup
$ vncserver :1

会看到如下输出

1
2
3
4
New 'X' desktop is your_hostname:1

Starting applications specified in /home/test/.vnc/xstartup
Log file is /home/test/.vnc/your_hostname:1.log

和上面提到的一样,如果启动:1失败,可以改成:2,启动一个编号为2的session。

XRDP

RDP协议走的就是大chou名ming鼎zhao鼎zhu的3389端口,它在windows上是自带支持的,但是在ubuntu上,你需要安装XRDP。你可以直接参考这篇教程。此外,如果你在使用mac,请参阅上面的#坑#章节。

直接使用如下命令安装:

1
2
3
4
5
$ sudo apt install xrdp
$ sudo sed -e 's/^new_cursors=true/new_cursors=false/g' \
-i /etc/xrdp/xrdp.ini
$ sudo systemctl enable xrdp
$ sudo systemctl restart xrdp

创建~/.xsession 和 ~/.xsessionrc

1
2
3
4
5
6
7
8
$ echo "xfce4-session" > ~/.xsession
$ D=/usr/share/xfce4:/usr/share/xubuntu:/usr/local/share
$ D=${D}:/usr/share:/var/lib/snapd/desktop:/usr/share
$ cat <<EOF > ~/.xsessionrc
export XDG_SESSION_DESKTOP=xubuntu
export XDG_DATA_DIRS=${D}
export XDG_CONFIG_DIRS=/etc/xdg/xdg-xubuntu:/etc/xdg:/etc/xdg
EOF

需要禁用light-locker,不然会有如下报错:

1
2
** (light-locker:2769): ERROR **: 08:09:59.625: Environment variable
XDG_SESSION_PATH not set. Is LightDM running?

使用如下命令来解决这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo cp /usr/bin/light-locker /usr/bin/light-locker.orig
$ cat <<EOF | sudo tee /usr/bin/light-locker
#!/bin/sh

# The light-locker uses XDG_SESSION_PATH provided by lightdm.
if [ ! -z "\${XDG_SESSION_PATH}" ]; then
/usr/bin/light-locker.orig
else
# Disable light-locker in XRDP.
true
fi
EOF
$ sudo chmod a+x /usr/bin/light-locker

最后,搜索安装微软官方的RDP客户端Microsoft Remote Desktop,输入你的内网ip就可以访问。

Part 3 性能测试

实际使用中,VNC和XRDP高画质下并不会卡顿。

Part 4 讨论

本方案 vs TeamViewer

有一个显而易见的缺点是,本方案并不是同步和直接控制现有的屏幕,而是新建了一个桌面进行操作。这可能会给一些复杂任务带来困难。不过这也可能也会成为本方案的优点,因为你log out退出到登录界面之后,依然可以通过VNC连接到内网计算机,并且因为是新建了一个桌面,你的真实电脑桌面依然是锁屏状态,这样就保证了你电脑的安全性。

另一个显而易见的缺点是,opengl没办法起来,可能需要virtual opengl之类的库。这对一些需要用到显卡的东西非常不友好。最简单的例子就是,没办法开安卓虚拟机。不过可以通过配置远程ADB解决或者用Scrcpy来进行手机投屏。这实在不行,还能把apk传回来本地再自己装嘛。

好处大概就是不需要再受teamviewer的气了吧233333,没花啥钱搭出来的玩意,要什么自行车。

VPN vs SS

和VPN相比,ss轻量级了很多,并且配置起来也更方便。VPN的主要问题在于握手次数过多,使得大量的时间浪费在了这个上面。不过VPN可以走UDP,但是运营商会有QoS的限制,可能会丢包。

其他玩法

如果内网机器在家中,那稍微配置一下就可以连回家里的局域网了。如果内网机器是一个国内节点,并且国内节点在你家,那你就有了有真实地址的国内ip了(非机房ip),诶嘿嘿嘿。

你也可以把你的网站布置在内网服务器中,通过frp转发出去,这样可以很好的隐藏自己的服务,具体玩法可以参考frp的手册。