HTTP(s) proxy 的实现
代理服务器程序(proxy)
最近项目调整到了web网关业务,之前对网关的daemon 功能有个大致的了解。 网关的流量模型本质是proxy, 在转流量的同时做virus-scanning, url filtering, traffic control 以及一些零零散散的统计报表,blocking page 的显示。
总体上没什么难理解的, 无非是non-block + epoll 的实现, 可以参考 tigerso
但是正如之前文章(HTTPS Decryption)讲的, 现在的web整体趋势是往https转, 所以作为web安全网关应该尽可能去解密HTTPS traffic, 才能充分调用网关的功能。
Https Decryption 介绍了解密(MITM)的原理是重签证书与客户端信任。
真正实现的过程中有两种方法:
- 拿到client connect 请求现行与 server 进行SSL握手, 然后重签证书, 与client 进行SSL 握手
- 拿到client connect 请求, 接收client 的 ssl-client-hello, 此时不进行ssl accept 而是将 ssl-client-hello 的所有信息(ssl version, cipher, alpn) 复制到 与server的ssl-client-hello中, 与 server 进行SSL握手, 然后重签证书, 与client 继续进行SSL 握手
方案1 最简单直接, 缺点是proxy不能模拟client的ssl 行为,会造成特殊站点(可能是用户内部自建website)的握手失败。
网关程序一般需要精确的模拟client行为(方案2),将ssl的设置留给使用者,为不是写死在程序里。
但是方案2需要hook “SSL_accept”, 改变api的行为,能够两次重入函数: 第一次 只拿client hello, 不做任何check, 不发ssl response 第二次 继续回到ssl 握手的流程
proxy 实现 (tigerso)
我想每个有理想的 programmer 内心都是有些排除他人程序的配置,希望能够完全掌控软件的所有细节, 有些程序洁癖。 我也一样,看了项目文档, 顿感手痒,花来三个星期自己造的个 forward http(s) proxy: tigerso
为了简单起见,tigerso 只实现了方案1的https MITM,做了简单的用户控制,websites控制。 目前已经将tigerso部署到公司的linux跳机上,成功替换掉squid, 完成了从公司翻墙代理的任务。因为是自己翻墙使用,现在已经关掉了Https Decryption功能。
在一个月的试用里面, 实测方案1对于一些不知名的https 站点确实不能完美解密。 对google这样的大站没什么影响。 但是youtube还是bypass吧,毕竟想要好的体验,视频流还是不要倒手一遍了。 另外mobile app / 网页app 的https基本没办法解开, 毕竟让我写app也会内嵌信任证书的,这写流量可以通过requst的agent值bypass。
epoll + non-block Reactor模型确实能够轻松的撑住10000级别的并发访问量, 性能测试中我用 4台linux肉鸡多线程不停打老虎(tigerso), 这只老虎也没死,最后是一般是卡在带宽上了, tcp 握手慢, tigerso就踢掉这些连接了。
值得吐槽的是这种异步回调方式,真的很考验人的心智, callback会不断的传播,每一步的状态都要保留并在callback中恢复。 如果只是http server其实还好, 但是https proxy就需要triple这些状态,后续再接上公司认证服务的话,又会是一大堆状态和callback.
如果后面还有时间和兴趣,还想集成些开源的virus scanning, url classification 库,看看效果。
二维码在线要饭