通过 Cloudflare Tunnel 访问内网服务
2022-05-18 10:09:18

出于实际需求,需要访问位于内网的 Windows 实例。TeamViewer 和 AnyDesk 等第三方方案存在授权和安全问题,而 FRP 既不灵活也不安全(服务直接暴露于公网)。最终决定使用 Cloudflare Tunnel。

严格按照本文说明,仅需 15 分钟即可完成所有操作。

服务端侧

首先从 Cloudflare/Cloudflared 下载编译好的二进制,解压至本地。

1
2
3
4
5
6
7
8
# 1. 登录到账号
.\cloudflared.exe login

# 2. 创建隧道
.\cloudflared.exe tunnel create <NAME>

# 3. 手动运行
.\cloudflared.exe tunnel --hostname <域名> --url rdp://127.0.0.1:3389

如果你没有 Cloudflare 账号,或者 Cloudflare 账号没有域名,则可以通过下方命令进行测试,会自动生成一个临时的 URL 用于访问。

1
.\cloudflared.exe tunnel --url rdp://127.0.0.1:3389

这里的 --url 参数为 service URL,用于指定要代理的服务,格式为 <协议头>://<IP>:<端口>。一些样例如下:

1
2
3
4
5
6
7
8
9
10
11
12
# RDP
rdp://127.0.0.1:3389
# TCP
tcp://127.0.0.1:554
# HTTP
http://127.0.0.1:80
# Minecraft
tcp://localhost:25565
# MySQL
tcp://localhost:3306
# SSH
ssh://localhost:22

Cloudflare Tunnel 可以用于中转任意 TCP 服务,也可以用于映射至内网其他服务,具体细节可以参考官方文档。暂时不支持 UDP 服务

客户端侧

客户端侧比较简单,下载编译好的二进制后

1
.\cloudflared.exe access rdp --hostname <域名> --url localhost:<映射至本地的端口>

随后,在 RDP 客户端中填写 localhost:<本地端口> 即可访问到远程桌面了。

注意:如果此处端口使用 3389,Windows 自带的 RDP 客户端会阻止连接。我们可以

  1. 访问 127.0.0.2,它其实是 127.0.0.1 的另一个别名。
  2. 换一个端口。
  3. 使用兼容性设置,让 mstsc.exe 以另一个系统环境运行。

注册为 Windows 服务实现自动运行

服务端侧

我们可以将其注册为系统服务于后台运行,免去手动运行 daemon 的烦恼。参考此页面

  1. 注册为系统服务

    1
    .\cloudflared.exe service install
  2. C:\Users\%USERNAME%\.cloudflared\ 目录下有一个形如 6ff42ae2-765d-4adf-8112-31c55c1551ef.json 的文件,为刚刚所创建的隧道的鉴权信息。在同目录下创建 config.yaml,内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 隧道的 UUID
    tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
    # 鉴权文件的绝对路径
    credentials-file: C:\Users\%USERNAME%\.cloudflared\6ff42ae2-765d-4adf-8112-31c55c1551ef.json

    ingress:
    # 修改为自己的二级域名
    - hostname: rdp.example.com
    service: rdp://localhost:3389
    # 兜底规则,将不符合前序规则的请求回应 404 错误
    - service: http_status:404
  3. 配置 DNS 记录(需要使用自己的域名)

    1
    .\cloudflared.exe tunnel route dns <NAME> <DOMAIN>
  4. 修改注册表(regedit.exe),配置启动参数:在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services找到 Cloudflared,将 ImagePath 修改为 <cloudflared.exe 绝对路径> --config=<YAML 配置文件绝对路径> tunnel run

  5. 通过命令行或任务管理器重启 Cloudflared 服务即可完成。

    1
    2
    3
    # 需要管理员权限
    sc stop cloudflared
    sc start cloudflared

客户端侧

只需将 ImagePath 修改为 <cloudflared.exe 绝对路径> access rdp --hostname <域名> --url localhost:<映射至本地的端口>,然后重启 cloudflared 服务。

可选操作:为 RDP 文件签名

mkcert 生成自签名的证书 host.pemhost-key.pem 改成同样的名字 host.pemhost.key,使用 Windows 自带的 certutil,合并为 host.pfx 文件。

1
certutil -mergepfx host.pem host.pfx

host.pfx 导入至 certmgr 的 Personal 目录。复制该证书的 SHA-1 指纹(thumbnail),使用 Windows 自带的 rdpsign 进行签名。

1
rdpsign /sha256 <sha1 hash> test.rdp

可选操作:将桌面程序配置为 RemoteApp

RemoteApp 是基于 Windows RDP 的增强功能,将远端的程序以本地程序的形式显示,效果和 VMware 的 Unity Mode 相似。

这里下载 RemoteApp Tool,添加本地程序(例如 Chrome, VMWare 等),并为之生成 .rdp 文件即可。全程傻瓜化操作,十分简单。注意 hostname 和端口需要修改为前文对应的客户端侧地址。

用户体验

默认设置

播放 YouTube 视频、打字都很流畅。快速滚动页面时可能会有一些掉帧;快速拖动窗口会存在少量拖影和延迟。

以 RemoteApp 形式运行应用时,部分 pop-up 菜单的显示效果不理想,可能会出现短暂的黑色或阴影部分。

细节优化

RDP 服务端优化:修改组策略(gpedit.msc),找到 计算机配置 -> 管理模板 -> Windows 组件 -> 远程桌面服务 -> 远程桌面会话主机 -> 远程会话环境

  • RDP 默认帧率 30,可以手动提升至 60 上限(不一定能达到):新建 DWMFRAMEINTERVAL,值设为十进制 15。在 1080p 分辨率下,修改前测试为 22 fps,修改后测试为 33 fps,提升 50%。
  • 隐藏壁纸:建议开启,节约带宽。
  • 硬件 GPU 加速:建议开启。
  • H.264/AVC 硬编码支持:如果有看视频需求,建议开启,否则建议关闭。
  • RemoteFX data compression:看情况吧,我设置为 optimize for use less network bandwidth。
  • Image quality:默认是由 RDP 客户端和服务端根据网络情况自协商的,可以在此强制指定。

RDP 客户端优化

  • 在 RDP 客户端高级选项中的 Experience 菜单指定。在网络质量很好的情况下,选择 WAN(10Mbps with high latency)或 Auto detect 对稳定性没有明显影响。建议设置为自动。
  • Colors:设置为 16-bit 足够满足办公需求。

总结

从本地到 Cloudflare DC 到远端计算机的 RTT 约为 60 ms。经过上述优化,在 16-bit 色彩、1080p 分辨率和自动画质的设置下,流畅性与本机应用近乎一致。

最后,为什么使用 RDP 而非 VNC 呢?

正如前面提到的 VNC 协议是基于像素的。尽管这带来了极大的灵活性,可以显示任何类型的桌面,但它的效率往往不如那些更好地理解底层图形布局(例如: X11)或桌面(例如:RDP)的解决方案。这些协议以更简单的形式(例如:打开窗口)发送图形原语或高级命令,而 VNC 的 RFB 协议尽管支持压缩但只能是发送原始像素数据。

VNC 还是 RDP? 云上的远程桌面究竟该如何选 - AWS Team

References

Prev
2022-05-18 10:09:18
Next