背景
这是在上家公司跑路前做的最后一个需求,主要应对的是外部对我们服务恶意扫描的。
事情的起源来自某次凌晨四点,我正睡得香甜。突然被 NOC 一个电话打了过来,那头说终于打通了一个电话,给你们组其他人打电话都接不通,你们服务的请求量暴涨,麻烦帮看一下是什么问题。
我内心:垃圾小米,说好的免打扰呢?
还是强忍着不得不说了句,好。
打开 Grafana 一看,凌晨三点多确实流量暴涨了好几倍。于是进一步打开 Kibana 去查看日志,发现那个时间点有很多请求,但路径都很奇怪,没什么规律,明显也不是我们的接口。
想了一下,我也不知道为什么。但大领导在群里盯着,要给个答复,大半夜的我能怎么办?我也很绝望啊。
刚好 ss 那个时间点失眠了,我就去问他这些请求是什么造成的?他告诉我可能是扫描。我就给领导说了应该是扫描造成的(虽然我也不知道什么是扫描),明天去了再进一步看看。
恶意扫描
这个具体的概念可以看下别人的描述:
黑客开启入侵的第一步即是“恶意探测”,通常也可理解为踩点扫描。黑客为了对攻击目标进行多方了解,最常用的途径就是利用扫描工具对目标用户网络进行端口及漏洞扫描,查看服务器的运行状态等基本信息,一旦发现安全漏洞就会利用其实施攻击,最终达到非法入侵的目的。因此,要想降低安全事件发生的概率,我们必须从源头阻止黑客的攻击。通过防扫描的方式阻止黑客“恶意探测”,让用户在第一时间发现安全威胁并阻止黑客扫描行为,从而提升黑客攻击成本,为自身赢得宝贵的应对时间,大幅度降低黑客侵入企业内网的风险。
简单来说,就是黑客通过发送一些随机的请求,来获取我们服务器的一些信息。
给我们带来了很大的困扰,主要是这三种:
- 短时间内流量暴涨,可能造成服务瘫痪。
- 大量错误日志,很容易埋没真正有问题的日志。
- 攻击者可能利用扫描发现一些安全漏洞,进而攻击服务器。
WAF
引用百度百科的描述来看:
Web应用防护系统(也称为:网站应用级入侵防御系统。英文:Web Application Firewall,简称: WAF)。利用国际上公认的一种说法:Web应用防火墙是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品。
WAF 就是一个防护系统,它可以对异常的 HTTP 请求进行检测,拦截不符合我们规范的请求。这里的拦截规则我们可以在 Nginx 里面进行配置。
配置规则来源于百度百科:
RulePath=”/www/server/panel/vhost/wafconf/”–waf 详细规则存放目录(一般无需修改) attacklog =“on”–是否开启攻击日志记录(on 代表开启,off 代表关闭。下同) logdir =”/www/wwwlogs/waf/”–攻击日志文件存放目录(一般无需修改) UrlDeny=“on”–是否开启恶意 url 拦截 Redirect=“on”–拦截后是否重定向 CookieMatch=“off”–是否开启恶意Cookie拦截 postMatch=“off”–是否开启 POST 攻击拦截 whiteModule=“on”–是否开启 url 白名单 black_fileExt={“php”,“jsp”}–文件后缀名上传黑名单,如有多个则用英文逗号分隔。如:{“后缀名1”,“后缀名2”,“后缀名3”……} ipWhitelist={“127.0.0.1”}–白名单 IP,如有多个则用英文逗号分隔。如: {“127.0.0.1”,“127.0.0.2”,“127.0.0.3”……}下同 ipBlocklist={“1.0.0.1”}–黑名单 IP CCDeny=“off”–是否开启 CC 攻击拦截 CCrate=“300/60”–CC 攻击拦截阈值,单位为秒。”300/60″代表60秒内如果同一个 IP 访问了300次则拉黑 配置文件中,RulePath 项对应的文件夹里存放的是具体的拦截规则。文件夹下有着相关的规则文件。作用解析如下: args –GET 参数拦截规则 blockip –无作用 cookie –Cookie拦截规则 denycc –无作用 post –POST 参数拦截规则 returnhtml –被拦截后的提示页面(HTML) url –url 拦截规则 user-agent –UA 拦截规则 whiteip –无作用 whiteurl –白名单网址
在我们那边,整个服务的请求-响应模式是这样的。
- Nginx 将请求转发到对应的 Ingress
- Ingress 路由匹配到 Service
- Service 通过 kube-proxy 转发到 Pod
- 请求进入 Node Agent(启动 Node 服务的进程管理,拦截了请求)
- 请求进入业务方服务
- 业务方响应
配置合法路径
在第一期需求里面,我们前端这边都是在项目里面去配置 YAML 文件,这个 YAML 文件里面规定了合法的请求路径和请求方式。
这样我们就可以在 Node Agent 里面去根据业务方配置的 YAML 文件来查询当前请求是否合法。如果合法,就允许它通过,如果不合法,就返回 403。
但这样并没有解决根本问题,异常的请求依然可以打到服务里面来,大量的请求还是会占用我们过多的服务器资源。
拦截 IP
后来,运维那边提供了完整的拦截能力。运维提供 IP 黑名单给业务方,业务方上报被打击的请求日志,这些统一都在业务方的网关里面进行。对前端来说,这层网关就是我们的 Node Agent。
这里依然结合了一期的功能,具体步骤如下:
- 业务方在项目里配置 YAML 文件,里面标明合法的请求路径。后期将配置从 YAML 文件移到配置中心里面。
- Node Agent 里面轮询查询运维提供的黑名单 IP 接口,将其缓存在本地。
- 请求进来的时候,去查询它是否在黑名单 IP 里面。
- 如果不在黑名单里面,再去看它是否在业务配置的合法请求路径里面。
- 如果在合法请求路径里面,那就上报一条正常的日志。
- 如果不在合法的请求路径里面或者在 IP 黑名单里面,那就上报一条有打击标志的日志,并且返回 403。
因为我们的业务大都是偏内部的,所以访问量都不会很大,对访问速度也没有很极致的追求。
测试在压测中,发现轮询黑名单 IP 接口也没有让请求耗时明显增加。
而运维又会定时从 Kibana 日志里面去捞带有异常关键字的日志,来判断当前请求方的 IP 是否我们内部的 IP,以及它是否被打击了。
如果不是内部的,请求的也不是合法路径,并且在短时间内请求到一定的阈值,那就加入到 IP 黑名单里面。
在之后,运维则会在 Nginx 层对这些打击到的黑名单 IP 进行拦截,防止请求进入服务里面。