實時採集並分析Nginx日誌, 自動化封禁風險IP方案php
文章地址: blog.piaoruiqing.com/2019/11/17/…html
本文分享了自動化採集、分析Nginx日誌並實時封禁風險IP的方案及實踐.node
閱讀這篇文章你能收穫到:nginx
閱讀本文你須要:git
分析nginx訪問日誌時, 看到大量404的無效請求, URL都是隨機的一些敏感詞. 並且近期這些請求愈來愈頻繁, 手動批量封禁了一些IP後, 很快就有新的IP進來.redis
所以萌生了經過自動化分析Nginx日誌實時封禁IP的想法.sql
序號 | 需求 | 備註 |
---|---|---|
1 | Nginx日誌收集 | 方案有不少, 筆者選擇了最適合我的服務器的方案: filebeat +redis |
2 | 日誌實時分析 | 實時消費redis 的日誌, 解析出須要的數據進行分析 |
3 | IP風險評估 | 對IP進行風險評估, 多個維度: 訪問次數、IP歸屬、用途等 |
4 | 實時封禁 | 針對風險IP進行不一樣時長的封禁 |
從日誌中簡單總結幾個特徵:docker
序號 | 特徵 | 描述 | 備註 |
---|---|---|---|
1 | 訪問頻繁 | 每秒數次甚至數十次 | 正常的流量行爲也存在突發流量, 但不會持續好久 |
2 | 持續請求 | 持續時間久 | 同上 |
3 | 多數404 | 請求的URL可能大多數都不存在, 且存在敏感詞彙如admin、login、phpMyAdmin、backup等 | 正常流量行爲不多存在這種狀況 |
4 | IP不正常 | 經過ASN能看出一些端倪, 通常這類請求的IP都不是普通的我的用戶. | 查詢其用途通常是COM(商業)、DCH(數據中心/網絡託管/傳輸)、SES(搜索引擎蜘蛛)等 |
備註: 這裏分析IP是經過ip2location的免費版數據庫, 後面會有詳細的描述.shell
來源: 筆者的網站經過docker部署, Nginx做爲惟一入口, 記錄了所有訪問日誌.數據庫
採集: 因爲資源有限, 筆者選擇了一款輕量的日誌採集工具Filebeat, 收集Nginx日誌並寫入Redis.
Monitor服務根據URL、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 複製代碼
$PWD
爲當前目錄, 即執行docker stack deploy
的目錄. ro
爲只讀權限.rw
爲讀寫權限.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數據:
Monitor
服務使用Java編寫, 使用docker部署, 與Actuator
服務經過http交互.
風險評估須要綜合多個維度:
序號 | 維度 | 策略 |
---|---|---|
1 | IP歸屬地 | 中文網站的用戶羣體通常歸屬地都在中國, 若IP歸屬地爲國外就須要警戒了. |
2 | 用途 | 經過IP獲取其用途, DCH(數據中心/網絡託管/傳輸)、SES(搜索引擎蜘蛛)等提升危險評分. |
3 | 訪問資源 | 訪問資源不存在且路徑中含有敏感詞, 如admin、login、phpMyAdmin、backup等, 提升危險評分. |
4 | 訪問頻率及持續時間 | 頻繁且持久的請求, 考慮提升評分. |
5 | 歷史評分 | 歷史評分綜合到當前評分中. |
IP歸屬地獲取比較容易, 有很多數據服務網站提供了免費套餐, 如IpInfo等. 也有免費版IP數據庫能夠下載如ip2location等.
筆者使用了ip2location的免費版數據庫:
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
.多數網站提供的免費服務中都沒法查詢ASN或沒有其用途. ASN數據也有免費的數據庫, 可是依舊沒有其用途及類型等. 此時筆者經過其它的方法曲線救國.
ip2location提供了免費版本的IP2Location™LITE IP-ASN
和IP2Proxy™LITE
數據庫.
IP2Location™LITE IP-ASN: 數據庫提供了肯定自治系統和編號(ASN)的參考.
IP2Proxy™LITE: 數據庫包含被用做開放代理的IP地址. 該數據庫包括全部公共IPv4和IPv6地址的代理類型、國家、地區、城市、ISP、域、使用類型、ASN和最新記錄.
IP2Location™LITE IP-ASN中沒法查詢到IP的使用類型, IP2Proxy™LITE數據較少中不必定會包含指定的IP. 但能夠結合這兩個庫, 大體猜想IP的用途:
首先, 在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 |
結合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 |
計算查詢到的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項, 進行計算, 能夠簡單的相加, 也可加權計算.
筆者採用**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 = `ipset -exist add ${name} ${ip} timeout ${timeout}`; console.log(cmd); shell.exec(cmd); res.send('ok\n'); }); module.exports = router; 複製代碼
目前, 仍是有很多"頭鐵"的IP頻繁掃描筆者的網站, 在發現後幾秒內自動屏蔽掉, 目前效果比較理想.
若是這篇文章對您有幫助,請點個贊吧 ( ̄▽ ̄)"