24

我是如何通过Nginx日志实时封禁风险IP的

 4 years ago
source link: https://blog.piaoruiqing.com/2019/11/17/block-ip-by-analyzing-nginx-logs/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

YN3Ufm2.jpg!web

十一月 17, 2019 朴瑞卿 方案 , 运维

我是如何通过Nginx日志实时封禁风险IP的

目录

  • 我是如何通过Nginx日志实时封禁风险IP的

实时采集并分析Nginx日志, 自动化封禁风险IP方案

文章地址: https://blog.piaoruiqing.com/2019/11/17/block-ip-by-analyzing-nginx-logs/

前言

本文分享了自动化采集、分析Nginx日志并实时封禁风险IP的方案及实践.

阅读这篇文章你能收获到:

  • 日志采集方案.
  • 风险IP评估的简单方案.
  • IP封禁策略及方案.

阅读本文你需要:

  • 熟悉编程.
  • 熟悉常用Linux命令.
  • 了解Docker.

背景

分析nginx访问日志时, 看到大量404的无效请求, URL都是随机的一些敏感词. 而且近期这些请求越来越频繁, 手动批量封禁了一些IP后, 很快就有新的IP进来.

因此萌生了通过自动化分析Nginx日志实时封禁IP的想法.

jueqy2V.jpg!web

需求

序号 需求 备注 1 Nginx日志收集 方案有很多, 笔者选择了最适合个人服务器的方案: filebeat + redis 2 日志实时分析 实时消费 redis 的日志, 解析出需要的数据进行分析 3 IP风险评估 对IP进行风险评估, 多个维度: 访问次数、IP归属、用途等 4 实时封禁 针对风险IP进行不同时长的封禁

分析

从日志中简单总结几个特征:

序号 特征 描述 备注 1 访问频繁 每秒数次甚至数十次 正常的流量行为也存在突发流量, 但不会持续很久 2 持续请求 持续时间久 同上 3 多数404 请求的URL可能大多数都不存在, 且存在敏感词汇如admin、login、phpMyAdmin、backup等 正常流量行为很少存在这种情况 4 IP不正常 通过ASN能看出一些端倪, 一般这类请求的IP都不是普通的个人用户. 查询其用途一般是COM(商业)、DCH(数据中心/网络托管/传输)、SES(搜索引擎蜘蛛)等

备注: 这里分析IP是通过 ip2location 的免费版数据库, 后面会有详细的描述.

方案

sequenceDiagram
    participant Nginx
    participant Filebeat
    participant Redis
    participant Monitor
    participant Actuator
    Nginx ->> Filebeat: accessLog(File)
    Filebeat ->> Redis: accessLog(JSON)
    loop Always
    Redis ->> Monitor: accessLog(JSON)
    activate Monitor
    Monitor ->> Monitor: Analysis
    Monitor ->> Monitor: Risk assessment
    Monitor ->> Actuator: Banned
    Monitor ->> Monitor: Storing
    end
    deactivate Monitor

日志采集

来源: 笔者的网站通过docker部署, Nginx作为唯一入口, 记录了全部访问日志.

采集: 由于资源有限, 笔者选择了一款轻量的日志采集工具 Filebeat , 收集Nginx日志并写入Redis.

风险评估

Monitor服务根据URL、IP、历史评分等进行风险评估, 计算出最终的危险系数.

IP封禁

Monitor发现危险IP后(危险系数超过阈值), 调用 Actuator 进行IP封禁, 封禁时长根据危险系数计算得出.

实施

日志采集

Filebeat的用法很简单, 笔者通过swarm进行部署, 其部署文件如下(为防止代码过长, 此处略去了其他服务):

version: '3.5'
services:
  filebeat:
    image: docker.elastic.co/beats/filebeat:7.4.2
    deploy:
      resources:
        limits:
          memory: 64M
      restart_policy:
        condition: on-failure
    volumes:
    - $PWD/filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
    - $PWD/filebeat/data:/usr/share/filebeat/data:rw
    - $PWD/nginx/logs:/logs/nginx:ro
    environment:
      TZ: Asia/Shanghai
    depends_on:
    - nginx
  • image : 指定镜像和版本.
  • deploy.resources.limits.memory : 限制内存.
  • $PWD/filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro : filebeat.yml是配置文件, 其中描述了日志来源和去处. $PWD 为当前目录, 即执行 docker stack deploy 的目录. ro 为只读权限.
  • $PWD/filebeat/data:/usr/share/filebeat/data:rw : 需要持久化data目录, 这样删除docker重新部署也会记录上一次读取日志的位置. rw 为读写权限.
  • $PWD/nginx/logs:/logs/nginx:ro : 将Nginx日志目录映射到Filebeat.
  • environment.TZ : 时区

