如何参与开源项目

2022-01-12 ⏳5.3分钟(2.1千字) g

今天是个值得纪念的日子。经过长达半年的努力,我给pion/dtls项目贡献的会话恢复功能终于被社区合并了。这是我参与的功能最复杂、涉及面最广、沟通最深入、时间跨度最长的一次开源合作。社区最终接受了我的提案和代码,也进而承担了后续的维护工作。今天就说一说这个事情的来龙去脉,以及我对如何参与开源项目的经验和反思。

大约在去年五月份,我开始研究思科的 AnyConnect VPN 协议。AnyConnect 在企业中应用非常广泛,也有像 OpenConnect 这样的开源实现。但这些开源实现大都是用C语言实现,难读难改。我就想着用Go语言重新开发一套。从网上搜了一下,找到了名为 AnyLink 的项目,完成度很高。我看了一下代码,发现这个项目把主要精力花到了管理后台相关的功能,但最重要的 DTLS 隧道功能却没有实现。

什么是 DTLS 隧道呢?AnyConnect 是所谓的 WEB VPN。用户认证通过以后,客户端会发起一个 HTTP CONNECT 请求,然后对应的 HTTP 会话会转为双向 TCP 连接,所以 VPN 上的 IP 报文都通过这个 TCP 连接传输。使用可靠的 TCP 连接传输不可靠的 IP 报文会衍生出很多问题,具体可以参考我的这篇文章。有鉴于此,AnyConnect 支持使用 UDP 版的 TLS 协议,也就是 DTLS 协议来收发 IP 报文。但这么核心的功能居然没有实现,这是非常大的遗憾。

那为什么没有实现呢?我查了一下,发现 AnyConnect 依赖 DTLS 的会话恢复功能。目前 Go 社区使用最多 DTLS 实现是 pion/dtls,但这个库不支持会话恢复。于是我在 pion/dtls 切了一个分支,简单实现了会话恢复功能,并给 AnyLink 项目做了适配。由于我对 DTLS 的了解有限,没能实现会话恢复的 完整特性,所以也就没好意思给 pion 提交 Pull Request。AnyLink 收到我的原型后居然就合并了,我还写着是 WIP 呢。再后来,AnyLink 那边就直接把我魔改过的 pion/dtls 代码直接拷贝到自己的项目。这是我最不愿意看到的。Fork 容易 Merge 难。我们不能只做简单的事,还要做正确的事。

从那之后,我就想着怎么让上游的 pion/dtls 支持会话恢复功能。于是我在2021年5月20号发起了Merge Request。我最早的希望是针对 AnyConnect 添加最小改动,我甚至连分支名都叫 cisco。

I have made my best to make changes of this PR as small as possible. Please make your comments. Thank you.

很快得到了正向回复,比如Daniele Sluijters:

With respect to your PR: we can’t accept this addition of functionality without tests that verify it works as intended. This will also ensure we don’t break this feature in the future. This is especially important for features we don’t expect to be used/exercised regularly by users of the library.

没有反对,只是说需要添加测试用例。这个问题不大。但是 Daniele 也指出:

On a personal note; we have made it this far without it, and I am a little frustrated by the idea of supporting this b/c a company with resources like Cisco can’t be bothered to follow an RFC and implement PSK support. However, that should not stop us from supporting session resumption in general.

Daniele 的意思是如果思科支持通过 PSK 方式协商 DTLS 会话,就不需要实现基于ID的会话恢复功能。但是,他并没有反对我的提案。

于此同时,Sean DuBois也表示赞同,并强调需要添加有单元测试以及仔细考虑安全问题:

Very cool project! Would love to help get this merged :) 100% agree with @daenney we need tests.

Also DTLS resumption has been known to cause security issues so want to be especially careful with this change!

我再次向他们确认,如果不反对我的提案,我就添加测试用例。而 Daniele 则要求一定要实现完整的会话恢复功能。

收到反馈后我倍受鼓舞,觉得很快就能合并这个分支。接下来做主要做两件事情。一是完整实现了基于标识的会话功能。这是一个非常痛苦的过程,需要反复阅读 RFC 标准,还要理解 pion/dtls 现有的代码。二是添加完整的测试用例。到了6月14号,我提交了第一个完整版本,并把 MR 的标题从 Cisco AnyConnect DTLS resume 改为 Add session resumption support。然后请他们做代码评审。

过了两天,没有动静。我就有点着急,于是留言询问。结果 Daniele 回复说这是开源项目,大家都是业余时间维护,没法在几个小时内回应。

时间已经过了两天,并非几个小时。我也没要求他们马上评审,只是想问一下后面的安排。但他这样说我就有点生气,于是回怼了一下。

然后,一个月过去了,杳无音讯。于是我在7月15号继续留言。没有任何回复。到了8月11号,我再一次留言。这次 Daniele 回话了,说自己现在没有时间,但代码没有问题,等他空闲了会看。然后,就没有然后了。

半年后,偶然想起还有这么一个 PR,于是再次留言讯问。这次 Daniele 终于说了实话。他说自己用不到会话恢复功能,所以也就没有专门研究。还说我的改动比较多,如果合并,后面需要持续维护。说白了就是自己不太懂,也不太想管。我内心问候他老妈一万次。你早干什么呢?半年前说没时间,现在说没兴趣……

不过,他在回复中问其他维护者是否愿意看我的代码,然后我的白衣骑士Atsushi Watanabe出现了。他就说了一句话:

I’ll review the code tomorrow

太他妈的帅了!这时候已经是12月12号。双十二不去购物却跟外国人扯皮,卷不卷😂

后面就进入了开发的快车道。Atsushi是安全方面的专家,提出了很多专业的意见,我一一改正。十天之后的23号,Atsushi 说代码的主流程没有问题了,剩下的都是细节。又过了四天,也就是27号,Atsushi 同意合并。终于等到了这一刻。

顺便说一句,Atsushi 是日本人,跟我们时区相近。所以我提交的改动他总能很快地回复,整个合作过程非常顺利。

到这个时候,西方已经进入圣诞和元旦假期,没人合并代码。等到2022年的1月3号,Daniele 再次现身,表示我的提交都在假期完成,别人没时间看,需要给其他维护者一周的时间,看有没有反对意见。并约定在1月10号合并。

到了11号早晨,没有任何动静。我真是等到花儿都谢了。今天起床看了一下邮件,Daniele 终于合并了我的分支,并且发布了 v2.1.0 版本。从此,这个世界因为我而有所不同。做了一点微小的贡献,实在是惭愧。

现在总结一下参与开源项目的技术要点:

以上就是本文的全部内容,希望能给大家带来启发。