目录
前几天被一个生产环境的bug给折磨的有些喘不过气,一来他非常随机,只有部分用户碰到,也没办法在本地重现,二来hard deadline没有几天了,roll back呢,怕是要赶不上deadline。不roll back呢,修复又毫无头绪。
更尴尬的是,这是我进公司搞的第一个项目,而且我就是总负责。人脉么人脉还没建立,公司的知识体系么还不是那么系统,正当需要靠项目立威之时,碰到这么一个问题,压力可谓巨大。
事件流程?
项目需要做一个SSO的升级,从其他的验证方式升级到OpenID,前后端都搞定并release之后,一部分用户突然报告infinite loop,无法完成认证进入程序。奇怪的是工程团队的人员都没办法重现这个问题,而且用户中也只有一部分人有这个问题,更诡异的是,这个问题是间歇性的。
我们收集到了一些断断续续的数据,得出以下线索:
线索一:一开始的时候,发现问题都发生在windows机器上,并且都用的chrome, 这让我很疑惑,chrome的解释差异不应该分mac和windows呀?查了下release note,发现的确在这个时间点chrome在windows上发布了新版本!
线索二:infinite loop之前在测试的时候发生过,是由于load balancer上没有设置stickiness cookie,导致多服务器的情况下,session不能准确的传递到使用中的服务器。所以,我的关注点又放在的load balancer上,也许在一定的环境情况下,load balancer的stickiness cookie会失效?
线索三:我们的解决方案是使用apache上的mod_auth_openidc这个组件,我找到了其他同样用这个架构的团队,然而他们近期上线的功能并没有遇到和类似的问题,整个公司中只有我和另一个团队遇到了相似的问题,于是我们在想是否是因为配置差异导致了问题。
找到了遇到相同问题的团队,稍微放宽了点心,至少这个问题存在普遍性,但又不是完全普遍,这也让我们十分困惑。由于是偶发事件,我们2个团队再次确认了没有一个工程人员可以重现问题。我们从周三一直到排查到了第二个周一,从chrome开始排查,再到AWS的ELB,最后只剩下了apache配置问题。
一直不能重现问题,让我们十分迷茫,因为即使找到了正确的配置,也不能确认是否解决了问题,总不能一直往生产环境去推版本,再让用户当小老鼠吧?
既然已经排除掉了其他原因,我们全力的focus在的apache的配置上,奈何我们没有一个是这方面的专家,只好重新拿出了mod_auth_openidc的文档一条条阅读,一点点去理解他的工作原理。功夫不负有心人,在理解原理后,不断的尝试破坏,终于重现了问题,问题一重现我们就找到了解决方案,因为这个问题的确挺蠢的。
问题所在
解释问题之前,我们先了解下mod_auth_openidc的工作机制:
- 客户端发送请求给服务器,服务器接收到请求之后给客户端一个临时的session cookie,我们叫他为state cookie
- 客户端拿着这个临时的state cookie去验证中心进行验证,验证结束,拿着这个state cookie到服务器进行比对验证
- 如果验证通过,服务器会删除这个state cookie然后给一个永久的session cookie
- 客户端使用这个永久的session cookie进行通讯验证
问题就出在了这个永久的session cookie上,经过反复验证我们可以顺利的从服务器拿到这个session cookie,但是拥有这个session cookie我们依旧会被服务器分配state cookie并且无限的跳转到验证中心页面,从而重复的在:生成state cookie -> 转换到 session cookie -> 生成新的state cookie -> 再覆盖之前的session cookie上。
既然问题找到了,那么是什么原因呢?
我们发现,这是因为,服务器根本没有拿刚生成的session cookie来验证!而是使用了其他的session cookie导致了验证一直无法通过。如下方式可以重现问题:
- 在A产品(a.xxx.com)上进行了OpenID的验证,得到了session cookie,但是A产品将session cookie的域名设置成了顶级域名:xxx.com而不是a.xxx.com
- 在到B产品(b.xxx.com)上进行验证,出现了无限循环,原因是B产品的session cookie使用的域名是子域名b.xxx.com
问题简单的来说,就是当有2个OpenID的session cookie时,服务器优先使用顶级域名的进行验证,而正好产品A错误的使用的顶级域名,导致我们使用准确域名的产品没办法得到验证。
也就是说,之所以我们不能重现问题,是因为我们没有使用“有问题”的产品A,得不到问题的cookie!
解决方案也应运而生:单独给我们的session cookie取个名字即可,域名再变都没事。
真相大白,真的吐血。
后续的思考
从技术角度来讲,这是一个非常低级的错误,但是也是在我们控制范围之外的,我们使用了严格的子域名来限制cookie,但一旦有团队使用了更广的设置,就会造成问题。这个问题不仅仅是影响我们团队,其实会影响到公司所有使用OpenID的团队,之所以我们碰到了,是恰巧我们的用户同时在使用“问题”产品。
修复问题之后,我也像mod_auth_openidc官方发了封信,希望这个问题能够从源头得以解决,虽然主域名优先级高没什么问题,但是应该优先验证request进来的域名,最坏也是验证所有子域名和主域名的cookie。最差的情况,也应该在域名设置的时候,给一个强烈提醒和best practices。
从技术角度来说,前端真的好复杂好麻烦,各种细节上的问题处理不当,都会埋下危险的伏笔。而这次正好是前端+DevOps服务器配置,让一个不是那么专业的人研究真的挺为难的。现在的大趋势就是Dev和Ops大融合,但这样真的好么?如果没有特别专业的认识把控,这种配置的问题又如何避免呢?
从团队角度来说,我现在慢慢理解了很多工程师一个级别一呆就是一辈子,总有一个级别薪资和责任是相对来说平衡的,再往上走要担待更多的责任,薪水并不一定成正比例的增长。越往上走,遇到问题也会越无力,本来可以依靠着更高级别的人帮你解决,但是随着你的级别提升,能帮助你的人越来越少,需要依赖你的人却越来越多。
但是不往上走,怎么通过决策来实现自我呢?
佩服你们的团队,没有直接说让客户换个浏览器
并不是没有这个声音,但没解决根本问题心理恨不踏实