filebeat.yml 文件内容如下:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /logs/nginx/access.log
  json.keys_under_root: true
  json.overwrite_keys: true

output.redis:
  hosts: ["redis-server"]
  password: "{your redis password}"
  key: "filebeat:nginx:accesslog"
  db: 0
  timeout: 5
  • filebeat.inputs: 定义输入

  • paths: 日志路径

  • json.keys_under_root: 将日志内容放到json的根节点(如果没有设置, 整条数据会被放到一个二级节点下). 注: 笔者将nginx日志配置为json格式. 参考配置如下:

    log_format  main_json  escape=json
                         '{'
                              '"@timestamp":"$time_iso8601",'
                              '"http_host":"$http_host",'
                              '"remote_addr":"$remote_addr",'
                              '"request_uri":"$request_uri",'
                              '"request_method":"$request_method",'
                              '"server_protocol":"$server_protocol",'
                              '"status":$status,'
                              '"request_time":"$request_time",'
                              '"body_bytes_sent":$body_bytes_sent,'
                              '"http_referer":"$http_referer",'
                              '"http_user_agent":"$http_user_agent",'
                              '"http_x_forwarded_for":"$http_x_forwarded_for"'
                         '}';
  • json.overwrite_keys: 覆盖Filebeat生成的KEY, 此处为了覆盖 @timestamp 字段.

  • output.redis: 定义输出.

部署成功后查看redis数据:

aIZbe2n.jpg!web

[版权声明]

本文发布于朴瑞卿的博客, 允许非商业用途转载, 但转载必须保留原作者朴瑞卿 及链接: https://blog.piaoruiqing.com .

如有授权方面的协商或合作, 请联系邮箱: [email protected] .

风险评估

Monitor 服务使用Java编写, 使用docker部署, 与 Actuator 服务通过http交互.

风险评估需要综合多个维度:

序号 维度 策略 1 IP归属地 中文网站的用户群体一般归属地都在中国, 若IP归属地为国外就需要警惕了. 2 用途 通过IP获取其用途, DCH(数据中心/网络托管/传输)、SES(搜索引擎蜘蛛)等提高危险评分. 3 访问资源 访问资源不存在且路径中含有敏感词, 如admin、login、phpMyAdmin、backup等, 提高危险评分. 4 访问频率及持续时间 频繁且持久的请求, 考虑提高评分. 5 历史评分 历史评分综合到当前评分中.

获取IP归属地

IP归属地获取比较容易, 有不少数据服务网站提供了免费套餐, 如 IpInfo 等. 也有免费版IP数据库可以下载如 ip2location 等.

笔者使用了 ip2location 的免费版数据库:

NNvAB3y.jpg!web

ip_from和 ip_to 是IP段起止, 存储的格式是十进制, MySQL中可通过 inet_aton('your ip') 函数将IP转为十进制. 如:

set @a:= inet_aton('172.217.6.78');
SELECT * FROM ip2location_db11 WHERE ip_from <= @a AND ip_to >= @a LIMIT 1;
ip_from ip_to country_code country_name region_name city_name latitude longitude zip_code time_zone 2899902464 2899910655 US United States California Mountain View 37.405992 -122.07852 94043 -07:00
  • 数据量很大, 建议带上 LIMIT 1 .

获取AS、ASN及用途

多数网站提供的免费服务中都无法查询ASN或没有其用途. ASN数据也有免费的数据库, 但是依旧没有其用途及类型等. 此时笔者通过其它的方法曲线救国.

ip2location 提供了免费版本的 IP2Location™LITE IP-ASNIP2Proxy™LITE 数据库.

IP2Location™LITE IP-ASN: 数据库提供了确定自治系统和编号(ASN)的参考.

IP2Proxy™LITE: 数据库包含被用作开放代理的IP地址. 该数据库包括所有公共IPv4和IPv6地址的代理类型、国家、地区、城市、ISP、域、使用类型、ASN和最新记录.

