一、由《你的灯亮着吗》说起
前段时间在李智慧老师专栏及网课中听他提到《你的灯亮着吗》这本书,书中讲了这样一个故事:
北欧有一个度假胜地,是欧洲人民夏天避暑度假的好去处,去度假胜地需要经过一个长长的隧道,隧道的工程师为了保证隧道的安全使用,在隧道入口处立了一块牌子,写着:请打开车灯。
游客们开着汽车,打开车灯,穿过隧道,到达度假胜地,愉快地去玩耍了。而等他们要回去的时候,有些人却发现车子无法启动——他们忘记关闭车灯,汽车电池耗尽了。小镇的警察们只好开着自己的警车四处为游客们充电,疲惫不堪。而沮丧的游客们则在回去以后四处抱怨,分享他们糟糕的旅游经验,导致小镇旅游业大受影响,镇长压力山大。
于是人们找到隧道的工程师,要求他在隧道的尽头再立一块牌子,写上:请关闭车灯。工程师照做了以后,却发现麻烦来了:夜晚穿过隧道的游客看到牌子,虽然非常疑惑,但还是按照指示关闭了车灯,结果却发生了车祸,麻烦更大了。于是工程师不得不写上:如果是白天,请关闭车灯。结果有的游客没看到隧道入口的牌子,却看到了隧道出口的牌子,同样疑惑。为了解决新问题,工程师不得不在牌子上继续写下去⋯⋯
这个案例和我们作为开发工程师所面临的境遇非常相似,我们平时的工作主要解决来自运营、老板、客户的各种具体问题,而我们往往陷入具体的问题细节,缺乏对问题本身的思考。至少我们需要关注以下几个具体问题自身的问题
- 问题的实质是什么?
- 这是谁的问题?
- 谁能够解决这个问题
二、工作案例:请关闭公网8888端口
我所在的部门开发维护一个基于Springcloud构建的微服务产品,我的小组主要维护几个基础微服务,这次出现问题的就是注册/配置中心微服务,它的启动端口是 8888;问题出现及解决经过如下:
现场技术支持同学报告用户扫描漏洞及上网查询资料结果
http://223.111.**.**:8888
慢请求漏洞。
网上查阅资料后了解到以下关键信息:
这个地址是注册中心服务eureka 的服务列表
慢请求攻击是什么?
缓慢的http拒绝服务攻击是以极低的速度往服务器发送HTTP请求。由于Web Server对于并发的连接数都有一定的上限,因此若是恶意地占用住这些连接不释放,那么Web Server的所有连接都将被恶意连接占用,从而无法接受新的请求,导致拒绝服务。
如何测试/发现慢请求漏洞?
使用 slowhttptest 工具可以检测目标地址是否存在慢请求漏洞。
查阅资料如何解决慢请求攻击?
一些主流的web服务器如Apache、 Nginx 都集成了慢请求防御模块/插件,而tomcat不具备此功能模块。
实验、沟通
- 在内网环境使用slowhttptest测试发现经过nginx代理的端口具备防御慢请求攻击功能,而直接暴露的tomcat的端口不具备此功能。
- 怀疑
http://223.111.**.**:8888
这个地址是后端微服务tomcat地址,未经nginx代理,和部署同学沟通后确认该端口确认该端口是直接暴露在公网上的tomcat端口!!!!
准备提供给现场同学的解决方案
- 修改注册中心(eureka)tomcat启动端口为18888
- 配置nginx监听8888端口,将该端口的请求转发到18888端口
- 修改其他服务,注册中心端口为18888
http://223.111.**.**:8888/swagger-ui.html
Swagger接口API泄露
经查阅代码及网上资料,发现这是一个小问题,由于代码中swagger的配置类指定了 profile 参数,修改配置文件中Spring激活的profile即可解决。
反转,请关闭公网8888端口
上述解决方案,看似已经将问题解决了,但是成本太高了,需要修改数个微服务配置文件并重启,成本太高了。疑问——为什么要在公网*问8888端口呢?部署访问明明都在同一个内网环境,于是我去查看了注册中心eureka页面注册访问的IP发现都是10...**,这不就是内网地址吗?!!!于是我再一次找现场同学沟通,为什么需要在公网*问8888端口?为什么需要在公网上查看eureka服务注册列表?回复,这些都是在内网访问的,不知道有什么需求要暴露在公网。明白了,有人错误的暴露了公网8888端口,导致了这一系列问题。于是建议他关闭了公网8888端口。解决了这个问题,后来了解到是当时的部署人员为了省去连接客户vpn直接查看服务注册列表页面的需要,放开了公网8888端口,事后忘记关闭。
三、审视
上述第二部分解决这个问题的过程中,首先我们就问题解决问题,根据客户侧提供的检测报告寻找具体问题对应的解决方案,这个视角下:
问题的实质是?:http://223.111.**.**:8888
缺乏前置的慢请求防御功能,落实解决方案是不直接暴露注册中心端口而采用nginx代理的方式使用nginx功能来解决这个问题。
这是谁的问题?:改变没有思考这是谁的问题。完全是陷在问题细节里面,问题细节 --> 搜索引擎 --> 代码/配置查看 -- >改代码/配置 --> 重新部署,下意识的无脑的使用这样的过程来解决问题(这种就谈不上解决思路,因为只是按既有的习惯,被问题和提出问题的牵着走,根本没有思考)。
谁来解决这个问题:现场技术支持和总部开发一起上,改代码、部署。
然而解决方案提出后,疑问牵着我重新审视这个问题:
问题的实质是?:客户的扫描规则针对的是公网环境,不该在公网直接暴露tomcat监听端口。
这是谁的问题?:运维部署人员的问题
谁来解决这个问题:开发及现场人员,梳理逻辑确认可关闭公网8888端口后,由现场运维同学操作
旧的疑问通过沟通找到答案了,但新的疑问产生了:
公网上暴露tomcat端口,并且暴露的是注册中心这种可用性要求特别高的端口,为什么运维同学会有这样的操作?为什么涉及这个案例问题的开发、运维同学对这类问题反应会如此淡定。
问题的实质是?:缺乏技术培训和相关规范,大家意识中不会重视甚至认识这样的问题
这是谁的问题?:技术管理者的问题
谁来解决这个问题:技术管理者自己需要制定规范并提供培训解决这个问题
四、总结:知识&行为&经验
提到《你的灯亮着吗》的专栏文章已经看过许久,但在前面部分提到的案例问题中一开始,仍然顺着老路走,这说明过去熟悉的知识并没有造成行为上的改变,然而在这个问题之后所产生的疑问又指引我回顾起之前熟悉的知识,顺着这个知识解决之后疑问的过程,我的行为有所改变,假以时日当这种新的行为愈加熟练,经验开始积累反应将更加准确迅速。而这让我联想到一些虚拟机有关的知识。
当我们启动一个Java工程,其类加载路径下有许多class文件,它们包含着无数有用的程序执行逻辑,但是启动时jvm并不会立即加载这些死的文件,非得到一定的时机如new 指令创建对应类对象、静态方法调用、反射调用等时机才会触发该类的加载,而只有将类的字节码加载到jvm的运行时结构中,它才能为jvm所用。同时jvm具有代码执行统计功能,达到执行热度阈值的代码会触发JIT将其编译为机器码,以提高其执行效率。这种机制联系前文所述,class字节码角色恰似那些了解过的知识,它们在一些使用中被加载进入jvm从而改变了程序执行行为(运行时,好像知识被激活了),而使用次数够多,就会形成虚拟机的经验(机器码),提高了执行效率。