本来没怎么关注这个东西,支持少非主流,IP over TCP,也是因为一些奇怪的事突然有了这方面需求了,才开始了解它。
目前在 Linux,服务器似乎只有 SoftEther 这个实现。这软件也是挺拼的,把各种流行的非主流的 VPN 协议都给实现了一遍,跨平台,GPL。还提供傻瓜化的 GUI 管理,中日英三语界面,简直就是部署 VPN 的大杀器。只是一来太重,二来对 RADIUS 支持不全,不太符合需求。
因为内部走的是 PPP,如果只是调用 pppd 并转发数据,由 pppd 负责用户认证等麻烦事,自己实现一个目测也不是太复杂。加上微软的官方文档甚详,近来又一直想学一下 Twisted 但又不知如何入手,决定来写一个试试。
托 Twisted 的简单易用,挺顺利地初步完成了这个 SSTP 服务器。
代码放在 GitHub 和 PyPI 上了,sorz/sstp-server 。
供参考,详见sstpd --help
:
sudo apt-get install python-dev python-pip python-twisted sudo pip install sstp-server sudo sstpd -c cert.pem --local 10.0.0.1 --remote 10.0.0.0/24
关于证书,请参考 HTTPS 证书相关教程。
别忘了建/etc/ppp/options.sstpd
,一个例子:
name sstpd require-mschap-v2 nologfd nodefaultroute ms-dns 8.8.8.8 ms-dns 8.8.4.4
需要 IPv6 支持的,可加参数--listen ::
。
折腾过程中遇到的主要障碍还是关于 PPP 的。简单地转发是不行的,因为每个 SSTP 包中只允许放入一个 PPP frame,需要自行分离出每个 frame。
PPP 这个古老的协议比预想中的要复杂。Windows 在 SSTP 中使用的其实是这个 HDLC-like Framing。找了一份 SSTP 客户端的代码,它是自行对两种格式进行了转换。好在后来发现 pppd 其实自身就支持 HDLC(sync 参数),于是成功偷懒。
但后来发现 HDLC 似乎需要 Linux 内核支持(CONFIG_PPP_SYNC_TTY = yes
)。
更新:
果然偷懒失败了,用 HDLC 取巧的方法是不行的,偶尔会有 frame 被截断,后面就全乱了。于是还是老老实实地照着 RFC 1662 来做 framing,自行 (un)escape 一些字符。但是这样性能变得很糟糕,试着把这部分用 C 扩展重写了一遍。想来这还是我第一次出于解决问题的需要写 C 呢……
感谢 @deba12 指出了这个问题,并协助测试、改善性能。
目前这个实现其实是不完整的,没有实现 Crypto binding 部分,导致其可能遭受中间人攻击。
使用了 SSL 还会遭受中间人攻击?微软在文档末尾提供了一个这种攻击的情境,挺有意思的。
攻击者建立一个假 Wi-Fi AP,然后诱骗用户连接。
Wi-Fi 使用 802.11 EAP 进行认证,用户以为他是在登录 Wi-Fi,但实际上,攻击者将这个认证请求转发给了 SSTP(PPP) 服务器!用户确实在和真的认证服务器在对话,只不过认证的不是 Wi-Fi 而是 SSTP 服务。
Crypto binding 可以防止这种攻击,想详细了解请参见微软文档。
但实现这个有些复杂,我这边的使用情境暂时没有这个需求,就先放一放了。
(懒你就直说 _(:з」∠)_