IP2Location™LITE IP-ASN中无法查询到IP的使用类型, IP2Proxy™LITE 数据较少中不一定会包含指定的IP. 但可以结合这两个库, 大致猜测IP的用途:

  1. 首先, 在 IP2Proxy™LITE 中查询出IP的ASN.

    set @a:= inet_aton('172.217.6.78');
    SELECT * FROM ip2location_asn WHERE ip_from <= @a AND ip_to >= @a LIMIT 1;
    ip_from ip_to cidr asn as 2899904000 2899904255 172.217.6.0/24 15169 Google LLC
  2. 结合ASN和IP, 查询相同ASN 最接近 指定IP的前后两条记录:

    set @a:= inet_aton('172.217.6.78');
    SELECT * FROM ip2proxy_px8 WHERE ip_from >= @a AND asn = 15169 ORDER BY ip_from ASC LIMIT 1;
    SELECT * FROM ip2proxy_px8 WHERE ip_from <= @a AND asn = 15169 ORDER BY ip_from DESC LIMIT 1;
    ip_from ip_to proxy_type country_code country_name region_name city_name isp domain usage_type asn as last_seen 2899904131 2899904131 PUB US United States California Mountain View Google LLC google.com DCH 15169 Google LLC 30 ip_from ip_to proxy_type country_code country_name region_name city_name isp domain usage_type asn as last_seen 2899904015 2899904015 PUB US United States California Mountain View Google LLC google.com DCH 15169 Google LLC 30
  3. 计算查询到的proxy记录中IP与当前IP的差值的绝对值.

    IP proxy IP abs(IP – proxy IP) 2899904078 2899904131 53 2899904078 2899904015 63

    如果绝对值很接近, 那么就认为此IP的用途和proxy IP相同. 很接近的定义可以根据情况调整, 如绝对值在65535范围内.

综合评分

综合评分的规则可根据实际场景进行调整

序号 评分项 评分规则(1-10分) 1 IP归属地 如: 国内5分, 国外10分, 可根据地区再进行细分 2 用途 如: ISP/MOB计2分, COM计5分, DCH计10分 3 访问资源 如: 404计5分, 存在敏感词一律10分 4 访问频率及持续时间 根据一段时间内平均访问次数计算分数 5 历史评分

综合上述1-5项, 进行计算, 可以简单的相加, 也可加权计算.

IP封禁

笔者采用 iptables + ipset 的方式进行IP封禁. Actuator 服务使用node编写, 运行在主机上, docker中的 Monitor 通过http与其交互. 封禁IP部分代码如下:

'use strict';

const express = require('express');
const shell = require('shelljs');
const router = express.Router();

router.post('/blacklist/:name/:ip', function (req, res, next) {
    let name = req.params.name;
    let ip = req.params.ip;
    let timeout = req.query.timeout;
    let cmd = <code>ipset -exist add ${name} ${ip} timeout ${timeout}</code>;
    console.log(cmd);
    shell.exec(cmd);
    res.send('ok\n');
});

module.exports = router;
  • name : 黑名单名称.
  • timeout : 超时时间, 单位: 秒.

目前, 还是有不少"头铁"的IP频繁扫描笔者的网站, 在发现后几秒内自动屏蔽掉, 目前效果比较理想.

iIBZzqi.jpg!web

结语

  • 爬虫、机器人、漏洞扫描等给网站造成了不必要的开销甚至带来风险, 不可忽视. 绝对安全很难做到, 但至少可以做到比别人安全.
  • 封禁是相对暴力的手段, 一定要把握好尺度, 出现误杀会导致网站流失用户.
  • 除了封禁外, 也可考虑"祸水东引", 将风险IP 302重定向到gitpage(备份站点), 这样就算误杀, 也不会造成用户无法访问的情况.
  • 道路千万条, 安全第一条

欢迎关注公众号(代码如诗)

z6zmm2N.jpg!web

推荐阅读

© 2019,朴瑞卿.

[版权声明]

本文发布于朴瑞卿的博客, 允许非商业用途转载, 但转载必须保留原作者朴瑞卿 及链接: https://blog.piaoruiqing.com . 如有授权方面的协商或合作, 请联系邮箱: [email protected] .

欢迎关注公众号: (代码如诗) z6zmm2N.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK